package
0.332.0
Repository: https://github.com/anz-bank/sysl-go.git
Documentation: pkg.go.dev

# README

Logging

Overview

Sysl-go comes equipped with flexible, out-of-the-box logging support. The following sections describe what gets logged and how the logger can be configured and utilised within custom code.

Logged Events

Sysl-go logs the following major categories and fields:

Error Events

Sysl-go logs all errors encountered within the running on an application. Examples include timeout errors, marshalling errors and errors encountered in custom code.

Info Events

Sysl-go logs all information for the purpose of understanding the state of the application and its requests. Examples include the server configuration on startup and the state of upstream and downstream requests.

Debug Events

Sysl-go logs verbose information for the purpose of debugging. Examples include JWT authorisation failures and request and response payloads (see External Configuration below).

Event Fields

Sysl-go includes attaches additional field values to some of its logs. The following notable fields are attached:

FieldDetails
traceidHTTP: Identifier retrieved from RequestID header or uniqely generated for the purpose of tracing a request.
remoteHTTP: The remote address of a downstream request.
latencyThe time required to fulfil a request.

Errors

Sysl-go logs all errors encountered within the running on an application.

Usage

Sysl-go utilises the context.Context to store the logger for use throughout the application. Anywhere that a context can be found, a log can be sent to the centralised logger:

import ( "github.com/anz-bank/sysl-go/log" )

log.Info(ctx, "Hello world")

In some instances it is desirable to attach additional information to all logs that utilise a given context:

ctx = log.WithStr(ctx, "server_name", "great_pineapple")
log.Info(ctx, "Server started") // Log includes server name
...
ctx = log.WithInt(ctx, "request_id", request.id)
log.Info(ctx, "Request received") // Log includes both server name and request id

Framework

Sysl-go respects that different teams want to use different logging solutions and that Sysl-go shouldn't prevent you from doing as such. In order to achieve this, the logging framework within Sysl-go is designed around the log.Logger interface that acts as a wrapper around concrete logging implementations.

Out-of-the-box Sysl-go supports the following logging implementations:

By default, the Pkg logger is used within Sysl-go. To use a different logger or to configure the logger beyond the log level, see Custom Configuration below.

External Configuration

Sysl-go applications are configured through an external configuration file. The logger can be configured to record logs at or above a particular level:

library:
  log:
    level: debug # one of error, info, debug

Another configurable value provides the ability to log the contents of requests and responses:

library:
  log:
    logPayload: true # include payload contents in log messages

Custom Configuration

By default, the Pkg logger is used within Sysl-go. To use a different logger or to configure the logger beyond the log level, add a custom Logger hook to the Hooks structure that builds and returns a customised logger.

The following example demonstrates the use of a customised Logrus logger:

func newAppServer(ctx context.Context) (core.StoppableServer, error) {
	return example.NewServer(ctx,
		func(ctx context.Context, config AppConfig) (*example.ServiceInterface, *core.Hooks, error) {
			return &gateway.ServiceInterface{
					...
				}, &core.Hooks{
				    Logger: func() log.Logger {
                        logger := logrus.New()
                        logger.Formatter = &logrus.JSONFormatter{}
                        return anzlog.NewLogrusLogger(logger)
                    }
                }, nil
		},
	)
}

The returned logger will have the log level applied from the configuration file by Sysl-go internally.

Native Support

Some logging implementations provide their own mechanism to interact with the context.Context. For example, the Pkg logger can be used directly:

import ( pkglog "github.com/anz-bank/pkg/log" )
ctx = pkglog.With("key", "value").Onto(ctx)
pkglog.Info(ctx, "Hello world")

The Sysl-go logging framework understands this and so the implementation works seamlessly whether you access the logger natively or through the wrapper:

import ( 
    pkglog "github.com/anz-bank/pkg/log" 
    "github.com/anz-bank/sysl-go/log" 
)
ctx = pkglog.With("key", "value").Onto(ctx) // Put a key/value into the context
pkglog.Info(ctx, "Native") // Native call, includes key/value pair
log.Info(ctx, "Wrapped") // Wrapped call, also includes key/value pair

Legacy Support

Sysl-go has gone through two iterations of logging. The first iteration enforced the use of Logrus. The second iteration replaced Logrus with the Pkg logger, providing a hook mechanism to route logs back through Logrus for backwards compatibility.

With the introduction of the new logging framework, backwards compatible support of both Logrus and the Pkg logger is provided seamlessly, however upgrading to the approach described in the Custom Configuration is recommended:

Logrus

import ( 
    "github.com/sirupsen/logrus" 
    "github.com/anz-bank/sysl-go/log" 
)
ctx := common.LoggerToContext(ctx, logrus.New(), nil) // Put the logger in the context (the legacy approach)
example.NewServer(ctx, ...) // Initialise the server
...
logger := common.GetLoggerFromContext(ctx) // Retrieve the logger from the context
logger.Info(ctx, "Native") // Log natively
log.Info(ctx, "Wrapped") // Log using the wrapper (uses the same Logrus instance)

Pkg logger

import ( 
    pkglog "github.com/anz-bank/pkg/log" 
    "github.com/anz-bank/sysl-go/log" 
)
ctx = pkglog.With("key", "value").Onto(ctx) // Initialise the pkg logger (the legacy approach)
example.NewServer(ctx, ...) // Initialise the server
...
pkglog.Info(ctx, "Native")  // Log natively
log.Info(ctx, "Wrapped")  // Log using the wrapper (uses the same pkg values)

# Functions

Debug logs the given message against the context found in the logger.
Debugf logs the given message against the context found in the logger.
Error logs the given error and message against the context found in the logger.
Errorf logs the given error and message against the context found in the logger.
GetLogger returns the logger from the context, or nil if no logger can be found.
Deprecated: Use log.GetLogger.
Deprecated: Use log.GetLogger.
Info logs the given message against the context found in the logger.
Infof logs the given message against the context found in the logger.
Deprecated: Use GetLogger, Error, Info or Debug methods.
NewDefaultLogger returns a logger that is regarded as the default logger to use within an application when no logger configuration is provided.
NewLogrusLogger returns an implementation of Logger that uses the Logrus logger.
NewPkgLogger returns is an implementation of Logger that uses the pkg/log logger.
NewZeroPkgLogger returns is an implementation of Logger that uses the pkg/logging logger.
PutLogger puts the given logger in the context.
WithDuration returns the given context with a logger that persists the given key/value.
WithInt returns the given context with a logger that persists the given key/value.
WithLevel returns the given context with a logger that logs at the given level.
WithStr returns the given context with a logger that persists the given key/value.

# Constants

# Interfaces

Logger is a component used to perform logging.

# Type aliases

Level represents the level at which a logger will log.