log

package
v1.21.1 Latest Latest
Warning

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

Go to latest
Published: Nov 4, 2021 License: Apache-2.0 Imports: 14 Imported by: 8

Documentation

Overview

Package log implements standard go logging

For logging:

log.Info(ctx, "message", log.F{field: 42})
log.Error(...)
log.Debug(...)
log.Fatal(...)

By default, log.Debug is not emitted but instead it is cached. If a higher event arrives within a couple of minutes of the debug log, the cached debug log is emitted (with the correct older timestamp).

Guidance on what type of log to use

Please see the confluence page for logging guidance: https://outreach-io.atlassian.net/wiki/spaces/EN/pages/699695766/Logging+Tracing+and+Metrics

Example
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	log.Info(context.Background(), "example", log.F{"myField": 42})
	log.Warn(context.Background(), "example", log.F{"myField": 42})
	log.Error(context.Background(), "example", log.F{"myField": 42})

	printEntries(logs.Entries())

}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"INFO","message":"example","myField":42}
{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"WARN","message":"example","myField":42}
{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"ERROR","message":"example","myField":42}
Example (AppName)
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/app"
	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	defer app.SetName(app.Info().Name)
	app.SetName("app_name")

	log.Info(context.Background(), "orgful", log.F{"myField": 42})

	printEntries(logs.Entries())
}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.name":"app_name","app.version":"testing","level":"INFO","message":"orgful","myField":42}
Example (With_custom_event)
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	log.Info(context.Background(), "example", MyEvent{"boo"})

	printEntries(logs.Entries())

}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}

// MyEvent demonstrates how custom events can be marshaled
type MyEvent struct {
	SomeField string
}

func (m MyEvent) MarshalRoot() log.Marshaler {
	return log.F{"rootfield": "value"}
}

func (m MyEvent) MarshalLog(addField func(field string, value interface{})) {
	addField("myevent_field", m.SomeField)
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"INFO","message":"example","myevent_field":"boo"}
Example (With_nested_custom_event)
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	log.Info(context.Background(), "example", log.F{
		"error": log.F{
			"cause": " error",
			"data":  MyEvent{"boo"},
		},
	})

	printEntries(logs.Entries())

}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}

// MyEvent demonstrates how custom events can be marshaled
type MyEvent struct {
	SomeField string
}

func (m MyEvent) MarshalRoot() log.Marshaler {
	return log.F{"rootfield": "value"}
}

func (m MyEvent) MarshalLog(addField func(field string, value interface{})) {
	addField("myevent_field", m.SomeField)
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","error.cause":" error","error.data.myevent_field":"boo","level":"INFO","message":"example","rootfield":"value"}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Debug

func Debug(ctx context.Context, message string, m ...Marshaler)

Debug emits a log at DEBUG level but only if an error or fatal happens within 2min of this event

Example (With_error)
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	log.Debug(context.Background(), "msg1", log.F{"myField": 42})
	log.Error(context.Background(), "msg2", log.F{"myField": 42})

	printEntries(logs.Entries())
}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"DEBUG","message":"msg1","myField":42}
{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"ERROR","message":"msg2","myField":42}
Example (Without_error)
package main

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/getoutreach/gobox/pkg/log"
	"github.com/getoutreach/gobox/pkg/log/logtest"
)

func main() {
	logs := logtest.NewLogRecorder(nil)
	defer logs.Close()

	log.Debug(context.Background(), "debug", log.F{"myField": 42})
	log.Info(context.Background(), "debug", log.F{"myField": 42})

	// trigger debug being pushed out but remove it from entries
	log.Error(context.Background(), "moo", nil)
	entries := logs.Entries()
	printEntries(entries[:len(entries)-1])

}

func printEntries(entries []log.F) {
	for _, entry := range entries {
		entry["@timestamp"] = "2019-09-05T14:27:40Z"
		bytes, err := json.Marshal(entry)
		if err != nil {
			fmt.Println("unexpected", err)
		} else {
			fmt.Println(string(bytes))
		}
	}
}
Output:

{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"INFO","message":"debug","myField":42}
{"@timestamp":"2019-09-05T14:27:40Z","app.version":"testing","level":"DEBUG","message":"debug","myField":42}

func Error

func Error(ctx context.Context, message string, m ...Marshaler)

Error emits a log at ERROR level. Error logs must be investigated

func Fatal

func Fatal(ctx context.Context, message string, m ...Marshaler)

Fatal emits a log at FATAL level and exits. This is for catastrophic unrecoverable errors.

func Flush

func Flush(ctx context.Context)

Flush writes out all debug logs

func Info

func Info(ctx context.Context, message string, m ...Marshaler)

Info emits a log at INFO level. This is not filtered and meant for non-debug information.

func Output

func Output() io.Writer

func Purge

func Purge(ctx context.Context)

Purge clears all debug logs without writing them out. This is useful to clear logs from a successful tests that we don't want output during a subsequent test

func SetOutput

func SetOutput(w io.Writer)

SetOutput can be used to set the output for the module Note: this function should not be used in production code outside of service startup. SetOutput can be used for tests that need to redirect or filter logs

func Warn

func Warn(ctx context.Context, message string, m ...Marshaler)

Warn emits a log at WARN level. Warn logs are meant to be investigated if they reach high volumes.

func With

func With(m ...Marshaler) logger

With creates a logger that captures the marshaler arguments.

All methods exposed by the logger automatically add the provided marshalers.

func Write

func Write(s string)

Types

type F

type F map[string]interface{}

F is a map of fields used for logging:

log.Info(ctx, "request started", log.F{"start_time": time.Now()})

When logging errors, use events.Err:

log.Error(ctx, "some failure", events.Err(err))

func (F) MarshalLog

func (f F) MarshalLog(addField func(field string, value interface{}))

MarshalLog implements the Marshaler interface for F

func (F) Set

func (f F) Set(field string, value interface{})

Set writes the field value into F. If the value implements interface { MarshalRoot() log.Marshaler } then it marshals it from the root level. If the value is a log.Marshaler, it recursively marshals that value into F.

type Many

type Many []Marshaler

Many aggregates marshaling of many items

This avoids having to build an append list and also simplifies code

func (Many) MarshalLog

func (m Many) MarshalLog(addField func(key string, v interface{}))

MarshalLog calls MarshalLog on all the individual elements

type Marshaler

type Marshaler interface {
	MarshalLog(addField func(field string, value interface{}))
}

Marshaler is the interface to be implemented by items that can be logged.

The MarshalLog function will be called by the logger with the addField function provided. The implementation an add logging fields using this function. The field value can itself be another Marshaler instance, in which case the field names are concatenated with dot to indicate nesting.

func Caller

func Caller() Marshaler

Caller returns a log entry of the form F{"caller": "fileName:nn"}

Directories

Path Synopsis
internal
logtest provides the ability to test logs Usage: func MyTestFunc(t *testing.T) { logs := logTest.NewLogRecorder(t) defer logs.Close() .....
logtest provides the ability to test logs Usage: func MyTestFunc(t *testing.T) { logs := logTest.NewLogRecorder(t) defer logs.Close() .....

Jump to

Keyboard shortcuts

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