logjam

package module
v0.0.17 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2021 License: MIT Imports: 24 Imported by: 0

README

Logjam client for Go

GoDoc Travis

This package provides integration with the Logjam monitoring tool for Go web-applications.

It buffers all log output for a request and sends the result to a configured logjam device when the request was finished.

Requirements

This package depends on github.com/pebbe/zmq4 which requires ZeroMQ version 4.0.1 or above. Make sure you have it installed on your machine.

E.g. for MacOS:

brew install zmq

How to use it

Install via go get github.com/xing/logjam-agent-go.

Initialize the client
// create a logger
logger := logjam.Logger{
	Logger: log.New(os.Stderr, "", log.StdFlags),
	LogLevel: logjam.ERROR,
}

// create an agent
agent := logjam.NewAgent(&logjam.Options{
	AppName: "MyApp",
	EnvName: "production",
	Logger: logger,
	LogLevel: logjam.INFO,
})

Note that logger and the agent have individual log levels: the one on the logger determines the log level for lines sent to the device it is attached to (os.Stderr in this example), whereas the one on the agent determines which lines are sent to the logjam endpoint.

Use the logjam middleware
r := mux.NewRouter()
r.NotFoundHandler = http.HandlerFunc(logjam.NotFoundHandler)
r.MethodNotAllowedHandler = http.HandlerFunc(logjam.MethodNotAllowedHandler)
...
server := http.Server{
	Handler: agent.NewHandler(r, logjam.MiddlewareOptions{BubblePanics: false})
	...
}

This example uses the Gorilla Mux package but it should also work with other router packages. Don't use the Gorilla syntax: r.Use(agent.NewMiddleware) because middleware added that way is only called for configured routes. So you'll not get 404s tracked in logjam.

You also need to set environment variables to point to the actual logjam broker instance:

export LOGJAM_BROKER=my-logjam-broker.host.name

Shutting down

Make sure to shut down the agent upon program termination in order to properly close the ZeroMQ socket used to send messages to logjam.

agent.Shutdown()
Adapting logjam action names

By default, the logjam middleware fabricates logjam action names from the escaped request path and request method. For example, a GET request to the endpoint "/users/123/friends" will be translated to "Users::Id::Friends#get". If you're not happy with that, you can override the action name in your request handler, as the associated logjam request is available from the request context:

func ShowFriends(w http.ResponseWriter, r *http.Request) {
	request := logjam.GetRequest(r)
	request.ChangeAction(w, "Users#friends")
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

If you're using the gorilla mux package, you can configure the desired logjam action name when declaring the route. The following examples uses a Rails inspired naming:

import ("github.com/xing/logjam-agent-go/gorilla")

gorilla.ActionName(router.Path("/users").Methods("GET").HandlerFunc(...), "Users#index")
gorilla.ActionName(router.Path("/users").Methods("POST").HandlerFunc(...), "Users#create")
gorilla.ActionName(router.Path("/users/{user_id}").Methods("GET").HandlerFunc(...), "Users#show")
gorilla.ActionName(router.Path("/users/{user_id}").Methods("PUT", "PATCH").HandlerFunc(...), "Users#update")
gorilla.ActionName(router.Path("/users/{user_id}").Methods("DELETE").HandlerFunc(...), "Users#destroy")
gorilla.ActionName(router.Path("/users/{user_id}/friends").Methods("GET").HandlerFunc(...), "Users#friends")

Make sure to have the route fully configured before calling gorilla.ActionName.

Using the agent for non web requests

The agent's middleware takes care of all the internal plumbing for web requests. If you have some other request handling mechanism, like a message processor for example, you have to manage that yourself.

You will need to create a request, then store the request in a context to be used by the logging mechanism and sending call headers to other services, then perform your business logic and then finally make sure to send the request information to logjam.

// Let's assume that the variable agent points to a logjam.Agent and logger is
// an instance of a logjam.Logger.

func myHandler(ctx context.Context) {
    // create a new request
    request := agent.NewRequest("myHandler#call")
    // create a new context containing the request, so the logjam logger can access it
    ctx := request.NewContext(ctx)
    code := int(200)
    // make sure to send the request at the end
    defer func() {
        // make we send information to logjam in case the app panics
        if r := recover(); r != nil {
            code = 500
            request.Finish(code)
            // optionally reraise the original panic:
            // panic(r)
        }
        request.Finish(code)
    }()
    ...
    // your logic belongs here
    ...
    if sucess {
        logger.Info(ctx, "200 OK")
        return
    }
    code = 500
    logger.Error(ctx, "500 Internal Server Error")
}

Obviously one could abstract this logic into a helper function, but a.t.m. we think this is best left to the application programmer, until we have an agreement on the desired API of such a helper.

Passing call headers to other logjam instrumented services

Logjam can provide caller relationship information between a collection of services, which helps in debugging and understanding your overall system. It can be considered a form of distributed tracing, restricted to HTTP based service relationships. The agent provides a helper function to simplify the task of passing the required information to the called service.

// Call some REST service passing the required logjam headers assuming the variable client
// refers to a http.Client, agent to the logjam agent and ctx refers to the http request
// context of the currently executing request handler which has been augmented by the logjam agent.

req, err := http.NewRequest("GET", "http://example.com", nil)
agent.SetCallHeaders(ctx, req)
resp, err := client.Do(req)

How to contribute?

Please fork the repository and create a pull-request for us.

Documentation

Overview

Package logjam provides a go client for sending application metrics and log lines to a logjam endpoint. See https://github.com/skaes/logjam_app.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultActionNameExtractor

func DefaultActionNameExtractor(r *http.Request) string

DefaultActionNameExtractor replaces slashes with "::" and camel cases the individual path segments.

func LegacyActionNameExtractor

func LegacyActionNameExtractor(r *http.Request) string

LegacyActionNameExtractor is an extractor used in older versions of this package. Use it if you want to keep old action names in Logjam.

func MethodNotAllowedHandler added in v0.0.15

func MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request)

