charmbracelet / log

A minimal, colorful Go logging library 🪵
MIT License
2.33k stars 66 forks source link
golang logging

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`][stdlog], the Charm logger provides customizable colorful human readable logging with batteries included. - Uses [Lip Gloss][lipgloss] 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. ```bash go get github.com/charmbracelet/log@latest ``` Then, `import` it in your Go files: ```go 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`. ```go 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. ```go 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. ```go log.Print("Baking 101") // 2023/01/04 10:04:06 Baking 101 ``` ### New loggers Use `New()` to create new loggers. ```go 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: ```go 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: ```go 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: ```go 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. ```go 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. ```go 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. ```go 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](./options.go). ### Styles You can customize the logger styles using [Lipgloss][lipgloss]. The styles are defined at a global level in [styles.go](./styles.go). ```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. ```go 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. ```go for item := 1; i <= 100; i++ { log.Info(fmt.Sprintf("Baking %d/100...", item)) } ``` Or arguments: ```go 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()`. ```go func startOven(degree int) { log.Helper() log.Info("Starting oven", "degree", degree) } log.SetReportCaller(true) startOven(400) // INFO 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`](https://pkg.go.dev/log/slog) handler. Just pass a logger instance to Slog and you're good to go. ```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][stdlog] 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. ```go 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][gum] to log messages to output. Use `gum log [flags] [message]` to handle logging in your shell scripts. See [charmbracelet/gum](https://github.com/charmbracelet/gum#log) for more information. [gum]: https://github.com/charmbracelet/gum [lipgloss]: https://github.com/charmbracelet/lipgloss [stdlog]: https://pkg.go.dev/log ## License [MIT](https://github.com/charmbracelet/log/raw/master/LICENSE) --- Part of [Charm](https://charm.sh). the Charm logo Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة