/log

A minimal, colorful Go logging library 🪵

Primary LanguageGoMIT LicenseMIT

Log


Latest Release Go Docs Build Status

A minimal and colorful Go logging library. 🪵

Made with VHS

It provides a leveled structured human readable logger with a small API. Unlike standard log, the Charm logger provides customizable colorful human readable logging with batteries included.

  • Uses Lip Gloss to style and colorize the output.
  • Colorful, human readable format.
  • Ability to customize the time stamp format.
  • Skips caller frames and marks function as helpers.
  • Leveled logging.
  • Text, JSON, and Logfmt formatters.
  • Store and retrieve logger in and from context.
  • Standard log adapter.

Usage

Use go get to download the dependency.

go get github.com/charmbracelet/log@latest

Then, import it in your Go files:

import "github.com/charmbracelet/log"

The Charm logger comes with a global package-wise logger with timestamps turned on, and the logging level set to info.

log.Debug("Cookie 🍪") // won't print anything
log.Info("Hello World!")
Made with VHS

All logging levels accept optional key/value pairs to be printed along with a message.

err := fmt.Errorf("too much sugar")
log.Error("failed to bake cookies", "err", err)
Made with VHS

You can use log.Print() to print messages without a level prefix.

log.Print("Baking 101")
// 2023/01/04 10:04:06 Baking 101

New loggers

Use New() to create new loggers.

logger := log.New()
if butter {
    logger.Warn("chewy!", "butter", true)
}

Levels

Log offers multiple levels to filter your logs on. Available levels are:

log.DebugLevel
log.WarnLevel
log.ErrorLevel
log.FatalLevel

Use log.SetLevel or create a new logger with the log.WithLevel option to set the level.

Use the corresponding function to log a message:

err := errors.New("Baking error 101")
log.Debug(err)
log.Warn(err)
log.Error(err)
log.Fatal(err) // this calls os.Exit(1)
log.Print(err) // prints regardless of log level

Structured

All the functions above take a message and key-value pairs of anything. The message can also be of type any.

ingredients := []string{"flour", "butter", "sugar", "chocolate"}
log.Debug("Available ingredients", "ingredients", ingredients)
// DEBUG Available ingredients ingredients="[flour butter sugar chocolate]"

Options

You can customize the logger with options. Use log.WithCaller() to enable printing source location. log.WithTimestamp() prints the timestamp of each log.

logger := log.New(log.WithTimestamp(), log.WithTimeFormat(time.Kitchen),
    log.WithCaller(), log.WithPrefix("Baking 🍪 "))
logger.Info("Starting oven!", "degree", 375)
time.Sleep(10 * time.Minute)
logger.Info("Finished baking")

Use log.SetFormatter() or log.WithFormatter() to change the output format. Available options are:

  • log.TextFormatter (default)
  • log.JSONFormatter
  • log.LogfmtFormatter

Note styling only affects the TextFormatter. Styling is disabled if the output is not a TTY.

For a list of available options, refer to options.go.

Set the logger level and options.

logger.SetReportTimestamp(false)
logger.SetReportCaller(false)
logger.SetLevel(log.DebugLevel)

Styles

You can customize the logger styles using Lipgloss. The styles are defined at a global level in styles.go.

// Override the default error level style.
log.ErrorLevelStyle = lipgloss.NewStyle().
    SetString("ERROR!!").
    Padding(0, 1, 0, 1).
    Background(lipgloss.AdaptiveColor{
        Light: "203",
        Dark:  "204",
    }).
    Foreground(lipgloss.Color("0"))
// Add a custom style for key `err`
log.KeyStyles["err"] = lipgloss.NewStyle().Foreground(lipgloss.Color("204"))
log.Error("Whoops!", "err", "kitchen on fire")

Sub-logger

Create sub-loggers with their specific fields.

batch2 := logger.With("batch", 2, "chocolateChips", true)
batch2.Debug("Preparing batch 2...")
batch2.Debug("Adding chocolate chips")

Format Messages

You can use fmt.Sprintf() to format messages.

for item := 1; i <= 100; i++ {
    log.Info(fmt.Sprintf("Baking %d/100...", item))
}

Or arguments:

for temp := 375; temp <= 400; temp++ {
    log.Info("Increasing temperature", "degree", fmt.Sprintf("%d°F", temp))
}

Helper Functions

Skip caller frames in helper functions. Similar to what you can do with testing.TB().Helper().

function startOven(degree int) {
    log.Helper()
    log.Info("Starting oven", "degree", degree)
}

log.SetReportCaller(true)
startOven(400) // INFO <cookies/oven.go:123> Starting oven degree=400

This will use the caller function (startOven) line number instead of the logging function (log.Info) to report the source location.

Standard Log Adapter

Some Go libraries, especially the ones in the standard library, will only accept the standard logger interface. For instance, the HTTP Server from net/http will only take a *log.Logger for its ErrorLog field.

For this, you can use the standard log adapter, which simply wraps the logger in a *log.Logger interface.

stdlog := log.New(log.WithPrefix("http")).StandardLog(log.StandardLogOption{
    ForceLevel: log.ErrorLevel,
})
s := &http.Server{
    Addr:     ":8080",
    Handler:  handler,
    ErrorLog: stdlog,
}
stdlog.Printf("Failed to make bake request, %s", fmt.Errorf("temperature is too low"))
// ERROR http: Failed to make bake request, temperature is too low

License

MIT


Part of Charm.

the Charm logo

Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة