Categorygithub.com/planetscale/log
modulepackage
0.0.0-20230818230039-324540f3f1cf
Repository: https://github.com/planetscale/log.git
Documentation: pkg.go.dev

# 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.

# Functions

DetectBuffering detects both if an override is set with PS_LOG_BUFFERED, and what the override says.
DetectEncoding detects the encoding to use based on PS_DEV_MODE env var.
DetectLevel returns a the Level based on PS_LOG_LEVEL env var.
New creates a new default PlanetScale Logger with auto detection of level.
NewAtLevel creates a new PlanetScale Logger at the desired Level.
NewNop returns a no-op logger.
NewPlanetScaleConfig creates a zap.Config with the desired encoding and Level.
NewPlanetScaleConfigDefault creates an opinionated zap.Config while detecting encoding and Level.
NewPlanetScaleLogger creates an opinionated zap.Logger.
NewPlanetScaleLoggerAtLevel creates an opinionated Logger at a desired Level.
NewPlanetScaleSugarLogger creates an opinionated zap.SugaredLogger.
No description provided by the author
NewSlackGoAdapter is a wrapper around a zap.Logger that implements the slack.Logger interface for the github.com/slack-go/slack package.
NewTemporalAdapter wraps a *zap.Logger to implement the temporal.Logger interface.
ParseLevel parses a level based on the lower-case or all-caps ASCII representation of the log level.
Stringers constructs a field with the given key, holding a list of the output provided by the value's String method.

# Constants

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

# Variables

Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
Re-export of all zap field functions for convenience.
No description provided by the author

# Structs

Config is our logging configration.
No description provided by the author
No description provided by the author

# Type aliases

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