Skip to content
/ jettison Public

Jettison provides structured logging and errors over gRPC

License

Notifications You must be signed in to change notification settings

luno/jettison

Repository files navigation

Jettison

Go Report Card Go Doc

What is it?

Jettison is a library providing structured logs and errors in a way that's interoperable with gRPC. It does this under the hood by serialising message details to protobuf messages and attaching them to gRPC Status objects - see the gRPC docs for details of how this works. Jettison is also compatible with the Go 2 error spec, which can be found in the draft design page.

Features

Jettison is in alpha, but the following features are planned:

  • [✓] Simple, gRPC-compatible utilities for building up structured errors/logs.
  • [✓] Support for error identification and unwrapping as per the Go 2 spec.
  • [✓] Structured error/log formatting utilities for both machines (JSON) and humans.
  • [✕] Key/value pair type support for compatibility with Elasticsearch.

API

Errors

The jettison/errors package provides functions for creating and working with gRPC-compatible error values. You can attach arbitrary metadata to jettison errors, decorate them with source/stacktrace information and wrap errors to form a chain as the stack unwinds. Passing jettison errors over gRPC preserves all metadata structure, in contrast to other error types (which get marshalled to a string by default).

Jettison also provides gRPC middleware that automatically groups the errors in a chain by the gRPC server (or "hop") they originated from.

See the jettison/example package for a more complete usage example, including a gRPC server/client passing jettison errors over the wire.

import (
    "github.com/luno/jettison"
    "github.com/luno/jettison/errors"
)

func ExampleNew() {
	// Construct your error as usual, with additional metadata.
	err := errors.New("something went wrong", j.KV("key", "value"))

	// Wrap errors with additional metadata as they get passed down the stack.
	err = errors.Wrap(err, "something else went wrong", j.KV("another_key", "another_value"))

	// Pass it around - including over gRPC - like you would any other error.
}

Logs

The jettison/log package provides structured JSON logging, with additional utilities for logging jettison errors. You can attach metadata to logs in the same manner as you attach metadata to errors.

import (
    "context"

    "github.com/luno/jettison"
    "github.com/luno/jettison/errors"
    "github.com/luno/jettison/log"
)

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

	// You can log general info as you normally would.
	log.Info(ctx, "entering the example function", j.KV("key", "value"))
}

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

	err := errors.New("a jettison error", j.KV("key", "value"))

	// Errors can be logged separately, with metadata marshalled to JSON in
	// a machine-friendly manner.
	log.Error(ctx, err, j.KV("another_key", "another_value"))
}

An example log written via log.Info:

{
  "message": "entering the example function",
  "source": "jettison/example/example.go:9",
  "level": "info",
  "parameters": [
    {
      "key": "key",
      "value": "value"
    }
  ]
}

An example log written via log.Error:

{
  "message": "a jettison error",
  "source": "jettison/example/example.go:18",
  "level": "error",
  "hops": [
    {
      "binary": "example",
      "errors": [
        {
          "message": "a jettison error",
          "source": "jettison/example/example.go:14",
          "parameters": [
            {
              "key": "key",
              "value": "value"
            }
          ]
        }
      ]
    }
  ],
  "parameters": [
    {
      "key": "key",
      "value": "value"
    },
    {
      "key": "another_key",
      "value": "another_value"
    }
  ]
}

Utilities

The jettison/j package contains aliases for common jettison options, saving you a couple of keystrokes:

import (
    "github.com/luno/jettison/errors"
    "github.com/luno/jettison/j"
)

func ExampleKS() {
	err := errors.New("using j.KS",
		j.KS("string_key", "value"))

	fmt.Printf("%%+v: %+v\n", err)
	fmt.Printf("%%#v: %#v\n", err)
}

func ExampleKV() {
	err := errors.New("using j.KV",
		j.KV("int_key", 1))

	fmt.Printf("%%+v: %+v\n", err)
	fmt.Printf("%%#v: %#v\n", err)
}

About

Jettison provides structured logging and errors over gRPC

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages