log

package
v1.0.22 Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2024 License: MIT Imports: 20 Imported by: 0

README

ca-go/log

The log package implements the Logging Standard. The design of this package is to provide a simple structured logging system that can be used in a variety of situations without requiring high cognitive load.

There are no new loggers to create or pass around, instead there is a singleton logger created in the package that you can call directly.

The log package wraps zerolog and therefore requires that you end all logging statements with a Details("_your_message_here") to actually emit the log.

Environment Variables

You MUST set these:

  • APP = The application name (eg. "employee-tasks-service")
  • AWS_REGION = The AWS region this code is running in (eg. "us-west-1")
  • PRODUCT = The product suite the service belongs to (eg. "engagement")

You can OPTIONALLY set these:

  • LOG_LEVEL = One of DEBUG, INFO, WARN, ERROR, defaults to "INFO"
  • AWS_ACCOUNT_ID = The AWS account Id this code is running in, defaults to "development"
  • FARM = The name of the farm or where the code is running, defaults to "local" (eg. "production", "dolly")
  • APP_VERSION = The version of the application, defaults to "1.0.0"

Use in Unit Tests

By default the logger will emit messages when running inside a test. You can override this behaviour by setting the QUIET_MODE environment variable to "true".

When running localling you can also set the CONSOLE_WRITER to "true" to change from json to key-value output. If you want coloured console output you can also set CONSOLE_COLOUR to "true".

Note: Never run with the CONSOLE_WRITER set to "true" in production.

Extensions

The log package includes some extensions for common groups:

  • WithRequestTracing(req *http.Request)
  • WithAuthenticatedUserTracing(auth *AuthPayload)
  • WithAuthorizationTracing(req *http.Request)
  • WithDatadogTracing(ctx context.Context)
  • WithSystemTracing()
  • WithGlamplifyRequestFieldsFromCtx(ctx context.Context)

Each of these will create the correct sub-doc ("system", "tracing" etc.) and print a number of standard properties. The use of these extensions is highly encouraged.

Managing Loggers Yourself

While we recommend using the package level methods for their ease of use, you may desire to create and manage loggers yourself, which you can do by calling:

config := NewLoggerConfig()
// optionally override default properties on the config
return NewLogger(config)

Log Examples

package cagoexample

import (
	"github.com/cultureamp/ca-go/log"
)

func basic_example() {
	var ipv4 net.IP

	then := time.Now()
	u := uuid.New()
	duration := time.Since(then)

	props := SubDoc().
		Str("str", "value").
		Int("int", 1).
		Bool("bool", true).
		Duration("dur", duration).
		IPAddr("ipaddr", ipv4).
		UUID("uuid", u)

	Debug("debug_with_all_field_types").
		WithSystemTracing().
		Properties(props).
		Details("logging should contain all types")

	Debug("debug_with_all_field_types").
		WithSystemTracing().
		Properties(props).
		Detailsf("logging should contain all types: %s", "ok")

	Debug("debug_with_all_field_types").
		WithSystemTracing().
		Properties(props).
		Send()
}

func http_request_example() {
	// create a dummy request and add it to the context
	req := httptest.NewRequest(http.MethodGet, "http://example.com/foo", nil)
	req.Header.Add(log.TraceHeader, "trace_123_id")
	req.Header.Add(log.RequestHeader, "request_456_id")
	req.Header.Add(log.CorrelationHeader, "correlation_789_id")

	Debug("debug_with_request_and_system_tracing").
		WithSystemTracing().
		WithRequestTracing(req).
		Properties(log.SubDoc().
			Str("resource", "resource_id").
			Int("test-number", 1),
		).Details("logging should contain both")
}

func jwtauth_payload_example() {
	// create a jwt payload
	auth := &log.AuthPayload{
		CustomerAccountID: "account_123_id",
		RealUserID:        "real_456_id",
		UserID:            "user_789_id",
	}

	log.Info("info_with_auth_and_system_tracing").
		WithSystemTracing().
		WithAuthenticatedUserTracing(auth).
		Properties(log.SubDoc().
			Str("resource", "resource_id").
			Int("test-number", 1),
		).Details("logging should contain both")
}

Legacy Glamplify Loggers

Included are both package and NewLegacyLogger methods that support the glamplfy log.Fields{} interface. Feel free to use this when migrating an existing project off glamplify to ca-go, but these are NOT recommended for use for new projects.

Legacy Glamplify Examples

package cagoexample

import (
	"github.com/cultureamp/ca-go/log"
)

