README.md

  1# Log
  2
  3<p>
  4    <picture>
  5      <source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359">
  6      <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/25087/219743408-3d7bef51-1409-40c0-8159-acc6e52f078e.png" width="359">
  7      <img src="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359" />
  8    </picture>
  9    <br>
 10    <a href="https://github.com/charmbracelet/log/releases"><img src="https://img.shields.io/github/release/charmbracelet/log.svg" alt="Latest Release"></a>
 11    <a href="https://pkg.go.dev/github.com/charmbracelet/log?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="Go Docs"></a>
 12    <a href="https://github.com/charmbracelet/log/actions"><img src="https://github.com/charmbracelet/log/workflows/build/badge.svg" alt="Build Status"></a>
 13    <a href="https://codecov.io/gh/charmbracelet/log"><img alt="Codecov branch" src="https://img.shields.io/codecov/c/github/charmbracelet/log/main.svg"></a>
 14    <a href="https://goreportcard.com/report/github.com/charmbracelet/log"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/charmbracelet/log"></a>
 15</p>
 16
 17A minimal and colorful Go logging library. 🪵
 18
 19<picture>
 20    <source media="(prefers-color-scheme: dark)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
 21    <source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
 22    <!-- <source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-2NvOYS29AauVRgRRPmquXx.gif"> -->
 23    <img src="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif" alt="Made with VHS" />
 24</picture>
 25
 26It provides a leveled structured human readable logger with a small API. Unlike
 27[standard `log`][stdlog], the Charm logger provides customizable colorful human
 28readable logging with batteries included.
 29
 30- Uses [Lip Gloss][lipgloss] to style and colorize the output.
 31- Colorful, human readable format.
 32- Ability to customize the time stamp format.
 33- Skips caller frames and marks function as helpers.
 34- Leveled logging.
 35- Text, JSON, and Logfmt formatters.
 36- Store and retrieve logger in and from context.
 37- Slog handler.
 38- Standard log adapter.
 39
 40## Usage
 41
 42Use `go get` to download the dependency.
 43
 44```bash
 45go get github.com/charmbracelet/log@latest
 46```
 47
 48Then, `import` it in your Go files:
 49
 50```go
 51import "github.com/charmbracelet/log"
 52```
 53
 54The Charm logger comes with a global package-wise logger with timestamps turned
 55on, and the logging level set to `info`.
 56
 57```go
 58log.Debug("Cookie 🍪") // won't print anything
 59log.Info("Hello World!")
 60```
 61
 62<picture>
 63    <source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
 64    <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
 65    <!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif"> -->
 66    <img width="400" src="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif" alt="Made with VHS" />
 67</picture>
 68
 69All logging levels accept optional key/value pairs to be printed along with a
 70message.
 71
 72```go
 73err := fmt.Errorf("too much sugar")
 74log.Error("failed to bake cookies", "err", err)
 75```
 76
 77<picture>
 78    <source media="(prefers-color-scheme: dark)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
 79    <source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
 80    <!-- <source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-7rk8wALXRDoFw8SLFwn9rW.gif"> -->
 81    <img width="600" src="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" alt="Made with VHS">
 82</picture>
 83
 84You can use `log.Print()` to print messages without a level prefix.
 85
 86```go
 87log.Print("Baking 101")
 88// 2023/01/04 10:04:06 Baking 101
 89```
 90
 91### New loggers
 92
 93Use `New()` to create new loggers.
 94
 95```go
 96logger := log.New(os.Stderr)
 97if butter {
 98    logger.Warn("chewy!", "butter", true)
 99}
100```
101
102<picture>
103    <source media="(prefers-color-scheme: dark)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
104    <source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
105    <!-- <source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-1nrhNSuFnQkxWD4RoMlE4O.gif"> -->
106    <img width="300" src="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
107</picture>
108
109### Levels
110
111Log offers multiple levels to filter your logs on. Available levels are:
112
113```go
114log.DebugLevel
115log.InfoLevel
116log.WarnLevel
117log.ErrorLevel
118log.FatalLevel
119```
120
121Use `log.SetLevel()` to set the log level. You can also create a new logger with
122a specific log level using `log.Options{Level: }`.
123
124Use the corresponding function to log a message:
125
126```go
127err := errors.New("Baking error 101")
128log.Debug(err)
129log.Info(err)
130log.Warn(err)
131log.Error(err)
132log.Fatal(err) // this calls os.Exit(1)
133log.Print(err) // prints regardless of log level
134```
135
136Or use the formatter variant:
137
138```go
139format := "%s %d"
140log.Debugf(format, "chocolate", 10)
141log.Warnf(format, "adding more", 5)
142log.Errorf(format, "increasing temp", 420)
143log.Fatalf(format, "too hot!", 500) // this calls os.Exit(1)
144log.Printf(format, "baking cookies") // prints regardless of log level
145
146// Use these in conjunction with `With(...)` to add more context
147log.With("err", err).Errorf("unable to start %s", "oven")
148```
149
150### Structured
151
152All the functions above take a message and key-value pairs of anything. The
153message can also be of type any.
154
155```go
156ingredients := []string{"flour", "butter", "sugar", "chocolate"}
157log.Debug("Available ingredients", "ingredients", ingredients)
158// DEBUG Available ingredients ingredients="[flour butter sugar chocolate]"
159```
160
161### Options
162
163You can customize the logger with options. Use `log.NewWithOptions()` and
164`log.Options{}` to customize your new logger.
165
166```go
167logger := log.NewWithOptions(os.Stderr, log.Options{
168    ReportCaller: true,
169    ReportTimestamp: true,
170    TimeFormat: time.Kitchen,
171    Prefix: "Baking 🍪 ",
172})
173logger.Info("Starting oven!", "degree", 375)
174time.Sleep(10 * time.Minute)
175logger.Info("Finished baking")
176```
177
178<picture>
179    <source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
180    <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
181    <!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-2X8Esd8ZsHo4DVPVgR36yx.gif"> -->
182    <img width="700" src="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
183</picture>
184
185You can also use logger setters to customize the logger.
186
187```go
188logger := log.New(os.Stderr)
189logger.SetReportTimestamp(false)
190logger.SetReportCaller(false)
191logger.SetLevel(log.DebugLevel)
192```
193
194Use `log.SetFormatter()` or `log.Options{Formatter: }` to change the output
195format. Available options are:
196
197- `log.TextFormatter` (_default_)
198- `log.JSONFormatter`
199- `log.LogfmtFormatter`
200
201> **Note** styling only affects the `TextFormatter`. Styling is disabled if the
202> output is not a TTY.
203
204For a list of available options, refer to [options.go](./options.go).
205
206### Styles
207
208You can customize the logger styles using [Lipgloss][lipgloss]. The styles are
209defined at a global level in [styles.go](./styles.go).
210
211```go
212// Override the default error level style.
213styles := log.DefaultStyles()
214styles.Levels[log.ErrorLevel] = lipgloss.NewStyle().
215	SetString("ERROR!!").
216	Padding(0, 1, 0, 1).
217	Background(lipgloss.Color("204")).
218	Foreground(lipgloss.Color("0"))
219// Add a custom style for key `err`
220styles.Keys["err"] = lipgloss.NewStyle().Foreground(lipgloss.Color("204"))
221styles.Values["err"] = lipgloss.NewStyle().Bold(true)
222logger := log.New(os.Stderr)
223logger.SetStyles(styles)
224logger.Error("Whoops!", "err", "kitchen on fire")
225```
226
227<picture>
228    <source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
229    <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
230    <!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4f6qLnIfudMMLDD9sxXUrv.gif"> -->
231    <img width="400" src="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
232</picture>
233
234### Sub-logger
235
236Create sub-loggers with their specific fields.
237
238```go
239logger := log.NewWithOptions(os.Stderr, log.Options{
240    Prefix: "Baking 🍪 "
241})
242batch2 := logger.With("batch", 2, "chocolateChips", true)
243batch2.Debug("Preparing batch 2...")
244batch2.Debug("Adding chocolate chips")
245```
246
247<picture>
248    <source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
249    <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
250    <img width="700" src="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
251</picture>
252
253### Format Messages
254
255You can use `fmt.Sprintf()` to format messages.
256
257```go
258for item := 1; i <= 100; i++ {
259    log.Info(fmt.Sprintf("Baking %d/100...", item))
260}
261```
262
263<picture>
264    <source media="(prefers-color-scheme: dark)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
265    <source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
266    <!-- <source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4RHXd4JSucomcPqJGZTpKh.gif"> -->
267    <img width="500" src="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
268</picture>
269
270Or arguments:
271
272```go
273for temp := 375; temp <= 400; temp++ {
274    log.Info("Increasing temperature", "degree", fmt.Sprintf("%d°F", temp))
275}
276```
277
278<picture>
279    <source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
280    <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
281    <!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-4AvAnoA2S53QTOteX8krp4.gif"> -->
282    <img width="700" src="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
283</picture>
284
285### Helper Functions
286
287Skip caller frames in helper functions. Similar to what you can do with
288`testing.TB().Helper()`.
289
290```go
291func startOven(degree int) {
292    log.Helper()
293    log.Info("Starting oven", "degree", degree)
294}
295
296log.SetReportCaller(true)
297startOven(400) // INFO <cookies/oven.go:123> Starting oven degree=400
298```
299
300<picture>
301    <source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
302    <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
303    <!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6DPg0bVL4K4TkfoHkAn2ap.gif"> -->
304    <img width="700" src="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
305</picture>
306
307This will use the _caller_ function (`startOven`) line number instead of the
308logging function (`log.Info`) to report the source location.
309
310### Slog Handler
311
312You can use Log as an [`log/slog`](https://pkg.go.dev/log/slog) handler. Just
313pass a logger instance to Slog and you're good to go.
314
315```go
316handler := log.New(os.Stderr)
317logger := slog.New(handler)
318logger.Error("meow?")
319```
320
321### Standard Log Adapter
322
323Some Go libraries, especially the ones in the standard library, will only accept
324the [standard logger][stdlog] interface. For instance, the HTTP Server from
325`net/http` will only take a `*log.Logger` for its `ErrorLog` field.
326
327For this, you can use the standard log adapter, which simply wraps the logger in
328a `*log.Logger` interface.
329
330```go
331logger := log.NewWithOptions(os.Stderr, log.Options{Prefix: "http"})
332stdlog := logger.StandardLog(log.StandardLogOptions{
333    ForceLevel: log.ErrorLevel,
334})
335s := &http.Server{
336    Addr:     ":8080",
337    Handler:  handler,
338    ErrorLog: stdlog,
339}
340stdlog.Printf("Failed to make bake request, %s", fmt.Errorf("temperature is too low"))
341// ERROR http: Failed to make bake request, temperature is too low
342```
343
344## Gum
345
346<img src="https://vhs.charm.sh/vhs-6jupuFM0s2fXiUrBE0I1vU.gif" width="600" alt="Running gum log with debug and error levels" />
347
348Log integrates with [Gum][gum] to log messages to output. Use `gum log [flags]
349[message]` to handle logging in your shell scripts. See
350[charmbracelet/gum](https://github.com/charmbracelet/gum#log) for more
351information.
352
353[gum]: https://github.com/charmbracelet/gum
354[lipgloss]: https://github.com/charmbracelet/lipgloss
355[stdlog]: https://pkg.go.dev/log
356
357## License
358
359[MIT](https://github.com/charmbracelet/log/raw/master/LICENSE)
360
361---
362
363Part of [Charm](https://charm.sh).
364
365<a href="https://charm.sh/"><img alt="the Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
366
367Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة