Documentation
¶
Overview ¶
Package ctxd defines contextualized structured logging and error handling.
Index ¶
- func AddFields(ctx context.Context, keysAndValues ...interface{}) context.Context
- func ClearFields(ctx context.Context) context.Context
- func Fields(ctx context.Context) []interface{}
- func IsDebug(ctx context.Context) bool
- func LabeledError(err error, labels ...error) error
- func LogError(ctx context.Context, err error, l LogFunc)
- func LogWriter(ctx context.Context) io.Writer
- func MultiError(primary error, secondary ...error) error
- func NewError(ctx context.Context, message string, keysAndValues ...interface{}) error
- func SetFields(ctx context.Context, keysAndValues ...interface{}) context.Context
- func WithDebug(ctx context.Context) context.Context
- func WithLogWriter(ctx context.Context, w io.Writer) context.Context
- func WrapError(ctx context.Context, err error, message string, keysAndValues ...interface{}) error
- type DeferredJSON
- type DeferredString
- type FieldNames
- type LogFunc
- type Logger
- type LoggerMock
- func (m *LoggerMock) Debug(ctx context.Context, msg string, keysAndValues ...interface{})
- func (m *LoggerMock) Error(ctx context.Context, msg string, keysAndValues ...interface{})
- func (m *LoggerMock) Important(ctx context.Context, msg string, keysAndValues ...interface{})
- func (m *LoggerMock) Info(ctx context.Context, msg string, keysAndValues ...interface{})
- func (m *LoggerMock) Warn(ctx context.Context, msg string, keysAndValues ...interface{})
- type LoggerProvider
- type NoOpLogger
- func (NoOpLogger) CtxdLogger() Logger
- func (NoOpLogger) Debug(_ context.Context, _ string, _ ...interface{})
- func (NoOpLogger) Error(_ context.Context, _ string, _ ...interface{})
- func (NoOpLogger) Important(_ context.Context, _ string, _ ...interface{})
- func (NoOpLogger) Info(_ context.Context, _ string, _ ...interface{})
- func (NoOpLogger) Warn(_ context.Context, _ string, _ ...interface{})
- type SentinelError
- type StructuredError
- type Tuples
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AddFields ¶
AddFields returns context with added loosely-typed key-value pairs as fields.
If key-value pairs exist in parent context already, new pairs are appended.
Example ¶
package main import ( "context" "fmt" "github.com/bool64/ctxd" ) func main() { logger := ctxd.LoggerMock{} // Once instrumented context can aid logger with structured information. ctx := ctxd.AddFields(context.Background(), "foo", "bar") logger.Info(ctx, "something happened") // Also context contributes additional information to structured errors. err := ctxd.NewError(ctx, "something failed", "baz", "quux") ctxd.LogError(ctx, err, logger.Error) fmt.Print(logger.String()) }
Output: info: something happened {"foo":"bar"} error: something failed {"baz":"quux","foo":"bar"}
func ClearFields ¶
ClearFields returns context without any fields.
func LabeledError ¶ added in v1.1.0
LabeledError adds indicative errors to an error wrap.
Labels could be checked with errors.Is, errors.As. Error message remains the same with original error.
Example ¶
package main import ( "errors" "fmt" "github.com/bool64/ctxd" ) func main() { err1 := errors.New("failed") label1 := ctxd.SentinelError("miserably") label2 := ctxd.SentinelError("hopelessly") err := ctxd.LabeledError(fmt.Errorf("oops: %w", err1), label1, label2) fmt.Println("err is err1:", errors.Is(err, err1)) fmt.Println("err is label1:", errors.Is(err, label1)) fmt.Println("err is label2:", errors.Is(err, label2)) // Labels do not implicitly contribute to error message. fmt.Println("error message:", err.Error()) // If there are two matches, only first is returned. var se ctxd.SentinelError fmt.Println("err as &se:", errors.As(err, &se)) fmt.Println("err as value:", string(se)) }
Output: err is err1: true err is label1: true err is label2: true error message: oops: failed err as &se: true err as value: miserably
func LogError ¶
LogError pushes error value to a contextualized logger method.
If err is nil, LogError produces no operation. LogError function matches Logger methods, e.g. Error.
func MultiError ¶ added in v1.2.0
MultiError creates an error with multiple unwrappables.
Secondary errors could be checked with errors.Is, errors.As. Error message remains the same with primary error.
Multi errors can be used to augment error with multiple checkable perks, without a limitation of single wrapping inheritance.
func NewError ¶
NewError creates error with optional structured data.
LogError fields from context are also added to error structured data.
func SetFields ¶ added in v0.1.4
SetFields returns context with added loosely-typed key-value pairs as fields.
Values of same keys that are already existing in parent context are replaced.
func WithLogWriter ¶
WithLogWriter returns context with custom log writer. Can be useful to write logs into response stream.
func WrapError ¶
WrapError returns an error annotated with message and structured data.
If err is nil, WrapError returns nil. LogError fields from context are also added to error structured data.
Example ¶
package main import ( "context" "errors" "fmt" "github.com/bool64/ctxd" ) func main() { ctx := context.Background() // Elaborate context with fields. ctx = ctxd.AddFields(ctx, "field1", 1, "field2", "abc", ) // Add more fields when creating error. err := ctxd.NewError(ctx, "something failed", "field3", 3.0, ) err2 := ctxd.WrapError( // You can use same or any other context when wrapping error. ctxd.AddFields(context.Background(), "field5", "V"), err, "wrapped", "field4", true) // Setup your logger. var ( lm = ctxd.LoggerMock{} logger ctxd.Logger = &lm ) // Inspect error fields. var se ctxd.StructuredError if errors.As(err, &se) { fmt.Printf("error fields: %v\n", se.Fields()) } // Log errors. ctxd.LogError(ctx, err, logger.Error) ctxd.LogError(ctx, err2, logger.Warn) fmt.Print(lm.String()) }
Output: error fields: map[field1:1 field2:abc field3:3] error: something failed {"field1":1,"field2":"abc","field3":3} warn: wrapped: something failed {"field1":1,"field2":"abc","field3":3,"field4":true,"field5":"V"}
Types ¶
type DeferredJSON ¶
type DeferredJSON func() interface{}
DeferredJSON postpones log field processing, suitable for debug logging.
func (DeferredJSON) MarshalJSON ¶
func (d DeferredJSON) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler.
type DeferredString ¶
type DeferredString func() interface{}
DeferredString postpones log field processing, suitable for debug logging.
func (DeferredString) String ¶
func (d DeferredString) String() string
String implements fmt.Stringer.
type FieldNames ¶ added in v0.1.4
type FieldNames struct { Timestamp string `default:"@timestamp"` Message string `default:"message"` // ClientIP is an IP address of the client (IPv4 or IPv6). ClientIP string `default:"client.ip"` HTTPMethod string `default:"http.request.method"` HTTPResponseBytes string `default:"http.response.bytes"` HTTPResponseStatus string `default:"http.response.status_code"` URL string `default:"url.original"` // UserAgentOriginal is an unparsed user_agent string. UserAgentOriginal string `default:"user_agent.original"` SpanID string `default:"span.id"` TraceID string `default:"trace.id"` TransactionID string `default:"transaction.id"` }
FieldNames defines standard field names.
Default names are aligned with https://www.elastic.co/guide/en/ecs/current/ecs-field-reference.html.
type Logger ¶
type Logger interface { // Debug logs a message. Debug(ctx context.Context, msg string, keysAndValues ...interface{}) // Info logs a message. Info(ctx context.Context, msg string, keysAndValues ...interface{}) // Important forcibly logs an important message with level INFO disregarding logger level constraints. // Can be used for logging historically important information. Important(ctx context.Context, msg string, keysAndValues ...interface{}) // Warn logs a message. Warn(ctx context.Context, msg string, keysAndValues ...interface{}) // Error logs a message. Error(ctx context.Context, msg string, keysAndValues ...interface{}) }
Logger is a contextualized structured logger.
Logging methods accept keys and values as variadic argument that contains loosely-typed key-value pairs. When processing pairs, the first element of the pair is used as the field key and the second as the field value.
func LoggerWithFields ¶
LoggerWithFields instruments contextualized logger with global fields.
Example ¶
package main import ( "context" "fmt" "github.com/bool64/ctxd" ) func main() { lm := ctxd.LoggerMock{} var globalLogger ctxd.Logger = &lm localLogger := ctxd.LoggerWithFields(globalLogger, "local", 123) ctx1 := ctxd.AddFields(context.Background(), "ctx", 1, "foo", "bar", ) ctx2 := ctxd.AddFields(context.Background(), "ctx", 2) localLogger.Info(ctx1, "hello", "he", "lo") localLogger.Warn(ctx2, "bye", "by", "ee") fmt.Print(lm.String()) }
Output: info: hello {"ctx":1,"foo":"bar","he":"lo","local":123} warn: bye {"by":"ee","ctx":2,"local":123}
type LoggerMock ¶ added in v0.1.2
type LoggerMock struct { OnError func(err error) sync.Mutex bytes.Buffer LoggedEntries []struct { Time time.Time `json:"time"` Level string `json:"level"` Message string `json:"message"` Data map[string]interface{} `json:"data,omitempty"` } }
LoggerMock logs messages to internal buffer.
func (*LoggerMock) Debug ¶ added in v0.1.2
func (m *LoggerMock) Debug(ctx context.Context, msg string, keysAndValues ...interface{})
Debug logs a message.
func (*LoggerMock) Error ¶ added in v0.1.2
func (m *LoggerMock) Error(ctx context.Context, msg string, keysAndValues ...interface{})
Error logs a message.
func (*LoggerMock) Important ¶ added in v0.1.2
func (m *LoggerMock) Important(ctx context.Context, msg string, keysAndValues ...interface{})
Important logs a message.
type LoggerProvider ¶
type LoggerProvider interface {
CtxdLogger() Logger
}
LoggerProvider is an embeddable provider interface.
type NoOpLogger ¶
type NoOpLogger struct{}
NoOpLogger is a contextualized logger stub.
func (NoOpLogger) Debug ¶
func (NoOpLogger) Debug(_ context.Context, _ string, _ ...interface{})
Debug discards debug message.
func (NoOpLogger) Error ¶
func (NoOpLogger) Error(_ context.Context, _ string, _ ...interface{})
Error discards error message.
func (NoOpLogger) Important ¶
func (NoOpLogger) Important(_ context.Context, _ string, _ ...interface{})
Important discards important message.
type SentinelError ¶ added in v1.1.0
type SentinelError string
SentinelError is a constant error.
See https://dave.cheney.net/2016/04/07/constant-errors for more details.
func (SentinelError) Error ¶ added in v1.1.0
func (e SentinelError) Error() string
Error returns error message.
type StructuredError ¶
type StructuredError interface { // Error returns message of error. Error() string // Tuples returns structured data of error in form of loosely-typed key-value pairs. Tuples() []interface{} // Fields returns structured data of error as a map. Fields() map[string]interface{} }
StructuredError defines error with message and data.