Categorygithub.com/Mattel/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

Package ctxlogrus wraps the go-grpc-middleware ctxlogrus, extracts a trace context to correlate logs emitted to the correct trace and span.
No description provided by the author
No description provided by the author

# Functions

DefaultErrorHandler does nothing.
DefaultFilterHTTP filters health checks and monitoring canaries from some well known user agents or URL paths.
DefaultFilterRPC filters gRPC standard health check and gRPC reflection requests.
InitLogging initializes a logrus logger to send things to stackdriver.
InitLogrusGoKitLogger initializes a go kit logger to send things to stackdriver.
LoggingMiddleware proivdes a request-scoped log entry into context for HTTP requests, writes request logs in a structured format to stackdriver.
NewFormatter returns a new Formatter.
NewLogrusGoKitLogger creates a gokit-compatible logger.
RecoveryMiddleware recovers from panics in the HTTP handler chain, logging an error for Error Reporting.
StreamLoggingInterceptor provides a request-scoped log entry into context for Streaming gRPC requests, and logs request details at the end of the stream.
StreamRecoveryInterceptor is an interceptor that recovers panics from Streaming services and turns them into nicer gRPC errors.
UnaryLoggingInterceptor provides a request-scoped log entry into context for Unary gRPC requests, and logs request details on the response.
UnaryRecoveryInterceptor is an interceptor that recovers panics and turns them into nicer GRPC errors.
No description provided by the author
WithGlobalTraceID sets a consistent trace id on the global logger context If not provided, a random id will be generated at runtime.
WithHTTPFilter provides a filter to the logging middleware that determines whether or not to log individual messages.
WithLogger initializes the log entry in context.
WithPrettyPrint pretty-prints logs.
WithProjectID makes sure all entries have your Project information.
WithRegexSkip lets you configure which functions or packages should be skipped for locating the error.
WithRPCFilter provides a filter to the logging middleware that determines whether or not to log individual messages.
WithService lets you configure the service name used for error reporting.
WithSkipTimestamp lets you avoid setting the timestamp.
WithSourceReference adds reference to the source code.
WithStackSkip lets you configure which packages should be skipped for locating the error.
WithStackTraceStyle configures where to write the stacktrace: appended to the message, as its own field, or both.
WithVersion lets you configure the service version used for error reporting.

# Constants

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

# Structs

Context is sent with every message to stackdriver.
Entry
Entry stores a log entry.
Formatter implements Stackdriver formatting for logrus.
GRPCRequest represents details of a gRPC request and response appended to a log.
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.
SourceLocation is the information about where a log entry was produced.
SourceReference is a reference to a particular snapshot of the source tree used to build and deploy an application.
No description provided by the author

# Type aliases

Logging filters.
Logging filters.
Logging filters.
No description provided by the author
Option lets you configure the Formatter.
No description provided by the author