Giter Club home page Giter Club logo

go-lane's Introduction

go-lane

A "lane" is a context that has logging associated. It is a melding of Go's log and its context.

Basic Use

import (
    "context"
    "github.com/jimsnab/go-lane"
)

func myFunc() {
    l := lane.NewLogLane(context.Background())

    l.Info("log something")
}

At the root, a lane needs a context, and that is typically nil or context.Background(). From there, instead of passing a context instance as the first parameter, pass the lane l.

func someFunc(l lane.Lane) {
     // use l like a context instance, or call one of its interface members
}

Interface

Lane interface {
	context.Context
	LaneId() string
	SetJourneyId(id string)
	SetLogLevel(newLevel LaneLogLevel) (priorLevel LaneLogLevel)

	Trace(args ...any)
	Tracef(format string, args ...any)
	TraceObject(message string, obj any)

	Debug(args ...any)
	Debugf(format string, args ...any)
	DebugObject(message string, obj any)

	Info(args ...any)
	Infof(format string, args ...any)
	InfoObject(message string, obj any)

	Warn(args ...any)
	Warnf(format string, args ...any)
	WarnObject(message string, obj any)

	Error(args ...any)
	Errorf(format string, args ...any)
	ErrorObject(message string, obj any)

	PreFatal(args ...any)
	PreFatalf(format string, args ...any)
	PreFatalObject(message string, obj any)

	Fatal(args ...any)
	Fatalf(format string, args ...any)
	FatalObject(message string, obj any)

	Logger() *log.Logger
	Close()

	Derive() Lane

	DeriveWithCancel() (Lane, context.CancelFunc)
	DeriveWithCancelCause() (Lane, context.CancelCauseFunc)
	DeriveWithoutCancel() (Lane, context.CancelFunc)

	DeriveWithDeadline(deadline time.Time) (Lane, context.CancelFunc)
	DeriveWithDeadlineCause(deadline time.Time, cause error) (Lane, context.CancelFunc)

	DeriveWithTimeout(duration time.Duration) (Lane, context.CancelFunc)
	DeriveWithTimeoutCause(duration time.Duration, cause error) (Lane, context.CancelFunc)
	
	DeriveReplaceContext(ctx OptionalContext) Lane

	EnableStackTrace(level LaneLogLevel, enable bool) (wasEnabled bool)

	AddTee(l Lane)
	RemoveTee(l Lane)

	SetPanicHandler(handler Panic)

	Parent() Lane
}

For the most part, application code will use the logging functions (Trace, Debug, ...).

Each log level provides:

  • a Sprint version (ex: Info or Error)
  • a Sprintf version (ex: Infof or Errorf)
  • an object logger (ex: InfoObject or ErrorObject)

The object logger will convert an object into JSON, including private fields.

A correlation ID is provided via LaneId(), which is automatically inserted into the logged message.

When spawining go routines, pass l around, or use one of the Derive functions when a new correlation ID is needed.

Optionally, an "outer ID" can be assigned with SetJourneyId(). This function is useful to correlate a transaction that involves many lanes, or to correlate with an externally generated ID. The journey id is inherited by derived lanes.

For example, a front end might generate a journey ID, passing it with its REST request to a go server that logs its activity via lanes. By setting the journey ID to what the front end has generated, the lanes will be correlated with front end logging.

Another lane can "tee" from a source lane. For example, it might be desired to tee a testing lane from a logging lane, and then a unit test can verify certain log messages occur during the test.

Two utility functions are available:

  • lane.LogObject provides access to the common implementation of InfoObject, InfoError, etc.
  • lane.CaptureObject exposes the function that turns an object into one that can be used with json.Marshal without losing private data. It does not retain json type annotations however.

Types of Lanes

  • NewLogLane log messages go to the standard Go log infrastructure. Access the log instance via Logger() to set flags, add a prefix, or change output I/O.

  • NewDiskLane like a "log lane" but writes output to a file.

  • NewTestingLane captures log messages into a buffer and provides helpers for unit tests:

    • VerifyEvents(), VerifyEventText() - check for exact log messages
    • FindEvents(), FindEventText() - check logged messages for specific logging events
    • EventsToString() - stringify the logged messages for verification by the unit test

    A testing lane also has the API WantDescendantEvents() to enable (or disable) capture of derived testing lane activity. This is useful to verify a child task reaches an expected logging point.

  • NewNullLane creates a lane that does not log but still has the context functionality. Logging is similar to log.SetOutput(io.Discard) - fatal errors still terminate the app.

Check out other projects, such as go-lane-gin or go-lane-opensearch for additional lane types.

Stack Trace

Stack trace logging can be enabled on a per level basis. For example, to enable stack trace output for ERROR:

func example() {
	l := NewLogLane(context.Background())
	l.EnableStackTrace(lane.LogLevelError, true)
	l.Error("sample")   // stack trace is logged also
}

Panic Handler

Fatal messages result in a panic. The panic handler can be replaced by test code to verify a fatal condition is reached within a test.

An ordinary unrecovered panic won't allow other go routines to continue, because, obviously, the process normally terminates on a panic. A test must ensure all go routines started by the test are stopped by its replacement panic handler.

At minimum, the test's replacement panic handler must not let the panicking go routine continue execution (it should call runtime.Goexit()).

OptionalContext

lane.OptionalContext is an alias type for context.Context. It's used because linters want to enforce callers to call context.Background() or context.TODO(). Callers can certainly do that, but the linter rule is questionable -- just pass nil, what's the problem?

Parent Context

Ordinary context does not provide a way to navigate to a parent. But sometimes this is wanted, such as when implementing diagnostics.

The parent lane ID is available as a context.Value() under the name ParentLaneIdKey:

The parent lane (which is a context) is also available by calling Lane.Parent().

	lOne := lane.NewLogLane(nil)
	lTwo := lOne.Derive()
	fmt.Printf("lOne %s == lTwo's parent %v\n", lOne.LaneId(), lTwo.Value(lane.ParentLaneIdKey))
	
	lThree := lTwo.Parent()
	fmt.Printf("lOne %s == lThree %s\n", lOne.Lane(), lThree.LaneId())

go-lane's People

Contributors

jimsnab avatar jsovalles avatar palaestracoder avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.