{"id":13459965,"url":"https://github.com/charmbracelet/log","last_synced_at":"2025-05-13T17:05:12.445Z","repository":{"id":71122092,"uuid":"573542519","full_name":"charmbracelet/log","owner":"charmbracelet","description":"A minimal, colorful Go logging library 🪵","archived":false,"fork":false,"pushed_at":"2025-04-28T12:04:54.000Z","size":539,"stargazers_count":2663,"open_issues_count":22,"forks_count":73,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-05-08T09:06:17.641Z","etag":null,"topics":["golang","logging"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/charmbracelet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-12-02T18:04:11.000Z","updated_at":"2025-05-08T08:11:46.000Z","dependencies_parsed_at":"2024-02-17T01:25:52.299Z","dependency_job_id":"c055755d-bf46-4356-9218-ba985d7d9b77","html_url":"https://github.com/charmbracelet/log","commit_stats":{"total_commits":185,"total_committers":22,"mean_commits":8.409090909090908,"dds":"0.30810810810810807","last_synced_commit":"624268e1b7ae6b5e412839f62ff6677a85e97baa"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charmbracelet%2Flog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charmbracelet%2Flog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charmbracelet%2Flog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/charmbracelet%2Flog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/charmbracelet","download_url":"https://codeload.github.com/charmbracelet/log/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253990460,"owners_count":21995774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","logging"],"created_at":"2024-07-31T10:00:33.104Z","updated_at":"2025-05-13T17:05:12.416Z","avatar_url":"https://github.com/charmbracelet.png","language":"Go","funding_links":[],"categories":["Go","语言资源库"],"sub_categories":["go"],"readme":"# Log\n\n\u003cp\u003e\n    \u003cpicture\u003e\n      \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png\" width=\"359\"\u003e\n      \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://user-images.githubusercontent.com/25087/219743408-3d7bef51-1409-40c0-8159-acc6e52f078e.png\" width=\"359\"\u003e\n      \u003cimg src=\"https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png\" width=\"359\" /\u003e\n    \u003c/picture\u003e\n    \u003cbr\u003e\n    \u003ca href=\"https://github.com/charmbracelet/log/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/release/charmbracelet/log.svg\" alt=\"Latest Release\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://pkg.go.dev/github.com/charmbracelet/log?tab=doc\"\u003e\u003cimg src=\"https://godoc.org/github.com/golang/gddo?status.svg\" alt=\"Go Docs\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/charmbracelet/log/actions\"\u003e\u003cimg src=\"https://github.com/charmbracelet/log/workflows/build/badge.svg\" alt=\"Build Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://codecov.io/gh/charmbracelet/log\"\u003e\u003cimg alt=\"Codecov branch\" src=\"https://img.shields.io/codecov/c/github/charmbracelet/log/main.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/charmbracelet/log\"\u003e\u003cimg alt=\"Go Report Card\" src=\"https://goreportcard.com/badge/github.com/charmbracelet/log\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nA minimal and colorful Go logging library. 🪵\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"https://vhs.charm.sh/vhs-2NvOYS29AauVRgRRPmquXx.gif\"\u003e --\u003e\n    \u003cimg src=\"https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif\" alt=\"Made with VHS\" /\u003e\n\u003c/picture\u003e\n\nIt provides a leveled structured human readable logger with a small API. Unlike\n[standard `log`][stdlog], the Charm logger provides customizable colorful human\nreadable logging with batteries included.\n\n- Uses [Lip Gloss][lipgloss] to style and colorize the output.\n- Colorful, human readable format.\n- Ability to customize the time stamp format.\n- Skips caller frames and marks function as helpers.\n- Leveled logging.\n- Text, JSON, and Logfmt formatters.\n- Store and retrieve logger in and from context.\n- Slog handler.\n- Standard log adapter.\n\n## Usage\n\nUse `go get` to download the dependency.\n\n```bash\ngo get github.com/charmbracelet/log@latest\n```\n\nThen, `import` it in your Go files:\n\n```go\nimport \"github.com/charmbracelet/log\"\n```\n\nThe Charm logger comes with a global package-wise logger with timestamps turned\non, and the logging level set to `info`.\n\n```go\nlog.Debug(\"Cookie 🍪\") // won't print anything\nlog.Info(\"Hello World!\")\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif\"\u003e --\u003e\n    \u003cimg width=\"400\" src=\"https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif\" alt=\"Made with VHS\" /\u003e\n\u003c/picture\u003e\n\nAll logging levels accept optional key/value pairs to be printed along with a\nmessage.\n\n```go\nerr := fmt.Errorf(\"too much sugar\")\nlog.Error(\"failed to bake cookies\", \"err\", err)\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"600\" srcset=\"https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif\" \u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"600\" srcset=\"https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif\" \u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"600\" srcset=\"https://vhs.charm.sh/vhs-7rk8wALXRDoFw8SLFwn9rW.gif\"\u003e --\u003e\n    \u003cimg width=\"600\" src=\"https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif\" alt=\"Made with VHS\"\u003e\n\u003c/picture\u003e\n\nYou can use `log.Print()` to print messages without a level prefix.\n\n```go\nlog.Print(\"Baking 101\")\n// 2023/01/04 10:04:06 Baking 101\n```\n\n### New loggers\n\nUse `New()` to create new loggers.\n\n```go\nlogger := log.New(os.Stderr)\nif butter {\n    logger.Warn(\"chewy!\", \"butter\", true)\n}\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"300\" srcset=\"https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"300\" srcset=\"https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"300\" srcset=\"https://vhs.charm.sh/vhs-1nrhNSuFnQkxWD4RoMlE4O.gif\"\u003e --\u003e\n    \u003cimg width=\"300\" src=\"https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif\"\u003e\n\u003c/picture\u003e\n\n### Levels\n\nLog offers multiple levels to filter your logs on. Available levels are:\n\n```go\nlog.DebugLevel\nlog.InfoLevel\nlog.WarnLevel\nlog.ErrorLevel\nlog.FatalLevel\n```\n\nUse `log.SetLevel()` to set the log level. You can also create a new logger with\na specific log level using `log.Options{Level: }`.\n\nUse the corresponding function to log a message:\n\n```go\nerr := errors.New(\"Baking error 101\")\nlog.Debug(err)\nlog.Info(err)\nlog.Warn(err)\nlog.Error(err)\nlog.Fatal(err) // this calls os.Exit(1)\nlog.Print(err) // prints regardless of log level\n```\n\nOr use the formatter variant:\n\n```go\nformat := \"%s %d\"\nlog.Debugf(format, \"chocolate\", 10)\nlog.Warnf(format, \"adding more\", 5)\nlog.Errorf(format, \"increasing temp\", 420)\nlog.Fatalf(format, \"too hot!\", 500) // this calls os.Exit(1)\nlog.Printf(format, \"baking cookies\") // prints regardless of log level\n\n// Use these in conjunction with `With(...)` to add more context\nlog.With(\"err\", err).Errorf(\"unable to start %s\", \"oven\")\n```\n\n### Structured\n\nAll the functions above take a message and key-value pairs of anything. The\nmessage can also be of type any.\n\n```go\ningredients := []string{\"flour\", \"butter\", \"sugar\", \"chocolate\"}\nlog.Debug(\"Available ingredients\", \"ingredients\", ingredients)\n// DEBUG Available ingredients ingredients=\"[flour butter sugar chocolate]\"\n```\n\n### Options\n\nYou can customize the logger with options. Use `log.NewWithOptions()` and\n`log.Options{}` to customize your new logger.\n\n```go\nlogger := log.NewWithOptions(os.Stderr, log.Options{\n    ReportCaller: true,\n    ReportTimestamp: true,\n    TimeFormat: time.Kitchen,\n    Prefix: \"Baking 🍪 \",\n})\nlogger.Info(\"Starting oven!\", \"degree\", 375)\ntime.Sleep(10 * time.Minute)\nlogger.Info(\"Finished baking\")\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-2X8Esd8ZsHo4DVPVgR36yx.gif\"\u003e --\u003e\n    \u003cimg width=\"700\" src=\"https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif\"\u003e\n\u003c/picture\u003e\n\nYou can also use logger setters to customize the logger.\n\n```go\nlogger := log.New(os.Stderr)\nlogger.SetReportTimestamp(false)\nlogger.SetReportCaller(false)\nlogger.SetLevel(log.DebugLevel)\n```\n\nUse `log.SetFormatter()` or `log.Options{Formatter: }` to change the output\nformat. Available options are:\n\n- `log.TextFormatter` (_default_)\n- `log.JSONFormatter`\n- `log.LogfmtFormatter`\n\n\u003e **Note** styling only affects the `TextFormatter`. Styling is disabled if the\n\u003e output is not a TTY.\n\nFor a list of available options, refer to [options.go](./options.go).\n\n### Styles\n\nYou can customize the logger styles using [Lipgloss][lipgloss]. The styles are\ndefined at a global level in [styles.go](./styles.go).\n\n```go\n// Override the default error level style.\nstyles := log.DefaultStyles()\nstyles.Levels[log.ErrorLevel] = lipgloss.NewStyle().\n\tSetString(\"ERROR!!\").\n\tPadding(0, 1, 0, 1).\n\tBackground(lipgloss.Color(\"204\")).\n\tForeground(lipgloss.Color(\"0\"))\n// Add a custom style for key `err`\nstyles.Keys[\"err\"] = lipgloss.NewStyle().Foreground(lipgloss.Color(\"204\"))\nstyles.Values[\"err\"] = lipgloss.NewStyle().Bold(true)\nlogger := log.New(os.Stderr)\nlogger.SetStyles(styles)\nlogger.Error(\"Whoops!\", \"err\", \"kitchen on fire\")\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"400\" srcset=\"https://vhs.charm.sh/vhs-4f6qLnIfudMMLDD9sxXUrv.gif\"\u003e --\u003e\n    \u003cimg width=\"400\" src=\"https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif\"\u003e\n\u003c/picture\u003e\n\n### Sub-logger\n\nCreate sub-loggers with their specific fields.\n\n```go\nlogger := log.NewWithOptions(os.Stderr, log.Options{\n    Prefix: \"Baking 🍪 \"\n})\nbatch2 := logger.With(\"batch\", 2, \"chocolateChips\", true)\nbatch2.Debug(\"Preparing batch 2...\")\nbatch2.Debug(\"Adding chocolate chips\")\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif\"\u003e\n    \u003cimg width=\"700\" src=\"https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif\"\u003e\n\u003c/picture\u003e\n\n### Format Messages\n\nYou can use `fmt.Sprintf()` to format messages.\n\n```go\nfor item := 1; i \u003c= 100; i++ {\n    log.Info(fmt.Sprintf(\"Baking %d/100...\", item))\n}\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"500\" srcset=\"https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"500\" srcset=\"https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"500\" srcset=\"https://vhs.charm.sh/vhs-4RHXd4JSucomcPqJGZTpKh.gif\"\u003e --\u003e\n    \u003cimg width=\"500\" src=\"https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif\"\u003e\n\u003c/picture\u003e\n\nOr arguments:\n\n```go\nfor temp := 375; temp \u003c= 400; temp++ {\n    log.Info(\"Increasing temperature\", \"degree\", fmt.Sprintf(\"%d°F\", temp))\n}\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-4AvAnoA2S53QTOteX8krp4.gif\"\u003e --\u003e\n    \u003cimg width=\"700\" src=\"https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif\"\u003e\n\u003c/picture\u003e\n\n### Helper Functions\n\nSkip caller frames in helper functions. Similar to what you can do with\n`testing.TB().Helper()`.\n\n```go\nfunc startOven(degree int) {\n    log.Helper()\n    log.Info(\"Starting oven\", \"degree\", degree)\n}\n\nlog.SetReportCaller(true)\nstartOven(400) // INFO \u003ccookies/oven.go:123\u003e Starting oven degree=400\n```\n\n\u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif\"\u003e\n    \u003c!-- \u003csource media=\"(prefers-color-scheme: light)\" width=\"700\" srcset=\"https://vhs.charm.sh/vhs-6DPg0bVL4K4TkfoHkAn2ap.gif\"\u003e --\u003e\n    \u003cimg width=\"700\" src=\"https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif\"\u003e\n\u003c/picture\u003e\n\nThis will use the _caller_ function (`startOven`) line number instead of the\nlogging function (`log.Info`) to report the source location.\n\n### Slog Handler\n\nYou can use Log as an [`log/slog`](https://pkg.go.dev/log/slog) handler. Just\npass a logger instance to Slog and you're good to go.\n\n```go\nhandler := log.New(os.Stderr)\nlogger := slog.New(handler)\nlogger.Error(\"meow?\")\n```\n\n### Standard Log Adapter\n\nSome Go libraries, especially the ones in the standard library, will only accept\nthe [standard logger][stdlog] interface. For instance, the HTTP Server from\n`net/http` will only take a `*log.Logger` for its `ErrorLog` field.\n\nFor this, you can use the standard log adapter, which simply wraps the logger in\na `*log.Logger` interface.\n\n```go\nlogger := log.NewWithOptions(os.Stderr, log.Options{Prefix: \"http\"})\nstdlog := logger.StandardLog(log.StandardLogOptions{\n    ForceLevel: log.ErrorLevel,\n})\ns := \u0026http.Server{\n    Addr:     \":8080\",\n    Handler:  handler,\n    ErrorLog: stdlog,\n}\nstdlog.Printf(\"Failed to make bake request, %s\", fmt.Errorf(\"temperature is too low\"))\n// ERROR http: Failed to make bake request, temperature is too low\n```\n\n## Gum\n\n\u003cimg src=\"https://vhs.charm.sh/vhs-6jupuFM0s2fXiUrBE0I1vU.gif\" width=\"600\" alt=\"Running gum log with debug and error levels\" /\u003e\n\nLog integrates with [Gum][gum] to log messages to output. Use `gum log [flags]\n[message]` to handle logging in your shell scripts. See\n[charmbracelet/gum](https://github.com/charmbracelet/gum#log) for more\ninformation.\n\n[gum]: https://github.com/charmbracelet/gum\n[lipgloss]: https://github.com/charmbracelet/lipgloss\n[stdlog]: https://pkg.go.dev/log\n\n## License\n\n[MIT](https://github.com/charmbracelet/log/raw/master/LICENSE)\n\n---\n\nPart of [Charm](https://charm.sh).\n\n\u003ca href=\"https://charm.sh/\"\u003e\u003cimg alt=\"the Charm logo\" src=\"https://stuff.charm.sh/charm-badge.jpg\" width=\"400\"\u003e\u003c/a\u003e\n\nCharm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharmbracelet%2Flog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcharmbracelet%2Flog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcharmbracelet%2Flog/lists"}