contextlogger

module
v1.6.2 Latest Latest
Warning

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

Go to latest
Published: Apr 2, 2019 License: MIT

README

ContextLogger

This project provides a pluggable, context-based logging, error reporting, and performance metrics interface for golang.

The "log" package is largely compatible with Logrus, and Logrus can be used for output.

You can see the slides from Chris Williams' presentation to GoSF in April 2017, Context Patterns for Logging, here.

Included Log Providers

The following packages are provided:

  • logrus: Log output using the Logrus logger
  • rollbar: Error reporting via Rollbar
  • newrelic: Performance and custom metrics via NewRelic
  • merry: Log structured error data and tracebacks to where an error was actually generated, using Merry errors
  • reported_at: Include the file and line number responsible for each log message

Log providers are chained together in whatever combination you desire. New log providers can be easily implemented by following the simple LogProvider interface.

Logging Interface

The main interface you interact with in using ContextLogger is log.ContextLogger, which includes standard library context.Context as well as the following logging methods:

ErrorReport(    args ...interface{})
Error(          args ...interface{})
WarnReport(     args ...interface{})
Warn(           args ...interface{})
InfoReport(     args ...interface{})
Info(           args ...interface{})
DebugReport(    args ...interface{})
Debug(          args ...interface{})

The distinction between, e.g., "Error" and "ErrorReport" is up to you to define in your environment. At Helix, we use it to distinguish between "this broke, and a human needs to look at it" (ErrorReport) and "this broke, but just make a note of it, don't wake anyone up" (Error). Having does-someone-get-notified be an explicit dimension independent from severity has worked out well for managing our on-call quality of life, but YMMV; if you don't like the *Report methods, just ignore them.

Adding Log Fields

ContextLogger allows you to build up metadata on a Context as it passed through your program, all of which will be output alongside any log message produced with that Context. This data survives even when the ContextLogger is passed as a standard context.Context. The interface for adding fields is Logrus-compatible:

func A(ctx log.ContextLogger) {
    ctx = ctx.WithFields(log.Fields{
        "A": 1,
    })
    B(ctx)
}
func B(ctx log.ContextLogger) {
    C(ctx.WithField("B", 2))
}
// Accepts standard context.Context
func C(ctx context.Context) {
    // Recover log.ContextLogger from context.Context
    // Will generate default ContextLogger if ctx never had one before
    ctxLog := log.FromContext(ctx)
    ctxLog.Info("Made it to C")
    // Output: msg="Made it to C" A=1 B=2
}

Metrics

ContextLogger also provides two methods for logging metrics:

Record(metrics Metrics)
RecordEvent(eventName string, metrics Metrics)

Metrics is just another name for map[string]interface{}, same as log.Fields; and you might wonder what the difference between a metric with an event name and a log field with a log message is -- similar to ErrorReport vs. Error, this is really to provide a way to selectively send information to a different destination. The NewRelic log provider will take data from Record and add it to a newrelic.Transaction in the Context, and will put data from RecordEvent into a NewRelic Custom Event. But you could easily write a provider to send these anywhere you want to track some sort of metrics.

Setting up the Default Provider

Here's an example config which chains together all the built-in providers (except for dummy, which is just for startup and testing):

// This assumes there's a "config" struct in this package where certain project config data is coming from.

import (
	"github.com/myhelix/contextlogger/log"
	cl_logrus "github.com/myhelix/contextlogger/providers/logrus"
	cl_merry "github.com/myhelix/contextlogger/providers/merry"
	cl_newrelic "github.com/myhelix/contextlogger/providers/newrelic"
	"github.com/myhelix/contextlogger/providers/reported_at"
	cl_rollbar "github.com/myhelix/contextlogger/providers/rollbar"
	"github.com/myhelix/rollbar"
)

func configureLogging() error {
	// Keep track for reporting at the end
	var rollbarEnabled, newRelicEnabled bool

	if config.LogLevel == "" {
		config.LogLevel = "info"
	}

	// Logrus provides the base log output
	logProvider, err := cl_logrus.LogProvider(nil, cl_logrus.Config{
		Output:    os.Stderr,
		Level:     config.LogLevel,
		Formatter: cl_logrus.RecommendedFormatter,
	})
	if err != nil {
		return err
	}

	// Rollbar error reporting
	if config.RollbarToken != "" {
		codeRevBytes, err := exec.Command("git", "rev-parse", "HEAD").Output()
		if err != nil {
			return err
		}
		codeRev := strings.Trim(string(codeRevBytes), " \n")

		rollbar.Token = config.RollbarToken
		rollbar.Environment = config.Env
		rollbar.CodeVersion = codeRev  // Git hash/branch/tag (required for GitHub integration)
		rollbar.ServerRoot = config.Package // path of project (required for GitHub integration and non-project stacktrace collapsing)
		rollbar.FilterFields = regexp.MustCompile("(?i)password|secret|token|auth")

		// Rollbar config is all at package level, so no config to pass in here
		logProvider, err = cl_rollbar.LogProvider(logProvider)
		if err != nil {
			return err
		}
		rollbarEnabled = true
	}

	// NewRelicApp is a newrelic.Application
	if config.NewRelicApp != nil {
		logProvider, err = cl_newrelic.LogProvider(logProvider, config.NewRelicApp)
		if err != nil {
			return err
		}
		newRelicEnabled = true
	}

	// Parse merry.Error values into log fields; log merry tracebacks with Error/ErrorReport
	logProvider, err = cl_merry.LogProvider(logProvider)
	if err != nil {
		return err
	}

	// Note the file and line number where each log message was reported from
	logProvider, err = reported_at.LogProvider(logProvider, reported_at.RecommendedConfig)
	if err != nil {
		return err
	}

	log.SetDefaultProvider(logProvider)

	log.WithFields(log.Fields{
		"level":           config.LogLevel,
		"rollbarEnabled":  rollbarEnabled,
		"newRelicEnabled": newRelicEnabled,
	}).Info("Configured logging")

	return nil
}

Directories

Path Synopsis
This package provides the generic logging interface; it is broadly compatible with logrus, which is itself generally compatible with the standard library logger.
This package provides the generic logging interface; it is broadly compatible with logrus, which is itself generally compatible with the standard library logger.
chaining
This package assists with chaining together providers by providing default implementations for LogProvider interface methods that don't panic if we're the last in the chain.
This package assists with chaining together providers by providing default implementations for LogProvider interface methods that don't panic if we're the last in the chain.
dummy
This package provides a basic dumb logger to get bootstrapped with
This package provides a basic dumb logger to get bootstrapped with
logrus
This package provides a concrete implementation of LogProvider using Logrus
This package provides a concrete implementation of LogProvider using Logrus
merry
This extracts merry Values into logger Fields, then passes along to the base logger
This extracts merry Values into logger Fields, then passes along to the base logger
newrelic
This package provides newrelic metric/request reporting via ContextLogger
This package provides newrelic metric/request reporting via ContextLogger
reported_at
This adds a field to log reports showing where the logger was called from
This adds a field to log reports showing where the logger was called from
rollbar
This logger reports errors to Rollbar if they came in via {Error,Warn,Info}Report, then passes through for logging by the base logger, if any.
This logger reports errors to Rollbar if they came in via {Error,Warn,Info}Report, then passes through for logging by the base logger, if any.

Jump to

Keyboard shortcuts

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