Categorygithub.com/StevenACoffman/logrus-stackdriver-formatter

# README

logrus-stackdriver-formatter

Go Report Card GoDoc License MIT

Logrus-stackdriver-formatter provides:

In addition to supporting level-based logging to Stackdriver, for Error, Fatal and Panic levels it will append error context for Error Reporting.

Installation

go get -u github.com/TV4/logrus-stackdriver-formatter

Logrus Usage

package main

import (

stackdriver "github.com/StevenACoffman/logrus-stackdriver-formatter"
"github.com/sirupsen/logrus"
)

var log = logrus.New()

func init() {
    log.Formatter = stackdriver.NewFormatter(
        stackdriver.WithService("your-service"), 
        stackdriver.WithVersion("v0.1.0"),
    )
    log.Level = logrus.DebugLevel

    log.Info("ready to log!")
}

Here's a sample entry (prettified) from the example:

{
  "serviceContext": {
    "service": "test-service",
    "version": "v0.1.0"
  },
  "message": "unable to parse integer: strconv.ParseInt: parsing \"text\": invalid syntax",
  "severity": "ERROR",
  "context": {
    "reportLocation": {
      "file": "github.com/StevenACoffman/logrus-stackdriver-formatter/example_test.go",
      "line": 21,
      "function": "ExampleLogError"
    }
  }
}

HTTP request context

If you'd like to add additional context like the httpRequest, here's a convenience function for creating a HTTP logger:

func httpLogger(logger *logrus.Logger, r *http.Request) *logrus.Entry {
    return logger.WithFields(logrus.Fields{
        "httpRequest": map[string]interface{}{
            "method":    r.Method,
            "url":       r.URL.String(),
            "userAgent": r.Header.Get("User-Agent"),
            "referrer":  r.Header.Get("Referer"),
        },
    })
}

Then, in your HTTP handler, create a new context logger and all your log entries will have the HTTP request context appended to them:

func handler(w http.ResponseWriter, r *http.Request) {
    httplog := httpLogger(log, r)
    // ...
    httplog.Infof("Logging with HTTP request context")
}

Go-kit Log Adapter

Go-kit log is wrapped to encode conventions, enforce type-safety, provide leveled logging, and so on. It can be used for both typical application log events, and log-structured data streams.

Typical application logging

import (
	"os"
	logadapter "github.com/StevenACoffman/logrus-stackdriver-formatter"
	kitlog "github.com/go-kit/kit/log"
)

func main() {
	w := kitlog.NewSyncWriter(os.Stderr)
	logger := logadapter.InitLogrusGoKitLogger(w)
	logger.Log("question", "what is the meaning of life?", "answer", 42)

	// Output:
	// question="what is the meaning of life?" answer=42
}

Contextual Loggers

import (
	"os"
	logadapter "github.com/StevenACoffman/logrus-stackdriver-formatter"
	kitlog "github.com/go-kit/kit/log"
)

func main() {
	logger := logadapter.InitLogrusGoKitLogger(kitlog.NewSyncWriter(os.Stderr))
	logger = kitlog.With(logger, "instance_id", 123)

	logger.Log("msg", "starting")
	NewWorker(kitlog.With(logger, "component", "worker")).Run()
	NewSlacker(kitlog.With(logger, "component", "slacker")).Run()
}

// Output:
// instance_id=123 msg=starting
// instance_id=123 component=worker msg=running
// instance_id=123 component=slacker msg=running

Enhancements

go-kit's package log is centered on the one-method Logger interface.

type Logger interface {
	Log(keyvals ...interface{}) error
}

This interface, and its supporting code like is the product of much iteration and evaluation. For more details on the evolution of the Logger interface, see The Hunt for a Logger Interface, a talk by Chris Hines. Also, please see #63, #76, #131, #157, #164, and #252 to review historical conversations about package log and the Logger interface.

Value-add packages and suggestions, like improvements to the leveled logger, are of course welcome. Good proposals should

Alternatives

  • Both go-kit log and logr both provide to developers clean, minimal surface area for sending log messages. However, neither has direct, first-class stackdriver support.
  • Uber's Zap is very performant, but it's SugaredLogger API is unfamiliar to me.

So one choice could be logr -> zapr -> zap -> zap-stackdriver or zapdriver but zapr still uses dep, didn't have go module support.

I could also have gone with go-kit log -> zap -> zap-stackdriver or zapdriver.

Instead, I pulled a bunch of people's forks together into this piece of mad science because it seemed like it would work. ¯\_(ツ)_/¯

# Packages

No description provided by the author
No description provided by the author

# Functions

InitLogging initializes a logrus logger to send things to stackdriver.
InitLogrusGoKitLogger initializes a go kit logger to send things to stackdriver.
LoggingMiddleware is a middleware for writing request logs in a structured format to stackdriver.
NewFormatter returns a new Formatter.
NewLogrusGoKitLogger creates a gokit-compatible logger.
WithProjectID makes sure all entries have your Project information.
WithRegexSkip lets you configure which functions or packages should be skipped for locating the error.
WithService lets you configure the service name used for error reporting.
WithSkipTimestamp lets you avoid setting the timestamp.
WithStackSkip lets you configure which packages should be skipped for locating the error.
WithVersion lets you configure the service version used for error reporting.

# Structs

Context is sent with every message to stackdriver.
Entry
Entry stores a log entry.
Formatter implements Stackdriver formatting for logrus.
HTTPRequest defines details of a request and response to append to a log.
LogrusGoKitLogger is a gokit-compatible wrapper for logrus.LogrusGoKitLogger.
ReportLocation is the information about where an error occurred.
ServiceContext provides the data about the service we are sending to Google.

# Type aliases

Option lets you configure the Formatter.