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,"service_name":"app_name"}
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 ¶
- func Debug(ctx context.Context, message string, m ...Marshaler)
- func Error(ctx context.Context, message string, m ...Marshaler)
- func Fatal(ctx context.Context, message string, m ...Marshaler)
- func Flush(ctx context.Context)
- func Info(ctx context.Context, message string, m ...Marshaler)
- func Output() io.Writer
- func Purge(ctx context.Context)
- func SetOutput(w io.Writer)
- func Warn(ctx context.Context, message string, m ...Marshaler)
- func With(m ...Marshaler) logger
- func Write(s string)
- type F
- type Many
- type Marshaler
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Debug ¶
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 Fatal ¶
Fatal emits a log at FATAL level and exits. This is for catastrophic unrecoverable errors.
func Info ¶
Info emits a log at INFO level. This is not filtered and meant for non-debug information.
func Purge ¶
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 ¶
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 ¶
Warn emits a log at WARN level. Warn logs are meant to be investigated if they reach high volumes.
Types ¶
type F ¶
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))
type Many ¶
Many aggregates marshaling of many items
This avoids having to build an append list and also simplifies code
type Marshaler ¶
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.