log is a free, open source monitoring & observability project written in Go and released under MIT. It has 3,414 GitHub stars, 105 forks and 41 open issues, and was last pushed 1 months ago. On this registry it ranks #125 of 270 tracked projects in Monitoring & Observability, with 5 head-to-head comparisons available. It gained 14 stars over the last 30 tracked days.

What is log?

Charm's log is a minimal, colorful, MIT-licensed logging library for Go, aimed at Go developers who want leveled, structured, human-readable output and a small API instead of hand-assembling formatting and level filtering on top of the standard library.

What it is

Log is a logging library in the Go ecosystem, published as the module charm.land/log/v2 and styled with Lip Gloss. It provides a leveled structured human-readable logger with a deliberately small API. The package ships a global, package-wise logger with timestamps turned on and the logging level set to info, and it can create independent loggers through log.New(os.Stderr) or log.NewWithOptions(os.Stderr, log.Options{...}). Options cover ReportCaller, ReportTimestamp, TimeFormat, Prefix, Level and Formatter, and the same settings can be changed on a live logger with setters such as SetReportTimestamp, SetReportCaller, SetLevel and SetFormatter.

The concrete problem it solves is the gap left by Go's standard log package. Standard log produces timestamps and prefixes only: no severity levels, no color, no structured key-value pairs, no choice of output format and no way to carry a logger through context. Log replaces that package while keeping a familiar call shape, so log.Info("Hello World!"), log.Errorf("unable to start %s", "oven") and log.Print("Baking 101") read like the standard functions but deliver filtering, structure and styling.

Key capabilities

  • Leveled logging across log.DebugLevel, log.InfoLevel, log.WarnLevel, log.ErrorLevel and log.FatalLevel, filtered with log.SetLevel() or log.Options{Level:}.
  • Key-value pairs on any level, with non-string values and any-typed messages: log.Error("failed to bake cookies", "err", err) and log.With("err", err).Errorf("unable to start %s", "oven").
  • Three output formatters: log.TextFormatter (default), log.JSONFormatter and log.LogfmtFormatter, selected through log.SetFormatter() or log.Options{Formatter:}.
  • Custom styling with Lip Gloss, defined at global level in styles.go and overridden per level or per key via log.DefaultStyles() and lipgloss.NewStyle().
  • Formatter and prefix functions (Debugf, Warnf, Errorf, Fatalf, Printf) alongside plain level functions, where log.Fatal and log.Fatalf call os.Exit(1).
  • Context integration: store and retrieve the logger in and from context.
  • Interoperability through an slog handler and a standard log adapter, plus caller-frame skipping and helper marking.

Who uses it and how

  • Command-line and terminal tools where output is a TTY, since styling applies only to TextFormatter and is disabled when output is not a TTY, leaving piped or redirected logs plain.
  • Services that emit log.JSONFormatter in production and log.TextFormatter locally, switching format through a single option.
  • Libraries that accept a logger through context and retrieve it at call sites rather than threading a logger argument through every function.
  • Code migrating from the standard log package or from Go's slog, using the standard log adapter or the slog handler to keep existing call sites intact.

Getting started

Install with go get charm.land/log/v2@latest and import it as "charm.land/log/v2"; the package then exposes a global logger with timestamps enabled at info level.

How it compares

The README positions it directly against Go's standard log, which offers no levels, no color, no structured key-value pairs and no formatter choice, and which the Charm logger explicitly replaces. Against Go's slog it is an ergonomic and stylistic alternative rather than a competitor, because it ships an slog handler and a standard log adapter so existing slog or log code can keep its call sites.

When to use it — and when not to

Choose it for Go programs that want readable colorized logs on a terminal with levels and structured fields, and no dependency beyond Lip Gloss. Do not choose it for non-Go stacks, or when color output is expected in CI or through a pipe, because styling is applied only to TextFormatter and dropped when the output is not a TTY. The README is a usage guide rather than a deployment document: it describes no rotation, sampling or shipping to a backend, and it defers the full option list to options.go and styles.go, so teams needing those features must look elsewhere; 41 open issues are also worth weighing before adoption.

project readme (upstream, from github) — read inline

Log


Latest Release Go Docs Build Status Codecov branch Go Report Card

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.
  • Slog handler.
  • Standard log adapter.

Usage

Use go get to download the dependency.

go get charm.land/log/v2@latest

Then, import it in your Go files:

import "charm.land/log/v2"

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(os.Stderr)
if butter {
    logger.Warn("chewy!", "butter", true)
}

Levels

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

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

Use log.SetLevel() to set the log level. You can also create a new logger with a specific log level using log.Options{Level: }.

Use the corresponding function to log a message:

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

Or use the formatter variant:

format := "%s %d"
log.Debugf(format, "chocolate", 10)
log.Warnf(format, "adding more", 5)
log.Errorf(format, "increasing temp", 420)
log.Fatalf(format, "too hot!", 500) // this calls os.Exit(1)
log.Printf(format, "baking cookies") // prints regardless of log level

// Use these in conjunction with `With(...)` to add more context
log.With("err", err).Errorf("unable to start %s", "oven")

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.NewWithOptions() and log.Options{} to customize your new logger.

logger := log.NewWithOptions(os.Stderr, log.Options{
    ReportCaller: true,
    ReportTimestamp: true,
    TimeFormat: time.Kitchen,
    Prefix: "Baking 🍪 ",
})
logger.Info("Starting oven!", "degree", 375)
time.Sleep(10 * time.Minute)
logger.Info("Finished baking")

You can also use logger setters to customize the logger.

logger := log.New(os.Stderr)
logger.SetReportTimestamp(false)
logger.SetReportCaller(false)
logger.SetLevel(log.DebugLevel)

Use log.SetFormatter() or log.Options{Formatter: } 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.

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.
styles := log.DefaultStyles()
styles.Levels[log.ErrorLevel] = lipgloss.NewStyle().
	SetString("ERROR!!").
	Padding(0, 1, 0, 1).
	Background(lipgloss.Color("204")).
	Foreground(lipgloss.Color("0"))
// Add a custom style for key `err`
styles.Keys["err"] = lipgloss.NewStyle().Foreground(lipgloss.Color("204"))
styles.Values["err"] = lipgloss.NewStyle().Bold(true)
logger := log.New(os.Stderr)
logger.SetStyles(styles)
logger.Error("Whoops!", "err", "kitchen on fire")

Sub-logger

Create sub-loggers with their specific fields.

logger := log.NewWithOptions(os.Stderr, log.Options{
    Prefix: "Baking 🍪 "
})
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().

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

Slog Handler

You can use Log as an log/slog handler. Just pass a logger instance to Slog and you're good to go.

handler := log.New(os.Stderr)
logger := slog.New(handler)
logger.Error("meow?")

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.

logger := log.NewWithOptions(os.Stderr, log.Options{Prefix: "http"})
stdlog := logger.StandardLog(log.StandardLogOptions{
    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

Gum

Running gum log with debug and error levels

Log integrates with Gum to log messages to output. Use gum log [flags] [message] to handle logging in your shell scripts. See charmbracelet/gum for more information.

License

MIT


Part of Charm.

the Charm logo

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

Frequently asked questions

Is log free to use?

log is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does log do?

A minimal, colorful Go logging library 🪵

What is log written in?

log is primarily written in Go. Its source is publicly available at https://github.com/charmbracelet/log, and it has 3,414 GitHub stars.