...

Package slog

import "cdr.dev/slog"
Overview
Index
Examples
Subdirectories

Overview ▾

Package slog implements minimal structured logging.

See https://cdr.dev/slog for overview docs and a comparison with existing libraries.

The examples are the best way to understand how to use this library effectively.

The Logger type implements a high level API around the Sink interface. Logger implements Sink as well to allow composition.

Implementations of the Sink interface are available in the sloggers subdirectory.

Example

Code:

log := slog.Make(sloghuman.Sink(os.Stdout))

log.Info(context.Background(), "my message here",
    slog.F("field_name", "something or the other"),
    slog.F("some_map", slog.M(
        slog.F("nested_fields", time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC)),
    )),
    slog.Error(
        xerrors.Errorf("wrap1: %w",
            xerrors.Errorf("wrap2: %w",
                io.EOF,
            ),
        ),
    ),
)

// 2019-12-09 05:04:53.398 [INFO]	<example.go:16>	my message here	{"field_name": "something or the other", "some_map": {"nested_fields": "2000-02-05T04:04:04Z"}} ...
//  "error": wrap1:
//      main.main
//          /Users/nhooyr/src/cdr/scratch/example.go:22
//    - wrap2:
//      main.main
//          /Users/nhooyr/src/cdr/scratch/example.go:23
//    - EOF

Example (Marshaller)

Code:

package slog_test

import (
    "context"
    "os"

    "cdr.dev/slog"
    "cdr.dev/slog/sloggers/sloghuman"
)

type myStruct struct {
    foo int
    bar int
}

func (s myStruct) MarshalJSON() ([]byte, error) {
    return slog.M(
        slog.F("foo", s.foo),
        slog.F("bar", s.foo),
    ).MarshalJSON()
}

func Example_marshaller() {
    l := slog.Make(sloghuman.Sink(os.Stdout))

    l.Info(context.Background(), "wow",
        slog.F("myStruct", myStruct{
            foo: 1,
            bar: 2,
        }),
    )

    // 2019-12-16 17:31:37.120 [INFO]	<example_marshaller_test.go:26>	wow	{"myStruct": {"foo": 1, "bar": 1}}
}

Example (Multiple)

Code:

l := slog.Make(sloghuman.Sink(os.Stdout))

f, err := os.OpenFile("stackdriver", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
    l.Fatal(context.Background(), "failed to open stackdriver log file", slog.Error(err))
}

l = l.AppendSinks(slogstackdriver.Sink(f))

l.Info(context.Background(), "log to stdout and stackdriver")

// 2019-12-07 20:59:55.790 [INFO]	<example_test.go:46>	log to stdout and stackdriver

Example (Struct)

Code:

l := slog.Make(sloghuman.Sink(os.Stdout))

type hello struct {
    Meow int       `json:"meow"`
    Bar  string    `json:"bar"`
    M    time.Time `json:"m"`
}

l.Info(context.Background(), "check out my structure",
    slog.F("hello", hello{
        Meow: 1,
        Bar:  "barbar",
        M:    time.Date(2000, time.February, 5, 4, 4, 4, 0, time.UTC),
    }),
)

// 2019-12-16 17:31:51.769 [INFO]	<example_test.go:56>	check out my structure	{"hello": {"meow": 1, "bar": "barbar", "m": "2000-02-05T04:04:04Z"}}

Example (Testing)

Code:

// Provided by the testing package in tests.
var t testing.TB

slogtest.Info(t, "my message here",
    slog.F("field_name", "something or the other"),
)

// t.go:55: 2019-12-05 21:20:31.218 [INFO]	<examples_test.go:42>	my message here	{"field_name": "something or the other"}

Example (Tracing)

Code:

log := slog.Make(sloghuman.Sink(os.Stdout))

ctx, _ := trace.StartSpan(context.Background(), "spanName")

log.Info(ctx, "my msg", slog.F("hello", "hi"))

// 2019-12-09 21:59:48.110 [INFO]	<example_test.go:62>	my msg	{"trace": "f143d018d00de835688453d8dc55c9fd", "span": "f214167bf550afc3", "hello": "hi"}

func Helper

func Helper()

Helper marks the calling function as a helper and skips it for source location information. It's the slog equivalent of testing.TB.Helper().

Example

Code:

package slog_test

import (
    "context"
    "net/http"
    "os"

    "cdr.dev/slog"
    "cdr.dev/slog/sloggers/sloghuman"
)

func httpLogHelper(ctx context.Context, status int) {
    slog.Helper()

    l.Info(ctx, "sending HTTP response",
        slog.F("status", status),
    )
}

var l = slog.Make(sloghuman.Sink(os.Stdout))

func ExampleHelper() {
    ctx := context.Background()
    httpLogHelper(ctx, http.StatusBadGateway)

    // 2019-12-07 21:18:42.567 [INFO]	<example_helper_test.go:24>	sending HTTP response	{"status": 502}
}

func Stdlib

func Stdlib(ctx context.Context, l Logger, level Level) *log.Logger

Stdlib creates a standard library logger from the given logger.

All logs will be logged at the level set by the logger and the given ctx will be passed to the logger's Log method, thereby logging all fields and tracing info in the context.

You can redirect the stdlib default logger with log.SetOutput to the Writer on the logger returned by this function. See the example.

Example

Code:

ctx := slog.With(context.Background(), slog.F("field", 1))
l := slog.Stdlib(ctx, slog.Make(sloghuman.Sink(os.Stdout)), slog.LevelInfo)

l.Print("msg")

// 2019-12-07 20:54:23.986 [INFO]	(stdlib)	<example_test.go:29>	msg	{"field": 1}

func With

func With(ctx context.Context, fields ...Field) context.Context

With returns a context that contains the given fields.

Any logs written with the provided context will have the given logs prepended.

It will append to any fields already in ctx.

Example

Code:

ctx := slog.With(context.Background(), slog.F("field", 1))

l := slog.Make(sloghuman.Sink(os.Stdout))
l.Info(ctx, "msg")

// 2019-12-07 20:54:23.986 [INFO]	<example_test.go:20>	msg	{"field": 1}

type Field

Field represents a log field.

type Field struct {
    Name  string
    Value interface{}
}

func Error

func Error(err error) Field

Error is the standard key used for logging a Go error value.

func F

func F(name string, value interface{}) Field

F is a convenience constructor for Field.

type Level

Level represents a log level.

type Level int

The supported log levels.

The default level is Info.

const (
    // LevelDebug is used for development and debugging messages.
    LevelDebug Level = iota

    // LevelInfo is used for normal informational messages.
    LevelInfo

    // LevelWarn is used when something has possibly gone wrong.
    LevelWarn

    // LevelError is used when something has certainly gone wrong.
    LevelError

    // LevelCritical is used when when something has gone wrong and should
    // be immediately investigated.
    LevelCritical

    // LevelFatal is used when the process is about to exit due to an error.
    LevelFatal
)

func (Level) String

func (l Level) String() string

String implements fmt.Stringer.

type Logger

Logger wraps Sink with a nice API to log entries.

Logger is safe for concurrent use.

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

func Make

func Make(sinks ...Sink) Logger

Make creates a logger that writes logs to the passed sinks at LevelInfo.

func (Logger) AppendSinks

func (l Logger) AppendSinks(s ...Sink) Logger

AppendSinks appends the sinks to the set sink targets on the logger.

func (Logger) Critical

func (l Logger) Critical(ctx context.Context, msg string, fields ...Field)

Critical logs the msg and fields at LevelCritical.

It will then Sync().

func (Logger) Debug

func (l Logger) Debug(ctx context.Context, msg string, fields ...Field)

Debug logs the msg and fields at LevelDebug.

func (Logger) Error

func (l Logger) Error(ctx context.Context, msg string, fields ...Field)

Error logs the msg and fields at LevelError.

It will then Sync().

func (Logger) Fatal

func (l Logger) Fatal(ctx context.Context, msg string, fields ...Field)

Fatal logs the msg and fields at LevelFatal.

It will then Sync() and os.Exit(1).

func (Logger) Info

func (l Logger) Info(ctx context.Context, msg string, fields ...Field)

Info logs the msg and fields at LevelInfo.

func (Logger) Leveled

func (l Logger) Leveled(level Level) Logger

Leveled returns a Logger that only logs entries equal to or above the given level.

Example

Code:

ctx := context.Background()

l := slog.Make(sloghuman.Sink(os.Stdout))
l.Debug(ctx, "testing1")
l.Info(ctx, "received request")

l = l.Leveled(slog.LevelDebug)

l.Debug(ctx, "testing2")

// 2019-12-07 21:26:20.945 [INFO]	<example_test.go:95>	received request
// 2019-12-07 21:26:20.945 [DEBUG]	<example_test.go:99>	testing2

func (Logger) Log

func (l Logger) Log(ctx context.Context, e SinkEntry)

Log logs the given entry with the context to the underlying sinks.

It extends the entry with the set fields and names.

func (Logger) Named

func (l Logger) Named(name string) Logger

Named appends the name to the set names on the logger.

Example

Code:

ctx := context.Background()

l := slog.Make(sloghuman.Sink(os.Stdout))
l = l.Named("http")
l.Info(ctx, "received request", slog.F("remote address", net.IPv4(127, 0, 0, 1)))

// 2019-12-07 21:20:56.974 [INFO]	(http)	<example_test.go:85>	received request	{"remote address": "127.0.0.1"}

func (Logger) Sync

func (l Logger) Sync()

Sync calls Sync on all the underlying sinks.

func (Logger) Warn

func (l Logger) Warn(ctx context.Context, msg string, fields ...Field)

Warn logs the msg and fields at LevelWarn.

func (Logger) With

func (l Logger) With(fields ...Field) Logger

With returns a Logger that prepends the given fields on every logged entry.

It will append to any fields already in the Logger.

type Map

Map represents an ordered map of fields.

type Map []Field

func M

func M(fs ...Field) Map

M is a convenience constructor for Map

func (Map) MarshalJSON

func (m Map) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler.

It is guaranteed to return a nil error. Any error marshalling a field will become the field's value.

Every field value is encoded with the following process:

1. json.Marshaller is handled.

2. xerrors.Formatter is handled.

3. structs that have a field with a json tag are encoded with json.Marshal.

4. error and fmt.Stringer is handled.

5. slices and arrays go through the encode function for every element.

6. For values that cannot be encoded with json.Marshal, fmt.Sprintf("%+v") is used.

7. json.Marshal(v) is used for all other values.

type Sink

Sink is the destination of a Logger.

All sinks must be safe for concurrent use.

type Sink interface {
    LogEntry(ctx context.Context, e SinkEntry)
    Sync()
}

type SinkEntry

SinkEntry represents the structure of a log entry. It is the argument to the sink when logging.

type SinkEntry struct {
    Time time.Time

    Level   Level
    Message string

    LoggerNames []string

    Func string
    File string
    Line int

    SpanContext trace.SpanContext

    Fields Map
}

Subdirectories

Name Synopsis
..
sloggers
sloghuman Package sloghuman contains the slogger that writes logs in a human readable format.
slogjson Package slogjson contains the slogger that writes logs in JSON.
slogstackdriver Package slogstackdriver contains the slogger for google cloud's stackdriver.
slogtest Package slogtest contains the slogger for use with Go's testing package.
assert Package assert is a helper package for test assertions.