Documentation
¶
Overview ¶
Grohl is an opinionated library for gathering metrics and data about how your applications are running in production. It does this through writing logs in a key=value structure. It also provides interfaces for sending stacktraces or metrics to external services.
This is a Go version of https://github.com/asenchi/scrolls. The name for this library came from mashing the words "go" and "scrolls" together. Also, Dave Grohl (lead singer of Foo Fighters) is passionate about event driven metrics.
Grohl treats logs as the central authority for how an application is behaving. Logs are written in a key=value structure so that they are easily parsed. If you use a set of common log keys, you can relate events from various services together.
Here's an example log that you might write:
grohl.Log(grohl.Data{"fn": "trap", "signal": "TERM", "at": "exit", "status": 0})
The output would look something like:
now=2013-10-14T15:04:05-0700 fn=trap signal=TERM at=exit status=0
Note: Other examples leave out the "now" keyword for clarity.
A *grohl.Context stores a map of keys and values that are written with every log message. You can set common keys for every request, or create a new context per new request or connection.
You can add more context to the example above by setting up the app name and deployed environment.
grohl.AddContext("app", "myapp") grohl.AddContext("deploy", os.Getenv("DEPLOY"))
This changes the output from above to:
app=myapp deploy=production fn=trap signal=TERM at=exit status=0
You can also create scoped Context objects. For instance, a network server may want a scoped Context for each request or connection.
context := grohl.NewContext(grohl.Data{"ns": "server"}) context.Log(grohl.Data{"fn": "trap", "signal": "TERM", "at": "exit", "status": 0})
This is the output (taking the global context above into consideration):
app=myapp deploy=production ns=server fn=trap signal=TERM at=exit status=0
As you can see we have some standard nomenclature around logging. Here's a cheat sheet for some of the methods we use:
- now: The current timestamp, automatically set by an IoLogger. Can be disabled if IoLogger.AddTime is disabled.
- app: Application
- lib: Library
- ns: Namespace (Class, Module or files)
- fn: Function
- at: Execution point
- deploy: Our deployment (typically an environment variable i.e. DEPLOY=staging)
- elapsed: Measurements (Time from a Timer)
- metric: The name of a Statter measurement
- count: Measurements (Counters through a Statter)
- gauge: Measurements (Gauges through a Statter)
- timing: Measurements (Timing through a Statter)
By default, all *grohl.Context objects write to STDOUT. Grohl includes support for both io and channel loggers.
writer, _ := syslog.Dial(network, raddr, syslog.LOG_INFO, tag) grohl.SetLogger(grohl.NewIoLogger(writer))
If you are writing to *grohl.Context objects in separate go routines, a channel logger can be used for concurrency.
// you can pass in your own "chan grohl.data" too. chlogger, ch := grohl.NewChannelLogger(nil) grohl.SetLogger(chlogger) // pipe messages from the channel to a single io.writer: writer, _ := syslog.Dial(network, raddr, syslog.LOG_INFO, tag) logger := grohl.NewIoLogger(writer) // reads from the channel until the program dies go grohl.Watch(logger, ch)
Grohl provides a grohl.Statter interface based on https://github.com/peterbourgon/g2s:
// these functions are available on a *grohl.Context too grohl.Counter(1.0, "my.silly.counter", 1) grohl.Timing(1.0, "my.silly.slow-process", time.Since(somethingBegan)) grohl.Gauge(1.0, "my.silly.status", "green")
Without any setup, this outputs:
metric=my.silly.counter count=1 metric=my.silly.slow-process timing=12345 metric=my.silly.status gauge=green
If you import "github.com/peterbourgon/g2s", you can dial into a statsd server over a udp socket:
statter, err := g2s.Dial("udp", "statsd.server:1234") if err != nil { panic(err) } grohl.CurrentStatter = statter
Once being set up, the statter functions above will not output any logs.
Grohl makes it easy to measure the run time of a portion of code.
// you can also create a timer from a *grohl.Context // timer := context.Timer(grohl.Data{"fn": "test"}) timer := grohl.NewTimer(grohl.Data{"fn": "test"}) grohl.Log(grohl.Data{"status": "exec"}) timer.Finish()
This would output:
fn=test at=start status=exec fn=test at=finish elapsed=0.300
You can change the time unit that Grohl uses to "milliseconds" (the default is "seconds"):
grohl.SetTimeUnit("ms") // or with a *grohl.Context context.TimeUnit = "ms"
You can also write to a custom Statter:
timer := grohl.NewTimer(grohl.data{"fn": "test"}) // uses grohl.CurrentStatter by default timer.SetStatter(nil, 1.0, "my.silly.slow-process") timer.Finish()
You can also set all *grohl.Timer objects to use the same statter.
// You can call SetStatter() on a *grohl.Context to affect any *grohl.Timer // objects created from it. // // This affects _all_ *grohl.Timer objects. grohl.SetStatter(nil, 1.0, "my.silly") timer := grohl.NewTimer(grohl.data{"fn": "test"}) // set just the suffix of the statter bucket set above timer.StatterBucketSuffix("slow-process") // overwrite the whole bucket timer.StatterBucket = "my.silly.slow-process" // Sample only 50% of the timings. timer.StatterSampleRate = 0.5 timer.Finish()
Grohl can report Go errors:
written, err := writer.Write(someBytes) if err ! nil { // context has the following from above: // grohl.Data{"app": "myapp", "deploy": "production", "ns": "server"} context.Report(err, grohl.Data{"written": written}) }
Without any ErrorReporter set, this logs the following:
app=myapp deploy=production ns=server at=exception class=*errors.errorString message="some message" app=myapp deploy=production ns=server at=exception class=*errors.errorString message="some message" site="stack trace line 1" app=myapp deploy=production ns=server at=exception class=*errors.errorString message="some message" site="stack trace line 2" app=myapp deploy=production ns=server at=exception class=*errors.errorString message="some message" site="stack trace line 3"
You can set the default ErrorReporter too:
myReporter := myreporter.New() grohl.SetErrorReporter(myReporter)
Index ¶
- Variables
- func AddContext(key string, value interface{})
- func BuildLog(data Data, addTime bool) string
- func Counter(sampleRate float32, bucket string, n ...int)
- func DeleteContext(key string)
- func ErrorBacktrace(err error) string
- func ErrorBacktraceLines(err error) []string
- func Format(value interface{}) string
- func Gauge(sampleRate float32, bucket string, value ...string)
- func Log(data Data)
- func Report(err error, data Data)
- func SetErrorReporter(reporter ErrorReporter)
- func SetStatter(statter Statter, sampleRate float32, bucket string)
- func SetTimeUnit(unit string)
- func TimeUnit() string
- func Timing(sampleRate float32, bucket string, d ...time.Duration)
- func Watch(logger Logger, logch chan Data)
- type ChannelLogger
- type Context
- func (c *Context) Add(key string, value interface{})
- func (c *Context) Counter(sampleRate float32, bucket string, n ...int)
- func (c *Context) Delete(key string)
- func (c *Context) Gauge(sampleRate float32, bucket string, value ...string)
- func (c *Context) Log(data Data) error
- func (c *Context) Merge(data Data) Data
- func (c *Context) New(data Data) *Context
- func (c *Context) Report(err error, data Data) error
- func (s Context) SetStatter(statter Statter, sampleRate float32, bucket string)
- func (s Context) StatterBucketSuffix(suffix string)
- func (c *Context) Timer(data Data) *Timer
- func (c *Context) Timing(sampleRate float32, bucket string, d ...time.Duration)
- type Data
- type ErrorReporter
- type IoLogger
- type Logger
- type Statter
- type Timer
Constants ¶
This section is empty.
Variables ¶
var CurrentContext = newContext(make(Data), CurrentLogger, "s", nil, &_statter{})
CurrentContext is the default Context used by Log, Report, AddContext, DeleteContext, NewTimer.
Functions ¶
func AddContext ¶
func AddContext(key string, value interface{})
AddContext adds the key and value to the CurrentContext's data.
func BuildLog ¶
BuildLog assembles a log message from the key/value data. If addTime is true, the current timestamp is logged with the "now" key.
func Counter ¶
Counter writes a counter value to the CurrentStatter. By default, values are simply logged.
func DeleteContext ¶
func DeleteContext(key string)
DeleteContext removes the key from the CurrentContext's data.
func ErrorBacktrace ¶
ErrorBacktrace creates a backtrace of the call stack.
func ErrorBacktraceLines ¶
ErrorBacktraceLines creates a backtrace of the call stack, split into lines.
func Format ¶
func Format(value interface{}) string
Format converts the value into a string for the Logger output.
func Gauge ¶
Gauge writes a static value to the CurrentStatter. By default, values are simply logged.
func Report ¶
Report sends the error and key/value data to the CurrentContext's ErrorReporter. If no reporter is set, the CurrentContext simply logs the error and stacktrace.
func SetErrorReporter ¶
func SetErrorReporter(reporter ErrorReporter)
SetErrorReporter sets the ErrorReporter used by the CurrentContext. This will skip the default logging of the reported errors.
func SetStatter ¶
SetStatter sets up a basic Statter in the CurrentContext. This Statter will be used by any Timer created from this Context.
func SetTimeUnit ¶
func SetTimeUnit(unit string)
SetTimeUnit sets the default time unit for the CurrentContext. This gets passed down to Timer objects created from this Context.
func TimeUnit ¶
func TimeUnit() string
TimeUnit returns the default time unit for the CurrentContext.
Types ¶
type ChannelLogger ¶
type ChannelLogger struct {
// contains filtered or unexported fields
}
ChannelLogger sends the key/value data to a channel. This is useful when loggers are in separate goroutines.
func NewChannelLogger ¶
func NewChannelLogger(channel chan Data) (*ChannelLogger, chan Data)
func (*ChannelLogger) Log ¶
func (l *ChannelLogger) Log(data Data) error
Log writes the assembled log line.
type Context ¶
type Context struct { Logger Logger TimeUnit string ErrorReporter ErrorReporter // contains filtered or unexported fields }
A Context holds default key/value data that merges with the data every Log() call receives.
func NewContext ¶
NewContext returns a new Context object with the given key/value data.
func (*Context) Log ¶
Log merges the given data with the Context's data, and passes it to the Logger.
func (*Context) Merge ¶
Merge combines the given key/value data with the Context's data. If no data is given, a clean duplicate of the Context's data is returned.
func (*Context) New ¶
New creates a duplicate Context object, merging the given data with the Context's data.
func (Context) SetStatter ¶
SetStatter sets a Statter to be used in Timer Log() calls.
func (Context) StatterBucketSuffix ¶
func (s Context) StatterBucketSuffix(suffix string)
StatterBucketSuffix changes the suffix of the bucket. If SetStatter() is called with bucket of "foo", then StatterBucketSuffix("bar") changes it to "foo.bar".
type Data ¶
type Data map[string]interface{}
Data is the map used to specify the key/value pairs for a logged message.
type ErrorReporter ¶
type IoLogger ¶
type IoLogger struct { AddTime bool // contains filtered or unexported fields }
IoLogger assembles the key/value pairs into a line and writes it to any io.Writer. This expects the writers to be threadsafe.
func NewIoLogger ¶
type Logger ¶
The Logger interface represents the ability to log key/value data.
var CurrentLogger Logger = NewIoLogger(nil)
CurrentLogger is the default Logger used by Log, Report.
type Statter ¶
type Statter interface { Counter(sampleRate float32, bucket string, n ...int) Timing(sampleRate float32, bucket string, d ...time.Duration) Gauge(sampleRate float32, bucket string, value ...string) }
Statter describes the interface used by the g2s Statter object. http://godoc.org/github.com/peterbourgon/g2s
var CurrentStatter Statter = CurrentContext
The CurrentStatter is the default Statter used in Counter, Timing, Gauge.
type Timer ¶
A Timer tracks the duration spent since its creation.
func (*Timer) Finish ¶
func (t *Timer) Finish()
Finish writes a final log message with the elapsed time shown.
func (*Timer) Log ¶
Log writes a log message with extra data or the elapsed time shown. Pass nil or use Finish() if there is no extra data.
func (Timer) SetStatter ¶
SetStatter sets a Statter to be used in Timer Log() calls.
func (Timer) StatterBucketSuffix ¶
func (s Timer) StatterBucketSuffix(suffix string)
StatterBucketSuffix changes the suffix of the bucket. If SetStatter() is called with bucket of "foo", then StatterBucketSuffix("bar") changes it to "foo.bar".