func glamplify_example() {
	ctx := context.Background()

	now := time.Now()
	f := log.Fields{
		"key1":    "string value",
		"key2":    1,
		"now":     now.Format(time.RFC3339),
		"later":   time.Now(),
		"details": "detailed message",
	}
	log.GlamplifyDebug(ctx, "log_fields", f)
	log.GlamplifyInfo(ctx, "log_fields", f)
	log.GlamplifyWarn(ctx, "log_fields", f)
	log.GlamplifyError(ctx, "log_fields", errors.New("test error"), f)

	// log.GlamplifyFatal calls os.exit() so this is hard to demonstrate!

	defer recoverFromPanic()
	log.GlamplifyPanic(ctx, "panic_error", errors.New("test error"), f)
}

func http_request_example() {
	ctx := context.Background()

	f := log.Fields{
		"key1":    "string value",
		"key2":    1,
		"now":     now.Format(time.RFC3339),
		"later":   time.Now(),
		"details": "detailed message",
	}

	// create a dummy request and add it to the context
	req := httptest.NewRequest(http.MethodGet, "http://example.com/foo", nil)
	req.Header.Add(log.TraceHeader, "trace_123_id")
	req.Header.Add(log.RequestHeader, "request_456_id")
	req.Header.Add(log.CorrelationHeader, "correlation_789_id")

	// Note: Glamplify logger automatically adds WithSystemTracing() to log.Fields{}
	log.GlamplifyDebug(ctx, "log_fields", f.WithRequestTracing(req))
}

func jwtauth_payload_example() {
	ctx := context.Background()

	f := log.Fields{
		"key1":    "string value",
		"key2":    1,
		"now":     now.Format(time.RFC3339),
		"later":   time.Now(),
		"details": "detailed message",
	}

	// create a jwt payload
	auth := &log.AuthPayload{
		CustomerAccountID: "account_123_id",
		RealUserID:        "real_456_id",
		UserID:            "user_789_id",
	}

	// Note: Glamplify logger automatically adds WithSystemTracing() to log.Fields{}
	log.GlamplifyInfo(ctx, "log_fields", f.WithAuthenticatedUserTracing(req))
}

func recoverFromLogPanic() {
	if saved := recover(); saved != nil {
		// convert to an error if it's not one already
		err, ok := saved.(error)
		if !ok {
			err = errors.New(fmt.Sprint(saved))
		}

		log.GlamplifyError("recovered_from_panic", err).Send()
	}
}

Documentation

Index

Constants

View Source
const (
	AppVerDefault       = "1.0.0"
	AwsAccountIDDefault = "development"
	AppFarmDefault      = "local"
	LogLevelDefault     = "INFO"
)
View Source
const (

	// *** Global Environment Variables ***.
	AppNameEnv       = "APP"
	AppNameLeagcyEnv = "APP_NAME"
	AppVerEnv        = "APP_VERSION"
	AppFarmEnv       = "FARM"
	AppFarmLegacyEnv = "APP_ENV"
	ProductEnv       = "PRODUCT"

	// *** AWS Environment Variables ***.
	AwsProfileEnv   = "AWS_PROFILE"
	AwsRegionEnv    = "AWS_REGION"
	AwsAccountIDEnv = "AWS_ACCOUNT_ID"

	// *** Log Environment Variables ***.
	LogLevelEnv         = "LOG_LEVEL"
	LogQuietModeEnv     = "QUIET_MODE"
	LogConsoleWriterEnv = "CONSOLE_WRITER"
	LogConsoleColourEnv = "CONSOLE_COLOUR"
)
View Source
const (
	TraceIDHeader                        = "X-amzn-Trace-ID"
	RequestIDHeader                      = "X-Request-ID"
	CorrelationIDHeader                  = "X-Correlation-ID"
	ErrorUUID                            = "00000000-0000-0000-0000-000000000000"
	AuthorizationHeader                  = "Authorization"
	XCAServiceGatewayAuthorizationHeader = "X-CA-SGW-Authorization"
	UserAgentHeader                      = "User-Agent"
	XForwardedForHeader                  = "X-Forwarded-For"
)

Variables

This section is empty.

Functions

func AddRequestFields deprecated added in v0.0.34

func AddRequestFields(ctx context.Context, rsFields RequestScopedFields) context.Context

Deprecated: AddRequestFields adds a RequestScopedFields to the context. Please migrate to use the WithRequestTracing() and WithAuthenticatedUserTracing() extension methods.

func NewLogger added in v0.0.13

func NewLogger(config *Config) *standardLogger

NewLogger creates a new standardLogger using the supplied config.

Types

type AuthPayload

type AuthPayload struct {
	// CustomerAccountID is the ID of the currently logged in user's parent
	// account/organization, sometimes known as the "account_aggregate_id".
	CustomerAccountID string
	// UserID is the ID of the currently authenticated user, and will
	// generally be a "user_aggregate_id".
	UserID string
	// RealUserID, when supplied, is the ID of the user who is currently
	// impersonating the current "UserID". This value is optional.
	RealUserID string
}

AuthPayload contains the customer account_id, user_id and realuser_id uuids.

type Config added in v0.0.13

type Config struct {
	// Mandatory fields listed in https://cultureamp.atlassian.net/wiki/spaces/TV/pages/3114598406/Logging+Standard
	AppName    string // The name of the application the log was generated from
	AppVersion string // The version of the application

	AwsRegion    string // the AWS region this code is running in
	AwsAccountID string // the AWS account ID this code is running in
	Product      string // performance, engagmentment, etc.
	Farm         string // The name of the farm or where the code is running

	LogLevel      string // The logging level
	Quiet         bool   // Are we running inside tests and we should be quiet?
	ConsoleWriter bool   // If Farm=local use key-value colour console logging
	ConsoleColour bool   // If ConsoleWriter=true then enable/disable colour

	TimeNow timeNowFunc // Defaults to "time.Now" but useful to set in tests
}

Config contains logging configuration values.

func NewLoggerConfig added in v0.0.13

func NewLoggerConfig() *Config

NewLoggerConfig creates a new configuration based on environment variables which can easily be reset before passing to NewLogger().

func (*Config) Level added in v1.0.12

func (c *Config) Level() zerolog.Level

func (*Config) ToLevel added in v1.0.12

func (c *Config) ToLevel(logLevel string) zerolog.Level

type EventCtxKey deprecated added in v0.0.34

type EventCtxKey int

Deprecated: EventCtxKey type.

const (
	// Deprecated: RequestFieldsCtx EventCtxKey = iota.
	RequestFieldsCtx EventCtxKey = iota
)

type Field added in v0.0.37

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

Field contains an element of the log, usually a key-value pair.

func Add added in v0.0.40

func Add() *Field

Add creates a new custom log properties list.

func (*Field) Bool added in v0.0.37

func (lf *Field) Bool(key string, b bool) *Field

Bool adds the property key with b as an bool to the log.

func (*Field) Bytes added in v1.0.12

func (lf *Field) Bytes(key string, val []byte) *Field

Bytes adds the property key with val as an []byte to the log.

func (*Field) Duration added in v0.0.37

func (lf *Field) Duration(key string, d time.Duration) *Field

Duration adds the property key with val as an time.Duration to the log.

func (*Field) Float32 added in v1.0.12

func (lf *Field) Float32(key string, val float32) *Field

Float32 adds the property key with val as an float32 to the log.

func (*Field) Float64 added in v1.0.12

func (lf *Field) Float64(key string, val float64) *Field

Float64 adds the property key with val as an float64 to the log.

func (*Field) Func added in v1.0.7

func (lf *Field) Func(f func(e *zerolog.Event)) *Field

Func allows an anonymous func to run for the accumulated event.

func (*Field) IPAddr added in v0.0.37

func (lf *Field) IPAddr(key string, ip net.IP) *Field

IPAddr adds the property key with val as an net.IP to the log.

func (*Field) Int added in v0.0.37

func (lf *Field) Int(key string, val int) *Field

Int adds the property key with val as an int to the log.

func (*Field) Int64 added in v1.0.7

func (lf *Field) Int64(key string, val int64) *Field

Int64 adds the property key with val as an int64 to the log.

func (*Field) Str added in v0.0.37

func (lf *Field) Str(key string, val string) *Field

Str adds the property key with val as a string to the log. Note: Empty string values will not be logged.

func (*Field) Time added in v0.0.37

func (lf *Field) Time(key string, t time.Time) *Field

Time adds the property key with val as an uuid.UUID to the log.

func (*Field) UInt added in v1.0.12

func (lf *Field) UInt(key string, val uint) *Field

UInt adds the property key with val as an int to the log.

func (*Field) UInt64 added in v1.0.7

func (lf *Field) UInt64(key string, val uint64) *Field

UInt64 adds the property key with val as an uint64 to the log.

func (*Field) UUID added in v0.0.37

func (lf *Field) UUID(key string, uuid uuid.UUID) *Field

UUID adds the property key with val as an uuid.UUID to the log.

type Logger

type Logger interface {
	Debug(event string) *Property
	Info(event string) *Property
	Warn(event string) *Property
	Error(event string, err error) *Property
	Fatal(event string, err error) *Property
	Panic(event string, err error) *Property
}

Logger interface used for mock testing.

var DefaultLogger Logger = nil

DefaultLogger is the package level default implementation used by all package level methods. Package level methods are provided for ease of use. For testing you can replace the DefaultLogger with your own mock:

DefaultLogger = newmockLogger().

type Property added in v0.0.13

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

Property contains an element of the log, usually a key-value pair.

func Debug

func Debug(event string) *Property

Debug starts a new message with debug level.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func Error

func Error(event string, err error) *Property

Error starts a new message with error level.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func Fatal

func Fatal(event string, err error) *Property

