grohl

package module
v0.0.0-...-efc2235 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2014 License: MIT Imports: 10 Imported by: 39

README

Grohl

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 exceptions or metrics to external services.

This is a Go version of 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.

See this blog post for the rationale behind this library.

Installation

$ go get github.com/technoweenie/grohl

Then import it:

import "github.com/technoweenie/grohl"

Usage

Grohl takes almost no setup. Everything writes to STDOUT by default. Here's a quick http server example:

package main

import (
  "github.com/technoweenie/grohl"
  "log"
  "net/http"
)

func main() {
  grohl.AddContext("app", "example")

  http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
    grohl.Log(grohl.Data{"path": r.URL.Path})
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  })

  log.Fatal(http.ListenAndServe(":8080", nil))
}

This writes a log on every HTTP request like:

now=2013-10-14T15:04:05-0700 app=example path=/foo

See the godocs for details on metrics, statsd integration, and custom error reporters.

Note on Patches/Pull Requests

  1. Fork the project on GitHub.
  2. Make your feature addition or bug fix.
  3. Add tests for it. This is important so I don't break it in a future version unintentionally.
  4. Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  5. Send me a pull request. Bonus points for topic branches.

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

Constants

This section is empty.

Variables

View Source
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

func BuildLog(data Data, addTime bool) string

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

func Counter(sampleRate float32, bucket string, n ...int)

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

func ErrorBacktrace(err error) string

ErrorBacktrace creates a backtrace of the call stack.

func ErrorBacktraceLines

func ErrorBacktraceLines(err error) []string

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

func Gauge(sampleRate float32, bucket string, value ...string)

Gauge writes a static value to the CurrentStatter. By default, values are simply logged.

func Log

func Log(data Data)

Log writes the key/value data to the CurrentLogger.

func Report

func Report(err error, data Data)

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

func SetStatter(statter Statter, sampleRate float32, bucket string)

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.

func Timing

func Timing(sampleRate float32, bucket string, d ...time.Duration)

Timing writes a timer value to the CurrentStatter. By default, values are simply logged.

func Watch

func Watch(logger Logger, logch chan Data)

Watch starts a for loop that sends any output from logch to logger.Log(). This is intended to be used in a goroutine.

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

func NewContext(data Data) *Context

NewContext returns a new Context object with the given key/value data.

func (*Context) Add

func (c *Context) Add(key string, value interface{})

Add adds the key and value to the Context's data.

func (*Context) Counter

func (c *Context) Counter(sampleRate float32, bucket string, n ...int)

Counter writes a counter value to the Context.

func (*Context) Delete

func (c *Context) Delete(key string)

Delete removes the key from the Context's data.

func (*Context) Gauge

func (c *Context) Gauge(sampleRate float32, bucket string, value ...string)

Gauge writes a static value to the Context.

func (*Context) Log

func (c *Context) Log(data Data) error

Log merges the given data with the Context's data, and passes it to the Logger.

func (*Context) Merge

func (c *Context) Merge(data Data) Data

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

func (c *Context) New(data Data) *Context

New creates a duplicate Context object, merging the given data with the Context's data.

func (*Context) Report

func (c *Context) Report(err error, data Data) error

Report writes the error to the ErrorReporter, or logs it if there is none.

func (Context) SetStatter

func (s Context) SetStatter(statter Statter, sampleRate float32, bucket string)

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".

func (*Context) Timer

func (c *Context) Timer(data Data) *Timer

Creates a Timer from the current Context, with the given key/value data.

func (*Context) Timing

func (c *Context) Timing(sampleRate float32, bucket string, d ...time.Duration)

Timing writes a timer value to the Context.

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 ErrorReporter interface {
	Report(err error, data Data) error
}

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

func NewIoLogger(stream io.Writer) *IoLogger

func (*IoLogger) Log

func (l *IoLogger) Log(data Data) error

Log writes the assembled log line.

type Logger

type Logger interface {
	Log(Data) error
}

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.

func SetLogger

func SetLogger(logger Logger) Logger

SetLogger updates the Logger object used by CurrentLogger and CurrentContext.

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

type Timer struct {
	Started  time.Time
	TimeUnit string
	// contains filtered or unexported fields
}

A Timer tracks the duration spent since its creation.

func NewTimer

func NewTimer(data Data) *Timer

NewTimer creates a new Timer with the given key/value data.

func (*Timer) Elapsed

func (t *Timer) Elapsed() time.Duration

Elapsed returns the duration since the Timer was created.

func (*Timer) Finish

func (t *Timer) Finish()

Finish writes a final log message with the elapsed time shown.

func (*Timer) Log

func (t *Timer) Log(data Data) error

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

func (s Timer) SetStatter(statter Statter, sampleRate float32, bucket string)

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".

func (Timer) Timing

func (s Timer) Timing(dur time.Duration)

Timing sends the timing to the configured Statter.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL