say

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

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

Go to latest
Published: Apr 20, 2016 License: MIT Imports: 13 Imported by: 0

README

Say

Build Status Code Coverage Documentation

Introduction

Say is a logging and metrics-reporting library that increases developer productivity.

The idea is to print everything - logs and metrics - to standard output by default. So that applications using Say are verbose and easier to debug while developing.

In production a listener function is usually set so that log lines and metrics are handled asynchronously in a goroutine exactly as the developer wants. It makes Say extremely fast and flexible.

Download

go get gopkg.in/say.v0

Example

func main() {
	defer say.CapturePanic() // Catch panics as FATAL messages.

	today := time.Now().Format("01/02")

	say.Info("Getting number of users having their birthday", "date", today)
	n, err := countBirthdays(today)
	if err != nil {
		panic(err)
	}
	say.Value("birthdays", n)
}

func countBirthdays(birthday string) (int, error) {
	defer say.NewTiming().Say("query.duration") // Time the function.

	var n int
	query := "SELECT count(id) FROM users WHERE birthday=?"

	err := db.QueryRow(query, id).Scan(&n)
	say.Event("query_user", "query", say.DebugHook(query))

	return n, err
}

This code will output:

INFO  Getting number of users having their birthday	| date="11/19"
EVENT query_user
VALUE query.duration:17ms
VALUE birthdays:6

Using say.Debug(true), the SQL query would be displayed:

...
EVENT query_user	| query="SELECT count(id) FROM users WHERE birthday=?"
...

If an error happens, we have the stack trace:

INFO  Getting number of users having their birthday	| date="11/19"
EVENT query_user
VALUE query.duration:17ms
FATAL sql: database is closed

      main.main()
      	/home/me/go/src/main.go:22 +0x269

In production you will usually want to set a listener:

var prod bool

func init() {
	flag.BoolVar(&prod, "prod", false, "Set to production mode.")
}

func main() {
	defer say.CapturePanic()

	f, err := os.Create("my_app.log")
	if err != nil {
		panic(err)
	}

	say.SetListener(func(m *say.Message) {
		switch m.Type {
		case say.TypeError, say.TypeFatal:
			// Send errors by email or to your favorite webservice.
			fallthrough
		case say.TypeInfo, say.TypeWarning:
			// Log to a file.
			if _, err = m.WriteTo(f); err != nil {
				panic(err)
			}
		}
	})

	// Your code...
}

Features

Developer friendly

Applications using Say are verbose since metrics are printed to standard output by default which tremendously helps debugging.

Say also has a cool API with many nice things that makes the developer's life easier: errors' stack traces are printed by default, one-liner to time a function, simple debugging functions, etc.

Flexible

With Say, it is very easy to handle logs and metrics exactly as you want: writing logs only if an error happens, sending errors by email, sending metrics to a StatsD backend or your favorite webservice, etc.

Lightweight and Fast

Say has been carefully written to be fast and with a low memory footprint. As a result, Say is way faster than most logging libraries and is even faster than the standard library:

BenchmarkStd-4        2000000     694 ns/op     32 B/op    2 allocs/op
BenchmarkSay-4        5000000     334 ns/op      0 B/op    0 allocs/op

BenchmarkStdData-4    1000000    1693 ns/op    112 B/op    6 allocs/op
BenchmarkSayData-4    1000000    1742 ns/op     96 B/op    6 allocs/op
Simple

Say's output is often deterministic (since there is no timestamp by default). So a simple diff of the output of two versions of an application running the same tests can provides quick insights of what changed in the behavior of the application.

License

MIT

Contribute

Do you have any question the documentation does not answer? Is there a use case that you feel is common and is not well-addressed by the current API?

Then you are more than welcome to open an issue or send a pull-request. See CONTRIBUTING.md for more info.

Documentation

Overview

Package say is a logging and metrics-reporting library.

See https://github.com/go-say/say for a presentation of the project.

Introduction

By default, Say prints all messages (logs and metrics) to standard output.

When a listener is set using SetListener(), messages are handler by the listener in a goroutine.

Logging functions

Say provides 5 severity levels:

  • Debug
  • Info
  • Warning
  • Error
  • Fatal

Metrics functions

Say provides 4 metrics-reporting functions:

  • Event: track the occurence of a particular event (user sign-up, query to the database)
  • Value: measure a value associated with a particular event (number of items returned by a search)
  • Timing.Say: measure a duration value (database query duration, webservice call duration)
  • Gauge: capture the current value of something that changes over time (number of active goroutines, number of connected users)

These metrics are directly inspired from StatsD metrics:

  • Event: counter
  • Value: histogram / timing
  • Timing.Say: timing
  • Gauge: gauge

See the function's descriptions below for more info.

Links:

Package-level or methods

These functions can be called at a package-level or you can create a Logger and use the associated methods:

log := new(say.Logger)
log.Info("Hello!")

Data

The point of using a Logger is to associate key-value pairs to it:

log := new(say.Logger)
log.AddData("request_id", requestID)
log.Info("Hello!")
// Output:
INFO   Hello!  | request_id=3

All logging and metric-reporting functions also accept key-value pairs:

Info("Hello!", "name", "Bob", "age", 30)
// Output:
INFO  Hello!  | name="Bob" age=30
Example
// Capture panics as FATAL.
defer say.CapturePanic()

say.Info("Getting list of users...")
say.Value("user_found", 42)
Output:

INFO  Getting list of users...
VALUE user_found:42

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddData

func AddData(key string, value interface{})

AddData adds a key-value pair that will be printed along with all messages sent with the package-level functions.

func CapturePanic

func CapturePanic()

CapturePanic captures panic values as FATAL messages.

Example
defer say.CapturePanic()

panic("oops!") // The panic message will be printed with a FATAL severity.
Output:

func CaptureStandardLog

func CaptureStandardLog()

CaptureStandardLog captures the log lines coming from the log package of the standard library. Captured lines are output with an INFO level.

Example
say.CaptureStandardLog()
log.Print("Hello from the standard library!")
Output:

INFO  Hello from the standard library!

func CheckError

func CheckError(v interface{}, data ...interface{})

CheckError prints an ERROR message with the stack trace.

If v is nil, nothing is printed. If v is a func() error, then Error run v and prints an error only if v return a non-nil error.

Example
f, err := os.Open("foo.txt")
say.CheckError(err)           // Print an error only if err is not nil.
defer say.CheckError(f.Close) // Call Close and print the error if not nil.
Output:

func Debug

func Debug(msg string, data ...interface{})

Debug prints a DEBUG message only if the debug mode is on.

Example
say.SetDebug(false)
say.Debug("foo")
say.SetDebug(true)
say.Debug("bar")
Output:

DEBUG bar

func DisableStackTraces

func DisableStackTraces(b bool)

DisableStackTraces disables printing the stack traces by default. This can still be

func Error

func Error(v interface{}, data ...interface{})

Error prints an ERROR message with the stack trace.

Example
_, err := os.Open("foo.txt")
if err != nil {
	say.Error(err) // Print an error with the stack trace.
}
Output:

func Event

func Event(name string, data ...interface{})

Event prints an EVENT message. Use it to track the occurence of a particular event (e.g. a user signs up, a database query fails).

Example
say.Event("new_user", "id", 7654)
Output:

EVENT new_user	| id=7654

func Events

func Events(name string, incr int, data ...interface{})

Events prints an EVENT message with an increment value. Use it to track the occurence of a batch of events (e.g. how many new files were uploaded).

Example
say.Events("file_uploaded", 3)
Output:

EVENT file_uploaded:3

func Fatal

func Fatal(v interface{}, data ...interface{})

Fatal prints a FATAL message with the stack trace.

Example
_, err := os.Open("foo.txt")
if err != nil {
	say.Fatal(err) // Print a fatal error with the stack trace.
}
Output:

func Flush

func Flush()

Flush flushes the message queue. It is a no-op when SetListener has not been used.

func Gauge

func Gauge(name string, value interface{}, data ...interface{})

Gauge prints a GAUGE message. Use it to capture the current value of something that changes over time (e.g. number of active goroutines, number of connected users)

Example
say.Gauge("connected_users", 73)
Output:

GAUGE connected_users:73

func Info

func Info(msg string, data ...interface{})

Info prints an INFO message.

Example
say.Info("Connecting to server...", "ip", "127.0.0.1")
Output:

INFO  Connecting to server...	| ip="127.0.0.1"

func Mute

func Mute() io.Writer

Mute disables any output. It is the same as Redirect(ioutil.Discard).

func Redirect

func Redirect(w io.Writer) (oldW io.Writer)

Redirect redirects the output to the given writer. It returns the writer where outputs were previously redirected to.

It is only effective when SetListener has not been used.

func SetData

func SetData(data ...interface{})

SetData sets a key-value pair that will be printed along with all messages sent with the package-level functions.

func SetDebug

func SetDebug(b bool)

SetDebug sets whether Say is in debug mode. The debug mode is off by default.

This function must not be called concurrently with the other functions of this package.

func SetListener

func SetListener(f func(*Message))

SetListener sets the function that is applied to each message.

SetListener(nil) restores the default behavior wich is printing messages to the standard output.

func Time

func Time(name string, f func(), data ...interface{})

Time prints a VALUE message with the duration in milliseconds of running f.

Example
say.Time("duration", func() {
	// The code that needs to be timed.
})
Output:

VALUE duration:17ms

func Value

func Value(name string, value interface{}, data ...interface{})

Value prints a VALUE message. Use it to measure a value associated with a particular event (e.g. number of items returned by a search).

Example
say.Value("search_items", 117)
Output:

VALUE search_items:117

func Warning

func Warning(v interface{}, data ...interface{})

Warning prints a WARNING message.

Example
say.Warning("Could not connect to host", "host", "example.com")
Output:

WARN  Could not connect to host	| host="example.com"

Types

type Data

type Data []KVPair

Data is a list of key-value pairs associated with a message.

func (Data) Get

func (d Data) Get(key string) (value interface{}, ok bool)

Get gets the value associated with the given key as an unquoted string. If the given key does not exists ok is false.

type Hook

type Hook func() interface{}

A Hook is a function used to provide dynamic Data values.

Example
goroutinesHook := say.Hook(func() interface{} {
	return runtime.NumGoroutine
})
// Print the current number of goroutines with each message.
say.SetData("num_goroutine", goroutinesHook)
Output:

func DebugHook

func DebugHook(v interface{}) Hook

DebugHook allows printing a key-value pairs only when Say is in debug mode.

Example
query := "SELECT * FROM users WHERE id = ?"
say.SetDebug(true)
say.Event("db.get_user", "query", say.DebugHook(query)) // Print the query.
say.SetDebug(false)
say.Event("db.get_user", "query", say.DebugHook(query)) // Omit the query.
Output:

EVENT db.get_user	| query="SELECT * FROM users WHERE id = ?"
EVENT db.get_user

func TimeHook

func TimeHook(format string) Hook

TimeHook prints the current time.

Example
// Print the current timestamp with each message.
say.SetData("num_goroutine", say.TimeHook("2006-01-02 15:04:05"))
Output:

type KVPair

type KVPair struct {
	Key   string
	Value interface{}
}

KVPair represents a key-value pair.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger is the object that prints messages.

func NewLogger

func NewLogger(opts ...Option) *Logger

NewLogger creates a new Logger inherits the Data and SkipStackFrames values from the package-level Logger.

Example
say.SetData("weather", "sunny")
log := say.NewLogger()
log.Info("hello") // INFO  hello	| weather="sunny"
Output:

func (*Logger) AddData

func (l *Logger) AddData(key string, value interface{})

AddData adds a key-value pair that will be printed along with all messages sent with this Logger.

Example
log := new(say.Logger)
log.AddData("id", 5)
log.Info("hello")
log.AddData("foo", "bar")
log.Info("dear")
Output:

INFO  hello	| id=5
INFO  dear	| id=5 foo="bar"

func (*Logger) CapturePanic

func (l *Logger) CapturePanic()

CapturePanic captures panic values as FATAL messages.

Example
log := new(say.Logger)
defer log.CapturePanic()

panic("oops!") // The panic message will be printed with a FATAL severity.
Output:

func (*Logger) CaptureStandardLog

func (l *Logger) CaptureStandardLog()

CaptureStandardLog captures the log lines coming from the log package of the standard library. Captured lines are output with an INFO level.

Example
l := new(say.Logger)
l.CaptureStandardLog()
log.Print("Hello from the standard library!")
Output:

INFO  Hello from the standard library!

func (*Logger) CheckError

func (l *Logger) CheckError(v interface{}, data ...interface{})

CheckError prints an ERROR message with the stack trace.

If v is nil, nothing is printed. If v is a func() error, then Error run v and prints an error only if v return a non-nil error.

Example
f, err := os.Open("foo.txt")
log := new(say.Logger)
log.CheckError(err)           // Print an error only if err is not nil.
defer log.CheckError(f.Close) // Call Close and print the error if not nil.
Output:

func (*Logger) Debug

func (l *Logger) Debug(msg string, data ...interface{})

Debug prints a DEBUG message only if the debug mode is on.

Example
log := new(say.Logger)
say.SetDebug(false)
log.Debug("foo")
say.SetDebug(true)
log.Debug("bar")
Output:

DEBUG bar

func (*Logger) Error

func (l *Logger) Error(v interface{}, data ...interface{})

Error prints an ERROR message with the stack trace.

Example
log := new(say.Logger)
_, err := os.Open("foo.txt")
if err != nil {
	log.Error(err) // Print an error with the stack trace.
}
Output:

func (*Logger) Event

func (l *Logger) Event(name string, data ...interface{})

Event prints an EVENT message. Use it to track the occurence of a particular event (e.g. a user signs up, a database query fails).

Example
log := new(say.Logger)
log.Event("new_user", "id", 7654)
Output:

EVENT new_user	| id=7654

func (*Logger) Events

func (l *Logger) Events(name string, incr int, data ...interface{})

Events prints an EVENT message with an increment value. Use it to track the occurence of a batch of events (e.g. how many new files were uploaded).

Example
log := new(say.Logger)
log.Events("file_uploaded", 3)
Output:

EVENT file_uploaded:3

func (*Logger) Fatal

func (l *Logger) Fatal(v interface{}, data ...interface{})

Fatal prints a FATAL message with the stack trace.

Example
log := new(say.Logger)
_, err := os.Open("foo.txt")
if err != nil {
	log.Fatal(err) // Print a fatal error with the stack trace.
}
Output:

func (*Logger) Gauge

func (l *Logger) Gauge(name string, value interface{}, data ...interface{})

Gauge prints a GAUGE message. Use it to capture the current value of something that changes over time (e.g. number of active goroutines, number of connected users)

Example
log := new(say.Logger)
log.Gauge("connected_users", 73)
Output:

GAUGE connected_users:73

func (*Logger) Info

func (l *Logger) Info(msg string, data ...interface{})

Info prints an INFO message.

Example
log := new(say.Logger)
log.Info("Connecting to server...", "ip", "127.0.0.1")
Output:

INFO  Connecting to server...	| ip="127.0.0.1"

func (*Logger) NewLogger

func (l *Logger) NewLogger(opts ...Option) *Logger

NewLogger creates a new Logger that inherits the Data and SkipStackFrames values from the parent Logger.

Example
log := new(say.Logger) // Create a clean Logger.
log.SetData("id", 5)
log2 := log.NewLogger() // log2 inherits its parent settings.
log2.AddData("age", 53)
log2.Info("hello")
Output:

INFO  hello	| id=5 age=53

func (*Logger) NewTiming

func (l *Logger) NewTiming() Timing

NewTiming returns a new Timing with the same associated data than the Logger.

func (*Logger) SetData

func (l *Logger) SetData(data ...interface{})

SetData sets a key-value pair that will be printed along with all messages sent with this Logger.

Example
log := new(say.Logger)
log.SetData("id", 5, "foo", "bar")
log.Info("hello")
Output:

INFO  hello	| id=5 foo="bar"

func (*Logger) Time

func (l *Logger) Time(name string, f func(), data ...interface{})

Time prints a VALUE message with the duration in milliseconds of running f.

Example
log := new(say.Logger)
log.Time("duration", func() {
	// The code that needs to be timed.
})
Output:

VALUE duration:17ms

func (*Logger) Value

func (l *Logger) Value(name string, value interface{}, data ...interface{})

Value prints a VALUE message. Use it to measure a value associated with a particular event (e.g. number of items returned by a search).

Example
log := new(say.Logger)
log.Value("search_items", 117)
Output:

VALUE search_items:117

func (*Logger) Warning

func (l *Logger) Warning(v interface{}, data ...interface{})

Warning prints a WARNING message.

Example
log := new(say.Logger)
log.Warning("Could not connect to host", "host", "example.com")
Output:

WARN  Could not connect to host	| host="example.com"

type Message

type Message struct {
	Type    Type
	Content string
	Data    Data
}

A Message represents a log line or a metric.

func (*Message) Duration

func (m *Message) Duration() (time.Duration, bool)

Duration returns the duration of a VALUE message. If the value is not a duration, ok is false.

func (*Message) Error

func (m *Message) Error() string

Error returns the error message of an ERROR or FATAL message.

func (*Message) Float64

func (m *Message) Float64() (float64, bool)

Float64 returns the value as an float64. If the value is not a float64, ok is false. If the value is a duration in milliseconds, return the number of milliseconds. It returns 1 if the message is an EVENT without an increment.

func (*Message) Int

func (m *Message) Int() (n int, ok bool)

Int returns the value as an integer. If the value is not an integer, ok is false. If the value is a duration in milliseconds, return the number of milliseconds. It returns 1 if the message is an EVENT without an increment.

func (*Message) Key

func (m *Message) Key() string

Key returns the key of an EVENT, VALUE or GAUGE message.

func (*Message) StackTrace

func (m *Message) StackTrace() string

StackTrace returns the stack trace of an ERROR or FATAL message.

func (*Message) Value

func (m *Message) Value() string

Value returns the value of an EVENT, VALUE or GAUGE message.

func (*Message) WriteJSONTo

func (m *Message) WriteJSONTo(w io.Writer) (int, error)

WriteJSONTo writes the JSON-encoded form of the Message to w.

func (*Message) WriteTo

func (m *Message) WriteTo(w io.Writer) (int64, error)

WriteTo writes the Message to w.

type Option

type Option func(*Logger)

An Option allows to customize a Logger.

func SkipStackFrames

func SkipStackFrames(skip int) Option

SkipStackFrames sets the number of stack frames to skip in the Error and Fatal methods. It is 0 by default.

A value of -1 disable printing the stack traces with this Logger.

type Timing

type Timing struct {
	// contains filtered or unexported fields
}

A Timing helps printing a duration.

func NewTiming

func NewTiming() Timing

NewTiming returns a new Timing with the package-level data.

func (Timing) Get

func (t Timing) Get() time.Duration

Get returns the duration since the Timing has been created.

func (Timing) Say

func (t Timing) Say(name string, data ...interface{})

Say prints a VALUE message with the duration in milliseconds since the Timing has been created. Use it to measure a duration value (e.g. database query duration, webservice call duration).

Example
t := say.NewTiming()
// Do some stuff.
t.Say("duration")
Output:

VALUE duration:17ms

type Type

type Type string

A Type represents a message type.

const (
	TypeEvent   Type = "EVENT"
	TypeValue   Type = "VALUE"
	TypeGauge   Type = "GAUGE"
	TypeDebug   Type = "DEBUG"
	TypeInfo    Type = "INFO "
	TypeWarning Type = "WARN "
	TypeError   Type = "ERROR"
	TypeFatal   Type = "FATAL"
)

All the available message types.

Jump to

Keyboard shortcuts

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