MethodNotAllowedHandler is an example handler function to deal with unroutable requests. If you use gorilla/mux, you can install it on your router using r.MethodNotAllowedHandler = http.HandlerFunc(logjam.MethodNotAllowedHandler).

func NotFoundHandler added in v0.0.15

func NotFoundHandler(w http.ResponseWriter, r *http.Request)

NotFoundHandler is an example handler function to deal with unroutable requests. If you use gorilla/mux, you can install it on your router using r.NotFoundHandler = http.HandlerFunc(logjam.NotFoundHandler).

func SetCallHeaders added in v0.0.7

func SetCallHeaders(ctx context.Context, outgoing *http.Request)

SetCallHeaders makes sure all X-Logjam-* Headers are copied into the outgoing request. Call this before you call other APIs.

Types

type ActionNameExtractor

type ActionNameExtractor func(*http.Request) string

ActionNameExtractor takes a HTTP request and returns a logjam conformant action name.

type Agent added in v0.0.9

type Agent struct {
	Options
	// contains filtered or unexported fields
}

Agent encapsulates information about a logjam agent.

func NewAgent added in v0.0.9

func NewAgent(options *Options) *Agent

NewAgent returns a new logjam agent.

func (*Agent) NewHandler added in v0.0.10

func (a *Agent) NewHandler(handler http.Handler, options MiddlewareOptions) http.Handler

NewHandler can be used to wrap any standard http.Handler. It handles panics caused by the next handler in the chain by logging an error message to os.Stderr and sending the same message to logjam. If the handler hasn't already written something to the response writer, or set its response code, it will write a 500 response with an empty response body. If the middleware option BubblePanics is true, it will panic again with the original object.

func (*Agent) NewMiddleware added in v0.0.9

func (a *Agent) NewMiddleware(options MiddlewareOptions) func(http.Handler) http.Handler

NewMiddleware is a convenience function to be used with the gorilla/mux package.

func (*Agent) NewRequest added in v0.0.9

func (a *Agent) NewRequest(action string) *Request

NewRequest creates a new logjam request for a given action name.

func (*Agent) Shutdown added in v0.0.9

func (a *Agent) Shutdown()

Shutdown the agent.

type DiscardingLogger added in v0.0.9

type DiscardingLogger struct{}

DiscardingLogger discards all log lines.

func (*DiscardingLogger) Println added in v0.0.9

func (d *DiscardingLogger) Println(v ...interface{})

Println does nothing.

type LogLevel

type LogLevel int

LogLevel (modeled after Ruby log levels).

const (
	DEBUG LogLevel = 0
	INFO  LogLevel = 1
	WARN  LogLevel = 2
	ERROR LogLevel = 3
	FATAL LogLevel = 4
)

type Logger

type Logger struct {
	*log.Logger          // The embedded log.Logger.
	LogLevel    LogLevel // Log attemtps with a log level lower than this field are not forwarded to the embbeded logger.
}

Logger extends the standard log.Logger with methods that send log lines to both logjam and the embedded Logger. Even though all standard logger methods are available, you will want to use Fatal, Fatalf and Fatalln only during program startup/shutdown, as they abort the runnning process. Note that lines which are logged at a level below the configured log level will not be sent to the embedded logger, only forwarded to the logjam logger. Usually one would configure log level DEBUG for development and ERROR or even FATAL for production environments, as logs are sent to logjam and/or Graylog anyway.

func (*Logger) Debug added in v0.0.9

func (l *Logger) Debug(ctx context.Context, args ...interface{})

Debug logs with DEBUG severity.

func (*Logger) Debugf added in v0.0.9

func (l *Logger) Debugf(ctx context.Context, format string, args ...interface{})

Debugf logs with DEBUG severity.

func (*Logger) Error added in v0.0.9