Fatal starts a new message with fatal level. The os.Exit(1) function is called by the Msg method, which terminates the program immediately.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func Info

func Info(event string) *Property

Info starts a new message with info level.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func Panic

func Panic(event string, err error) *Property

Panic starts a new message with panic level. The panic() function is called by the Msg method, which stops the ordinary flow of a goroutine.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func Warn

func Warn(event string) *Property

Warn starts a new message with warn level.

You must call Details, Detailsf or Send on the returned event in order to send the event to the output.

func (*Property) Details added in v0.0.13

func (lf *Property) Details(details string) error

Details adds the property 'details' with the val as a string to the log. This is a terminating Property that signals that the log statement is complete and can now be sent to the output. It returns nil on success, or an error if there was a problem.

NOTICE: once this method is called, the *Property should be disposed. Calling Details twice can have unexpected results.

func (*Property) Detailsf added in v0.0.13

func (lf *Property) Detailsf(format string, v ...interface{}) error

Detailsf adds the property 'details' with the format and args to the log. This is a terminating Property that signals that the log statement is complete and can now be sent to the output.It returns nil on success, or an error if there was a problem.

NOTICE: once this method is called, the *Property should be disposed. Calling Detailsf twice can have unexpected results.

func (*Property) Properties added in v0.0.13

func (lf *Property) Properties(fields *Field) *Property

Properties adds an entire sub-document of type Property to the log.

func (*Property) Send added in v0.0.13

func (lf *Property) Send() error

Send terminates the log and signals that it is now complete and can be sent to the output. It returns nil on success, or an error if there was a problem.

NOTICE: once this method is called, the *Property should be disposed. Calling Send twice can have unexpected results.

func (*Property) WithAuthenticatedUserTracing added in v0.0.13

func (lf *Property) WithAuthenticatedUserTracing(auth *AuthPayload) *Property

WithAuthenticatedUserTracing adds an "authentication" subdocument to the log that includes important account, user and realuser fields.

func (*Property) WithAuthorizationTracing added in v0.0.29

func (lf *Property) WithAuthorizationTracing(req *http.Request) *Property

WithAuthorizationTracing adds an "authorization" subdocument to the log that includes important authorization headers that are automatically redacted.

func (*Property) WithDatadogTracing added in v1.0.7

func (lf *Property) WithDatadogTracing(ctx context.Context) *Property

WithDatadogTracing adds a "datadog" subdocument to the log that includes the fields dd.trace_id and dd.span_id. If Xray is configured it also adds xray.trace_id and xray.seg_id fields.

func (*Property) WithGlamplifyRequestFieldsFromCtx deprecated added in v0.0.34

func (lf *Property) WithGlamplifyRequestFieldsFromCtx(ctx context.Context) *Property

Deprecated: WithGlamplifyRequestFieldsFromCtx emits a new log message with panic level. Please migrate to use the WithRequestTracing() and WithAuthenticatedUserTracing() extension methods.

func (*Property) WithRequestDiagnostics added in v0.0.40

func (lf *Property) WithRequestDiagnostics(req *http.Request) *Property

WithRequestDiagnostics adds a "request" subdocument to the log that includes important request fields.

func (*Property) WithRequestTracing added in v0.0.13

func (lf *Property) WithRequestTracing(req *http.Request) *Property

WithRequestTracing adds a "tracing" subdocument to the log that includes important trace, request and correlation fields.

func (*Property) WithSystemTracing added in v0.0.13

func (lf *Property) WithSystemTracing() *Property

WithSystemTracing adds a "system" subdocument to the log that includes important host, runtime, cpu and loc fields.

type RequestScopedFields deprecated added in v0.0.34

type RequestScopedFields struct {
	TraceID             string `json:"trace_id"`       // AWS XRAY trace id. Format of this is controlled by AWS. Do not rely on it, some services may not use XRAY.
	RequestID           string `json:"request_id"`     // Client generated RANDOM string. Most of the time this will be empty. Clients can set this to help us diagnose issues.
	CorrelationID       string `json:"correlation_id"` // Set ALWAYS by the web-gateway as a UUID v4.
	UserAggregateID     string `json:"user"`           // If JWT and correct key present, then this will be set to the Effective User UUID
	CustomerAggregateID string `json:"customer"`       // If JWT and correct key present, then this will be set to the Customer UUID (aka Account)
}

Deprecated: RequestScopedFields instead use the WithRequestTracing() and WithAuthenticatedUserTracing() extension methods.

func GetRequestScopedFields deprecated added in v0.0.34

func GetRequestScopedFields(ctx context.Context) (RequestScopedFields, bool)

Deprecated: GetRequestScopedFields gets the RequestScopedFields from the context. Please migrate to use the WithRequestTracing() and WithAuthenticatedUserTracing() extension methods.

Jump to

Keyboard shortcuts

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