aelog

package module
v0.0.0-...-e52b05e Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2024 License: Apache-2.0 Imports: 9 Imported by: 0

Documentation

Overview

Package aelog adds support for structured logging in Google App Engine apps.

Basic usage

In the simplest case, create a new Handler and use it for the default logger:

func main() {
	slog.SetDefault(slog.New(aelog.NewHandler(os.Stderr, nil, nil)))
	// register handlers and start server
}

Then, any logging through the package-level functions of the slog package will be formatted as JSON in a structure that the Google Cloud Logging machinery can parse.

The handler maps logging levels to matching severities. Any unnamed level maps to the next-higher severity; for example, any level in the half-open interval (slog.LevelWarn, slog.LevelError] maps to ERROR. The package also defines a few additional named levels such as LevelNotice.

HTTP middleware

To support additional logging entries for HTTP requests, you can use the Middleware function to install an HTTP middleware. For this to work, you need to wrap your HTTP handlers using Middleware and use the context-aware logging functions such as slog.InfoContext with a context returned by http.Request.Context (or a derived context). See the example for the Middleware function for a worked-out example.

Example
package main

import (
	"log/slog"
	"os"

	"github.com/phst/aelog"
)

func main() {
	// This shows the most basic use case.
	slog.SetDefault(slog.New(aelog.NewHandler(os.Stderr, nil, nil)))
}
Output:

Index

Examples

Constants

View Source
const (
	SeverityKey       = "severity"
	MessageKey        = "message"
	TimeKey           = "time"
	SourceLocationKey = "logging.googleapis.com/sourceLocation"
)

Constants for special keys in the output record.

View Source
const (
	LevelCritical = LevelError + 4*(iota+1)
	LevelAlert
	LevelEmergency
	LevelNotice = (LevelInfo + LevelWarn) / 2
)

Additional logging levels corresponding to Cloud Logging severities.

View Source
const (
	LevelDebug = slog.LevelDebug
	LevelInfo  = slog.LevelInfo
	LevelWarn  = slog.LevelWarn
	LevelError = slog.LevelError
)

Aliases for the standard levels, just for symmetry reasons.

Variables

This section is empty.

Functions

func Middleware

func Middleware(h http.Handler) http.Handler

Middleware returns a derived version of the given HTTP handler that calls it after ensuring that a Handler can extract HTTP-specific information from HTTP requests.

Example
package main

import (
	"io"
	"log/slog"
	"net/http"
	"net/http/httptest"
	"os"

	"github.com/phst/aelog"
)

func main() {
	// Suppress time and other noise.
	removeNoise := func(_ []string, a slog.Attr) slog.Attr {
		switch a.Key {
		case aelog.TimeKey, "userAgent", "remoteIp", "protocol":
			return slog.Group("")
		default:
			return a
		}
	}
	log := slog.New(aelog.NewHandler(
		os.Stdout,
		&slog.HandlerOptions{ReplaceAttr: removeNoise},
		&aelog.Options{ProjectID: "test"},
	))

	handler := func(w http.ResponseWriter, r *http.Request) {
		log.InfoContext(r.Context(), "hi")
		io.WriteString(w, "ok")
	}
	srv := httptest.NewServer(aelog.Middleware(http.HandlerFunc(handler)))
	defer srv.Close()

	resp, err := srv.Client().Get(srv.URL)
	if err != nil {
		panic(err)
	}
	resp.Body.Close()

	req, err := http.NewRequest(http.MethodGet, srv.URL, nil)
	if err != nil {
		panic(err)
	}
	// https://cloud.google.com/trace/docs/setup#force-trace
	req.Header.Add("X-Cloud-Trace-Context", "abc/123;o=1")
	resp, err = srv.Client().Do(req)
	if err != nil {
		panic(err)
	}
	resp.Body.Close()
}
Output:

{"severity":"INFO","message":"hi","httpRequest":{"requestMethod":"GET","requestUrl":"/"}}
{"severity":"INFO","message":"hi","httpRequest":{"requestMethod":"GET","requestUrl":"/"},"logging.googleapis.com/trace":"projects/test/traces/abc","logging.googleapis.com/spanId":"123"}

Types

type Handler

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

Handler is an slog.Handler that sends structured log messages in JSON format. Use NewHandler to create Handler objects; the zero Handler isn’t valid. Handler objects can’t be copied once created.

Example
package main

import (
	"log/slog"
	"os"

	"github.com/phst/aelog"
)

func main() {
	// Suppress timestamp noise.
	removeTime := func(_ []string, a slog.Attr) slog.Attr {
		if a.Key == aelog.TimeKey {
			return slog.Group("")
		}
		return a
	}

	log := slog.New(aelog.NewHandler(os.Stdout, &slog.HandlerOptions{ReplaceAttr: removeTime}, nil))

	log.Info("info")
	log.Warn("warning", "foo", "bar")
	log.Debug("this message won’t appear")
	log.With("foo", "bar").Info("info", "attr", 123)
	log.WithGroup("group").Error("error", "foo", "bar")
}
Output:

{"severity":"INFO","message":"info"}
{"severity":"WARNING","message":"warning","foo":"bar"}
{"severity":"INFO","message":"info","foo":"bar","attr":123}
{"severity":"ERROR","message":"error","group":{"foo":"bar"}}

func NewHandler

func NewHandler(w io.Writer, basicOpts *slog.HandlerOptions, extOpts *Options) *Handler

NewHandler creates a new Handler. The handler will write to the given io.Writer (typically os.Stderr). It can be configured using generic slog.HandlerOptions and App Engine-specific Options. Passing nil for any of the options has the same effect as passing a pointer to a zero struct.

If Options doesn’t contain a project ID, NewHandler attempts to auto-detect the current project; this typically works when running in production. If no project can be detected, tracing information won’t be filled out.

func (*Handler) Enabled

func (h *Handler) Enabled(ctx context.Context, l slog.Level) bool

Enabled implements slog.Handler.Enabled.

func (*Handler) Handle

func (h *Handler) Handle(ctx context.Context, r slog.Record) error

Handle implements slog.Handler.Handle.

func (*Handler) WithAttrs

func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler

WithAttrs implements slog.Handler.WithAttrs.

func (*Handler) WithGroup

func (h *Handler) WithGroup(name string) slog.Handler

WithGroup implements slog.Handler.WithGroup.

type Options

type Options struct {
	// Alphanumeric Google Cloud project ID of the current project.  If
	// empty, NewHandler tries to auto-detect the project ID.
	ProjectID string
}

Options contains additional options for configuring a Handler. It can be passed to NewHandler.

Jump to

Keyboard shortcuts

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