func (l *Logger) Error(ctx context.Context, args ...interface{})

Error logs with ERROR severity.

func (*Logger) Errorf added in v0.0.9

func (l *Logger) Errorf(ctx context.Context, format string, args ...interface{})

Errorf logs with ERROR severity.

func (*Logger) Exception added in v0.0.10

func (l *Logger) Exception(ctx context.Context, tag string, args ...interface{})

Exception logs an exception tag and adds the exception to the logjam request.

func (*Logger) Exceptionf added in v0.0.10

func (l *Logger) Exceptionf(ctx context.Context, tag string, format string, args ...interface{})

Exceptionf logs an exception tag and adds the exception to the logjam request.

func (*Logger) Info added in v0.0.9

func (l *Logger) Info(ctx context.Context, args ...interface{})

Info logs with INFO severity.

func (*Logger) Infof added in v0.0.9

func (l *Logger) Infof(ctx context.Context, format string, args ...interface{})

Infof logs with INFO severity.

func (*Logger) Warn added in v0.0.9

func (l *Logger) Warn(ctx context.Context, args ...interface{})

Warn logs with WARN severity.

func (*Logger) Warnf added in v0.0.9

func (l *Logger) Warnf(ctx context.Context, format string, args ...interface{})

Warnf logs WARN severity.

type MiddlewareOptions added in v0.0.10

type MiddlewareOptions struct {
	BubblePanics bool // Whether the logjam middleware should let panics bubble up the handler chain.
}

MiddlewareOptions defines options for the logjam middleware.

type Options added in v0.0.7

type Options struct {
	AppName             string              // Name of your application
	EnvName             string              // What environment you're running in (production, preview, ...)
	Endpoints           string              // Comma separated list of ZeroMQ connections specs, defaults to localhost
	Port                int                 // ZeroMQ default port for ceonnection specs
	Linger              int                 // ZeroMQ socket option of the same name
	Sndhwm              int                 // ZeroMQ socket option of the same name
	Rcvhwm              int                 // ZeroMQ socket option of the same name
	Sndtimeo            int                 // ZeroMQ socket option of the same name
	Rcvtimeo            int                 // ZeroMQ socket option of the same name
	Logger              Printer             // Logjam errors are printed using this interface.
	LogLevel            LogLevel            // Only lines with a severity equal to or higher are sent to logjam. Defaults to DEBUG.
	ActionNameExtractor ActionNameExtractor // Function to transform path segments to logjam action names.
	ObfuscateIPs        bool                // Whether IP addresses should be obfuscated.
	MaxLineLength       int                 // Long lines truncation threshold, defaults to 2048.
	MaxBytesAllLines    int                 // Max number of bytes of all log lines, defaults to 1MB.
}

Options such as appliction name, environment and ZeroMQ socket options.

type Printer added in v0.0.9

type Printer interface {
	Println(args ...interface{})
}

Printer is a minimal interface for the agent to log errors.

type Request

type Request struct {
	// contains filtered or unexported fields
}

Request encapsulates information about the current logjam request.

func GetRequest

func GetRequest(ctx context.Context) *Request

GetRequest retrieves a logjam request from an Context. Returns nil if no request is stored in the context.

func (*Request) AddCount

func (r *Request) AddCount(key string, value int64)

AddCount increments a counter metric associated with this request.

func (*Request) AddDuration

func (r *Request) AddDuration(key string, value time.Duration)

AddDuration increases increments a timer metric associated with this request.

func (*Request) AddException added in v0.0.10

func (r *Request) AddException(name string)

AddException adds an exception tag to be sent to logjam.

func (*Request) AugmentRequest

func (r *Request) AugmentRequest(incoming *http.Request) *http.Request

AugmentRequest extends a given http request with a logjam request stored in its context.

func (*Request) ChangeAction added in v0.0.6

func (r *Request) ChangeAction(w http.ResponseWriter, action string)

ChangeAction changes the action name and updates the corresponding header on the given http request writer.

func (*Request) Count

func (r *Request) Count(key string)

Count behaves like AddCount with a value of 1

func (*Request) Finish

func (r *Request) Finish(code int)

Finish adds the response code to the requests and sends it to logjam.

func (*Request) GetField

func (r *Request) GetField(key string) interface{}

GetField retrieves sets an additional key value pair on the request.

func (*Request) Log added in v0.0.3

func (r *Request) Log(severity LogLevel, line string)

Log adds a log line to be sent to logjam to the request.

func (*Request) MeasureDuration

func (r *Request) MeasureDuration(key string, f func())

MeasureDuration is a helper function that records the duration of execution of the passed function in cases where it is cumbersome to just use AddDuration instead.

func (*Request) NewContext

func (r *Request) NewContext(c context.Context) context.Context

NewContext creates a new context with the request added.

func (*Request) SetField

func (r *Request) SetField(key string, value interface{})

SetField sets an additional key value pair on the request.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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