log

package module
v0.0.0-...-324540f Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2023 License: Apache-2.0 Imports: 8 Imported by: 1

README

log 🪵

PlanetScale's logger module. Implemented as a set of convenience functions providing a common configuration of the zap logging library. zap "provides fast, structured, leveled logging."

Logs emitted follow the standards from PlanetScale Structure Logging (coda).

Usage

go get github.com/planetscale/log

zap provides two logging interfaces: zap.Logger and zap.SugarLogger. Zap describes each logger and how to choose between them:

In contexts where performance is nice, but not critical, use the zap.SugaredLogger. It's 4-10x faster than other structured logging packages and supports both structured and printf-style logging. Like log15 and go-kit, the SugaredLogger's structured logging APIs are loosely typed and accept a variadic number of key-value pairs. (For more advanced use cases, they also accept strongly typed fields - see the SugaredLogger.With documentation for details.

In the rare contexts where every microsecond and every allocation matter, use the zap.Logger. It's even faster than the SugaredLogger and allocates far less, but it only supports strongly-typed, structured logging.

Examples:

zap.Logger:

import "github.com/planetscale/log"

func main() {
  logger := log.New()
  defer logger.Sync()

  logger.Info("info log with fields",
    // Structured context as typed key-value pairs
    log.String("user_id", "12345678"),
    log.String("branch_id", "xzyhnkhpi12"),
  )
}

zap.SugarLogger:

import "github.com/planetscale/log"

func main() {
  logger := log.NewPlanetScaleSugarLogger()
  defer logger.Sync()

  logger.Infof("info log printf example: %v", "foo")

  logger.Infow("info log with fields",
    // Structured context as loosely typed key-value pairs.
    "user_id", "12345678",
    "branch_id", "xzyhnkhpi12",
  )
}

Additional customizations to the logger config may be obtained by calling the NewPlanetScaleConfig() function to return a pre-configured zap.Config which can be further customized before calling .Build() to create a zap.Logger. Example:

  // disable the `caller` field in logs:
  cfg := log.NewPlanetScaleConfig()
  logger, _ := cfg.Build(zap.WithCaller(false))
  defer logger.Sync()

See ./examples.

glog

Many PlanetScale applications use the github.com/golang/glog library which is commonly used in Vitess and Kuberenetes client libraries. Glog has some interesting properties, namely that it hooks into flags for configuration and causes libraries that use it to output their own logs, regardless of the application's logging config. When combined with this library you will end up with an application that is mixing structured JSON logs from zap with plain-text logs from glog.

Using noglog the glog library's log calls can be replaced with our logger such that all logs emitted by the application are in a common, structured, JSON format.

  1. Add the following to your go.mod:
require (
    github.com/google/glog master
)

replace github.com/google/glog => github.com/planetscale/noglog master
  1. Replace glog's log calls with our SugaredLogger:
  logger := log.NewPlanetScaleSugarLogger()
  defer logger.Sync()

  glog.SetLogger(&glog.LoggerFunc{
    DebugfFunc: func(f string, a ...interface{}) { logger.Debugf(f, a...) },
    InfofFunc:  func(f string, a ...interface{}) { logger.Infof(f, a...) },
    WarnfFunc:  func(f string, a ...interface{}) { logger.Warnf(f, a...) },
    ErrorfFunc: func(f string, a ...interface{}) { logger.Errorf(f, a...) },
  })

If using the zap.Logger call .Sugar() to get a SugaredLogger first:

  logger := log.New()
  defer logger.Sync()

  slogger := logger.Sugar()
  glog.SetLogger(&glog.LoggerFunc{
    DebugfFunc: func(f string, a ...interface{}) { slogger.Debugf(f, a...) },
    InfofFunc:  func(f string, a ...interface{}) { slogger.Infof(f, a...) },
    WarnfFunc:  func(f string, a ...interface{}) { slogger.Warnf(f, a...) },
    ErrorfFunc: func(f string, a ...interface{}) { slogger.Errorf(f, a...) },
  })

Adapters

Adapters are available for the following libraries:

github.com/slack-go/go

Use NewSlackGoAdapter() to wrap a *zap.Logger that can be used with the github.com/slack-go/slack package:

logger := log.NewPlanetScaleLogger()
defer logger.Sync()

wrappedLogger := log.NewSlackGoAdapter(logger)

client := slack.New(
  token,
  slack.OptionAppLevelToken(appToken),
  slack.OptionLog(wrappedLogger),
)

socketClient := socketmode.New(
  client,
  socketmode.OptionLog(wrappedLogger),
)
github.com/temporalio/sdk-go

Use NewTemporalAdapter() to wrap a *zap.Logger that can be used with the github.com/temporalio/sdk-go package:

logger := log.NewPlanetScaleLogger()
defer logger.Sync()

temporalLogger := log.NewTemporalAdapter(logger)

tClient, err := client.NewClient(client.Options{
  HostPort: fmt.Sprintf("%s:%d", host, port),
  Logger: temporalLogger,
})

// or client.Dial():
tClient, err := client.Dial(client.Options{
  HostPort: fmt.Sprintf("%s:%d", host, port),
  Logger: temporalLogger,
})

Development mode

All logs are emitted as JSON by default. Sometimes this can be difficult to read. Set the PS_DEV_MODE=1 environment variable to switch into a more human friendly log format.

Documentation

Index

Constants

View Source
const (
	PrettyEncoding = "pretty"
	JSONEncoding   = "json"
)
View Source
const (
	DebugLevel  = zapcore.DebugLevel
	InfoLevel   = zapcore.InfoLevel
	WarnLevel   = zapcore.WarnLevel
	ErrorLevel  = zapcore.ErrorLevel
	DPanicLevel = zapcore.DPanicLevel
	PanicLevel  = zapcore.PanicLevel
	FatalLevel  = zapcore.FatalLevel
)

Variables

View Source
var (
	Any         = zap.Any
	Array       = zap.Array
	Binary      = zap.Binary
	Bool        = zap.Bool
	Boolp       = zap.Boolp
	Bools       = zap.Bools
	ByteString  = zap.ByteString
	ByteStrings = zap.ByteStrings
	Complex128  = zap.Complex128
	Complex128p = zap.Complex128p
	Complex128s = zap.Complex128s
	Complex64   = zap.Complex64
	Complex64p  = zap.Complex64p
	Complex64s  = zap.Complex64s
	Duration    = zap.Duration
	Durationp   = zap.Durationp
	Durations   = zap.Durations
	Error       = zap.Error
	Errors      = zap.Errors
	Float32     = zap.Float32
	Float32p    = zap.Float32p
	Float32s    = zap.Float32s
	Float64     = zap.Float64
	Float64p    = zap.Float64p
	Float64s    = zap.Float64s
	Inline      = zap.Inline
	Int         = zap.Int
	Int16       = zap.Int16
	Int16p      = zap.Int16p
	Int16s      = zap.Int16s
	Int32       = zap.Int32
	Int32p      = zap.Int32p
	Int32s      = zap.Int32s
	Int64       = zap.Int64
	Int64p      = zap.Int64p
	Int64s      = zap.Int64s
	Int8        = zap.Int8
	Int8p       = zap.Int8p
	Int8s       = zap.Int8s
	Intp        = zap.Intp
	Ints        = zap.Ints
	NamedError  = zap.NamedError
	Namespace   = zap.Namespace
	Object      = zap.Object
	Reflect     = zap.Reflect
	Skip        = zap.Skip
	Stack       = zap.Stack
	StackSkip   = zap.StackSkip
	String      = zap.String
	Stringer    = zap.Stringer
	Stringp     = zap.Stringp
	Strings     = zap.Strings
	Time        = zap.Time
	Timep       = zap.Timep
	Times       = zap.Times
	Uint        = zap.Uint
	Uint16      = zap.Uint16
	Uint16p     = zap.Uint16p
	Uint16s     = zap.Uint16s
	Uint32      = zap.Uint32
	Uint32p     = zap.Uint32p
	Uint32s     = zap.Uint32s
	Uint64      = zap.Uint64
	Uint64p     = zap.Uint64p
	Uint64s     = zap.Uint64s
	Uint8       = zap.Uint8
	Uint8p      = zap.Uint8p
	Uint8s      = zap.Uint8s
	Uintp       = zap.Uintp
	Uintptr     = zap.Uintptr
	Uintptrp    = zap.Uintptrp
	Uintptrs    = zap.Uintptrs
	Uints       = zap.Uints
)

Re-export of all zap field functions for convenience

View Source
var WithCaller = zap.WithCaller

Functions

func DetectBuffering

func DetectBuffering() (bool, bool)

DetectBuffering detects both if an override is set with PS_LOG_BUFFERED, and what the override says. The common case would be to entirely disable buffering, and simply setting PS_LOG_BUFFERED=0 would explicitly disable it.

func DetectEncoding

func DetectEncoding() string

DetectEncoding detects the encoding to use based on PS_DEV_MODE env var.

func NewPrettyEncoder

func NewPrettyEncoder(cfg zapcore.EncoderConfig) zapcore.Encoder

Types

type Config

type Config struct {
	Level    zap.AtomicLevel
	Encoding string
	Buffered bool
	NanoTime bool
}

Config is our logging configration

func NewPlanetScaleConfig

func NewPlanetScaleConfig(encoding string, level Level) Config

NewPlanetScaleConfig creates a zap.Config with the desired encoding and Level.

func NewPlanetScaleConfigDefault

func NewPlanetScaleConfigDefault() Config

NewPlanetScaleConfigDefault creates an opinionated zap.Config while detecting encoding and Level.

func (Config) Build

func (cfg Config) Build(opts ...zap.Option) (*Logger, error)

Build creates a Logger out of our Config. Note that this returns an error, but this doesn't actually error. This is maintained for compatibility with zapcore.Config{}.Build().

type Field

type Field = zap.Field

func Stringers

func Stringers[T interface{ String() string }](key string, value []T) Field

Stringers constructs a field with the given key, holding a list of the output provided by the value's String method

type Level

type Level = zapcore.Level

func DetectLevel

func DetectLevel() Level

DetectLevel returns a the Level based on PS_LOG_LEVEL env var.

func ParseLevel

func ParseLevel(text string) (Level, error)

ParseLevel parses a level based on the lower-case or all-caps ASCII representation of the log level. If the provided ASCII representation is invalid an error is returned.

This is particularly useful when dealing with text input to configure log levels.

This is vendored out of `zapcore` since it's added in newer versions, so it's trivial enough to vendor and not require a newer `zap` module.

type Logger

type Logger = zap.Logger

func New

func New() *Logger

New creates a new default PlanetScale Logger with auto detection of level.

func NewAtLevel

func NewAtLevel(l Level) *Logger

NewAtLevel creates a new PlanetScale Logger at the desired Level.

func NewNop

func NewNop() *Logger

NewNop returns a no-op logger

func NewPlanetScaleLogger

func NewPlanetScaleLogger() *Logger

NewPlanetScaleLogger creates an opinionated zap.Logger. Additional customization is available by passing in one or more zap.Options.

func NewPlanetScaleLoggerAtLevel

func NewPlanetScaleLoggerAtLevel(l Level) *Logger

NewPlanetScaleLoggerAtLevel creates an opinionated Logger at a desired Level.

type SlackGoAdapter

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

func NewSlackGoAdapter

func NewSlackGoAdapter(zl *zap.Logger) *SlackGoAdapter

NewSlackGoAdapter is a wrapper around a zap.Logger that implements the slack.Logger interface for the github.com/slack-go/slack package.

func (SlackGoAdapter) Output

func (l SlackGoAdapter) Output(calldepth int, s string) error

Output implements the slack.Logger interface

type SugaredLogger

type SugaredLogger = zap.SugaredLogger

func NewPlanetScaleSugarLogger

func NewPlanetScaleSugarLogger() *SugaredLogger

NewPlanetScaleSugarLogger creates an opinionated zap.SugaredLogger. Additional customization is available by passing in one or more zap.Options. NOTE: A SugaredLogger can be converted into a zap.Logger with the .DeSugar() method.

type TemporalAdapter

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

func NewTemporalAdapter

func NewTemporalAdapter(zl *zap.Logger) *TemporalAdapter

NewTemporalAdapter wraps a *zap.Logger to implement the temporal.Logger interface.

func (*TemporalAdapter) Debug

func (log *TemporalAdapter) Debug(msg string, keyvals ...interface{})

Debug level log message.

func (*TemporalAdapter) Error

func (log *TemporalAdapter) Error(msg string, keyvals ...interface{})

Error level log message.

func (*TemporalAdapter) Info

func (log *TemporalAdapter) Info(msg string, keyvals ...interface{})

Info level log message.

func (*TemporalAdapter) Warn

func (log *TemporalAdapter) Warn(msg string, keyvals ...interface{})

Warn level log message.

Jump to

Keyboard shortcuts

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