{"id":13563355,"url":"https://github.com/go-logr/logr","last_synced_at":"2026-01-12T10:39:56.018Z","repository":{"id":38245278,"uuid":"79188507","full_name":"go-logr/logr","owner":"go-logr","description":"A simple logging interface for Go","archived":false,"fork":false,"pushed_at":"2024-10-28T07:24:27.000Z","size":473,"stargazers_count":1263,"open_issues_count":9,"forks_count":77,"subscribers_count":17,"default_branch":"master","last_synced_at":"2024-10-29T19:59:37.100Z","etag":null,"topics":["logger","logging"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/go-logr.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-17T04:32:26.000Z","updated_at":"2024-10-29T08:10:16.000Z","dependencies_parsed_at":"2024-01-11T09:29:24.495Z","dependency_job_id":"19f26466-2381-40d1-91ec-e351b11de560","html_url":"https://github.com/go-logr/logr","commit_stats":{"total_commits":284,"total_committers":27,"mean_commits":"10.518518518518519","dds":0.5704225352112676,"last_synced_commit":"8d6912c185148a6128c9b20cadfe9d620fe789a0"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-logr%2Flogr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-logr%2Flogr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-logr%2Flogr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/go-logr%2Flogr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/go-logr","download_url":"https://codeload.github.com/go-logr/logr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222296670,"owners_count":16962773,"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":["logger","logging"],"created_at":"2024-08-01T13:01:18.237Z","updated_at":"2026-01-12T10:39:55.990Z","avatar_url":"https://github.com/go-logr.png","language":"Go","funding_links":[],"categories":["Go","开源类库","Open source library"],"sub_categories":["日志","Logs"],"readme":"# A minimal logging API for Go\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/go-logr/logr.svg)](https://pkg.go.dev/github.com/go-logr/logr)\n[![Go Report Card](https://goreportcard.com/badge/github.com/go-logr/logr)](https://goreportcard.com/report/github.com/go-logr/logr)\n[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/go-logr/logr/badge)](https://securityscorecards.dev/viewer/?platform=github.com\u0026org=go-logr\u0026repo=logr)\n\nlogr offers an(other) opinion on how Go programs and libraries can do logging\nwithout becoming coupled to a particular logging implementation.  This is not\nan implementation of logging - it is an API.  In fact it is two APIs with two\ndifferent sets of users.\n\nThe `Logger` type is intended for application and library authors.  It provides\na relatively small API which can be used everywhere you want to emit logs.  It\ndefers the actual act of writing logs (to files, to stdout, or whatever) to the\n`LogSink` interface.\n\nThe `LogSink` interface is intended for logging library implementers.  It is a\npure interface which can be implemented by logging frameworks to provide the actual logging\nfunctionality.\n\nThis decoupling allows application and library developers to write code in\nterms of `logr.Logger` (which has very low dependency fan-out) while the\nimplementation of logging is managed \"up stack\" (e.g. in or near `main()`.)\nApplication developers can then switch out implementations as necessary.\n\nMany people assert that libraries should not be logging, and as such efforts\nlike this are pointless.  Those people are welcome to convince the authors of\nthe tens-of-thousands of libraries that *DO* write logs that they are all\nwrong.  In the meantime, logr takes a more practical approach.\n\n## Typical usage\n\nSomewhere, early in an application's life, it will make a decision about which\nlogging library (implementation) it actually wants to use.  Something like:\n\n```\n    func main() {\n        // ... other setup code ...\n\n        // Create the \"root\" logger.  We have chosen the \"logimpl\" implementation,\n        // which takes some initial parameters and returns a logr.Logger.\n        logger := logimpl.New(param1, param2)\n\n        // ... other setup code ...\n```\n\nMost apps will call into other libraries, create structures to govern the flow,\netc.  The `logr.Logger` object can be passed to these other libraries, stored\nin structs, or even used as a package-global variable, if needed.  For example:\n\n```\n    app := createTheAppObject(logger)\n    app.Run()\n```\n\nOutside of this early setup, no other packages need to know about the choice of\nimplementation.  They write logs in terms of the `logr.Logger` that they\nreceived:\n\n```\n    type appObject struct {\n        // ... other fields ...\n        logger logr.Logger\n        // ... other fields ...\n    }\n\n    func (app *appObject) Run() {\n        app.logger.Info(\"starting up\", \"timestamp\", time.Now())\n\n        // ... app code ...\n```\n\n## Background\n\nIf the Go standard library had defined an interface for logging, this project\nprobably would not be needed.  Alas, here we are.\n\nWhen the Go developers started developing such an interface with\n[slog](https://github.com/golang/go/issues/56345), they adopted some of the\nlogr design but also left out some parts and changed others:\n\n| Feature | logr | slog |\n|---------|------|------|\n| High-level API | `Logger` (passed by value) | `Logger` (passed by [pointer](https://github.com/golang/go/issues/59126)) |\n| Low-level API | `LogSink` | `Handler` |\n| Stack unwinding | done by `LogSink` | done by `Logger` |\n| Skipping helper functions | `WithCallDepth`, `WithCallStackHelper` | [not supported by Logger](https://github.com/golang/go/issues/59145) |\n| Generating a value for logging on demand | `Marshaler` | `LogValuer` |\n| Log levels | \u003e= 0, higher meaning \"less important\" | positive and negative, with 0 for \"info\" and higher meaning \"more important\" |\n| Error log entries | always logged, don't have a verbosity level | normal log entries with level \u003e= `LevelError` |\n| Passing logger via context | `NewContext`, `FromContext` | no API |\n| Adding a name to a logger | `WithName` | no API |\n| Modify verbosity of log entries in a call chain | `V` | no API |\n| Grouping of key/value pairs | not supported | `WithGroup`, `GroupValue` |\n| Pass context for extracting additional values | no API | API variants like `InfoCtx` |\n\nThe high-level slog API is explicitly meant to be one of many different APIs\nthat can be layered on top of a shared `slog.Handler`. logr is one such\nalternative API, with [interoperability](#slog-interoperability) provided by\nsome conversion functions.\n\n### Inspiration\n\nBefore you consider this package, please read [this blog post by the\ninimitable Dave Cheney][warning-makes-no-sense].  We really appreciate what\nhe has to say, and it largely aligns with our own experiences.\n\n### Differences from Dave's ideas\n\nThe main differences are:\n\n1. Dave basically proposes doing away with the notion of a logging API in favor\nof `fmt.Printf()`.  We disagree, especially when you consider things like output\nlocations, timestamps, file and line decorations, and structured logging.  This\npackage restricts the logging API to just 2 types of logs: info and error.\n\nInfo logs are things you want to tell the user which are not errors.  Error\nlogs are, well, errors.  If your code receives an `error` from a subordinate\nfunction call and is logging that `error` *and not returning it*, use error\nlogs.\n\n2. Verbosity-levels on info logs.  This gives developers a chance to indicate\narbitrary grades of importance for info logs, without assigning names with\nsemantic meaning such as \"warning\", \"trace\", and \"debug.\"  Superficially this\nmay feel very similar, but the primary difference is the lack of semantics.\nBecause verbosity is a numerical value, it's safe to assume that an app running\nwith higher verbosity means more (and less important) logs will be generated.\n\n## Implementations (non-exhaustive)\n\nThere are implementations for the following logging libraries:\n\n- **a function** (can bridge to non-structured libraries): [funcr](https://github.com/go-logr/logr/tree/master/funcr)\n- **a testing.T** (for use in Go tests, with JSON-like output): [testr](https://github.com/go-logr/logr/tree/master/testr)\n- **github.com/google/glog**: [glogr](https://github.com/go-logr/glogr)\n- **k8s.io/klog** (for Kubernetes): [klogr](https://git.k8s.io/klog/klogr)\n- **a testing.T** (with klog-like text output): [ktesting](https://git.k8s.io/klog/ktesting)\n- **go.uber.org/zap**: [zapr](https://github.com/go-logr/zapr)\n- **log** (the Go standard library logger): [stdr](https://github.com/go-logr/stdr)\n- **github.com/sirupsen/logrus**: [logrusr](https://github.com/bombsimon/logrusr)\n- **github.com/wojas/genericr**: [genericr](https://github.com/wojas/genericr) (makes it easy to implement your own backend)\n- **logfmt** (Heroku style [logging](https://www.brandur.org/logfmt)): [logfmtr](https://github.com/iand/logfmtr)\n- **github.com/rs/zerolog**: [zerologr](https://github.com/go-logr/zerologr)\n- **github.com/go-kit/log**: [gokitlogr](https://github.com/tonglil/gokitlogr) (also compatible with github.com/go-kit/kit/log since v0.12.0)\n- **bytes.Buffer** (writing to a buffer): [bufrlogr](https://github.com/tonglil/buflogr) (useful for ensuring values were logged, like during testing)\n\n## slog interoperability\n\nInteroperability goes both ways, using the `logr.Logger` API with a `slog.Handler`\nand using the `slog.Logger` API with a `logr.LogSink`. `FromSlogHandler` and\n`ToSlogHandler` convert between a `logr.Logger` and a `slog.Handler`.\nAs usual, `slog.New` can be used to wrap such a `slog.Handler` in the high-level\nslog API.\n\n### Using a `logr.LogSink` as backend for slog\n\nIdeally, a logr sink implementation should support both logr and slog by\nimplementing both the normal logr interface(s) and `SlogSink`.  Because\nof a conflict in the parameters of the common `Enabled` method, it is [not\npossible to implement both slog.Handler and logr.Sink in the same\ntype](https://github.com/golang/go/issues/59110).\n\nIf both are supported, log calls can go from the high-level APIs to the backend\nwithout the need to convert parameters. `FromSlogHandler` and `ToSlogHandler` can\nconvert back and forth without adding additional wrappers, with one exception:\nwhen `Logger.V` was used to adjust the verbosity for a `slog.Handler`, then\n`ToSlogHandler` has to use a wrapper which adjusts the verbosity for future\nlog calls.\n\nSuch an implementation should also support values that implement specific\ninterfaces from both packages for logging (`logr.Marshaler`, `slog.LogValuer`,\n`slog.GroupValue`). logr does not convert those.\n\nNot supporting slog has several drawbacks:\n- Recording source code locations works correctly if the handler gets called\n  through `slog.Logger`, but may be wrong in other cases. That's because a\n  `logr.Sink` does its own stack unwinding instead of using the program counter\n  provided by the high-level API.\n- slog levels \u003c= 0 can be mapped to logr levels by negating the level without a\n  loss of information. But all slog levels \u003e 0 (e.g. `slog.LevelWarning` as\n  used by `slog.Logger.Warn`) must be mapped to 0 before calling the sink\n  because logr does not support \"more important than info\" levels.\n- The slog group concept is supported by prefixing each key in a key/value\n  pair with the group names, separated by a dot. For structured output like\n  JSON it would be better to group the key/value pairs inside an object.\n- Special slog values and interfaces don't work as expected.\n- The overhead is likely to be higher.\n\nThese drawbacks are severe enough that applications using a mixture of slog and\nlogr should switch to a different backend.\n\n### Using a `slog.Handler` as backend for logr\n\nUsing a plain `slog.Handler` without support for logr works better than the\nother direction:\n- All logr verbosity levels can be mapped 1:1 to their corresponding slog level\n  by negating them.\n- Stack unwinding is done by the `SlogSink` and the resulting program\n  counter is passed to the `slog.Handler`.\n- Names added via `Logger.WithName` are gathered and recorded in an additional\n  attribute with `logger` as key and the names separated by slash as value.\n- `Logger.Error` is turned into a log record with `slog.LevelError` as level\n  and an additional attribute with `err` as key, if an error was provided.\n\nThe main drawback is that `logr.Marshaler` will not be supported. Types should\nideally support both `logr.Marshaler` and `slog.Valuer`. If compatibility\nwith logr implementations without slog support is not important, then\n`slog.Valuer` is sufficient.\n\n### Context support for slog\n\nStoring a logger in a `context.Context` is not supported by\nslog. `NewContextWithSlogLogger` and `FromContextAsSlogLogger` can be\nused to fill this gap. They store and retrieve a `slog.Logger` pointer\nunder the same context key that is also used by `NewContext` and\n`FromContext` for `logr.Logger` value.\n\nWhen `NewContextWithSlogLogger` is followed by `FromContext`, the latter will\nautomatically convert the `slog.Logger` to a\n`logr.Logger`. `FromContextAsSlogLogger` does the same for the other direction.\n\nWith this approach, binaries which use either slog or logr are as efficient as\npossible with no unnecessary allocations. This is also why the API stores a\n`slog.Logger` pointer: when storing a `slog.Handler`, creating a `slog.Logger`\non retrieval would need to allocate one.\n\nThe downside is that switching back and forth needs more allocations. Because\nlogr is the API that is already in use by different packages, in particular\nKubernetes, the recommendation is to use the `logr.Logger` API in code which\nuses contextual logging.\n\nAn alternative to adding values to a logger and storing that logger in the\ncontext is to store the values in the context and to configure a logging\nbackend to extract those values when emitting log entries. This only works when\nlog calls are passed the context, which is not supported by the logr API.\n\nWith the slog API, it is possible, but not\nrequired. https://github.com/veqryn/slog-context is a package for slog which\nprovides additional support code for this approach. It also contains wrappers\nfor the context functions in logr, so developers who prefer to not use the logr\nAPIs directly can use those instead and the resulting code will still be\ninteroperable with logr.\n\n## FAQ\n\n### Conceptual\n\n#### Why structured logging?\n\n- **Structured logs are more easily queryable**: Since you've got\n  key-value pairs, it's much easier to query your structured logs for\n  particular values by filtering on the contents of a particular key --\n  think searching request logs for error codes, Kubernetes reconcilers for\n  the name and namespace of the reconciled object, etc.\n\n- **Structured logging makes it easier to have cross-referenceable logs**:\n  Similarly to searchability, if you maintain conventions around your\n  keys, it becomes easy to gather all log lines related to a particular\n  concept.\n\n- **Structured logs allow better dimensions of filtering**: if you have\n  structure to your logs, you've got more precise control over how much\n  information is logged -- you might choose in a particular configuration\n  to log certain keys but not others, only log lines where a certain key\n  matches a certain value, etc., instead of just having v-levels and names\n  to key off of.\n\n- **Structured logs better represent structured data**: sometimes, the\n  data that you want to log is inherently structured (think tuple-link\n  objects.)  Structured logs allow you to preserve that structure when\n  outputting.\n\n#### Why V-levels?\n\n**V-levels give operators an easy way to control the chattiness of log\noperations**.  V-levels provide a way for a given package to distinguish\nthe relative importance or verbosity of a given log message.  Then, if\na particular logger or package is logging too many messages, the user\nof the package can simply change the v-levels for that library.\n\n#### Why not named levels, like Info/Warning/Error?\n\nRead [Dave Cheney's post][warning-makes-no-sense].  Then read [Differences\nfrom Dave's ideas](#differences-from-daves-ideas).\n\n#### Why not allow format strings, too?\n\n**Format strings negate many of the benefits of structured logs**:\n\n- They're not easily searchable without resorting to fuzzy searching,\n  regular expressions, etc.\n\n- They don't store structured data well, since contents are flattened into\n  a string.\n\n- They're not cross-referenceable.\n\n- They don't compress easily, since the message is not constant.\n\n(Unless you turn positional parameters into key-value pairs with numerical\nkeys, at which point you've gotten key-value logging with meaningless\nkeys.)\n\n### Practical\n\n#### Why key-value pairs, and not a map?\n\nKey-value pairs are *much* easier to optimize, especially around\nallocations.  Zap (a structured logger that inspired logr's interface) has\n[performance measurements](https://github.com/uber-go/zap#performance)\nthat show this quite nicely.\n\nWhile the interface ends up being a little less obvious, you get\npotentially better performance, plus avoid making users type\n`map[string]string{}` every time they want to log.\n\n#### What if my V-levels differ between libraries?\n\nThat's fine.  Control your V-levels on a per-logger basis, and use the\n`WithName` method to pass different loggers to different libraries.\n\nGenerally, you should take care to ensure that you have relatively\nconsistent V-levels within a given logger, however, as this makes deciding\non what verbosity of logs to request easier.\n\n#### But I really want to use a format string!\n\nThat's not actually a question.  Assuming your question is \"how do\nI convert my mental model of logging with format strings to logging with\nconstant messages\":\n\n1. Figure out what the error actually is, as you'd write in a TL;DR style,\n   and use that as a message.\n\n2. For every place you'd write a format specifier, look to the word before\n   it, and add that as a key value pair.\n\nFor instance, consider the following examples (all taken from spots in the\nKubernetes codebase):\n\n- `klog.V(4).Infof(\"Client is returning errors: code %v, error %v\",\n  responseCode, err)` becomes `logger.Error(err, \"client returned an\n  error\", \"code\", responseCode)`\n\n- `klog.V(4).Infof(\"Got a Retry-After %ds response for attempt %d to %v\",\n  seconds, retries, url)` becomes `logger.V(4).Info(\"got a retry-after\n  response when requesting url\", \"attempt\", retries, \"after\n  seconds\", seconds, \"url\", url)`\n\nIf you *really* must use a format string, use it in a key's value, and\ncall `fmt.Sprintf` yourself.  For instance: `log.Printf(\"unable to\nreflect over type %T\")` becomes `logger.Info(\"unable to reflect over\ntype\", \"type\", fmt.Sprintf(\"%T\"))`.  In general though, the cases where\nthis is necessary should be few and far between.\n\n#### How do I choose my V-levels?\n\nThis is basically the only hard constraint: increase V-levels to denote\nmore verbose or more debug-y logs.\n\nOtherwise, you can start out with `0` as \"you always want to see this\",\n`1` as \"common logging that you might *possibly* want to turn off\", and\n`10` as \"I would like to performance-test your log collection stack.\"\n\nThen gradually choose levels in between as you need them, working your way\ndown from 10 (for debug and trace style logs) and up from 1 (for chattier\ninfo-type logs). For reference, slog pre-defines -4 for debug logs\n(corresponds to 4 in logr), which matches what is\n[recommended for Kubernetes](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md#what-method-to-use).\n\n#### How do I choose my keys?\n\nKeys are fairly flexible, and can hold more or less any string\nvalue. For best compatibility with implementations and consistency\nwith existing code in other projects, there are a few conventions you\nshould consider.\n\n- Make your keys human-readable.\n- Constant keys are generally a good idea.\n- Be consistent across your codebase.\n- Keys should naturally match parts of the message string.\n- Use lower case for simple keys and\n  [lowerCamelCase](https://en.wiktionary.org/wiki/lowerCamelCase) for\n  more complex ones. Kubernetes is one example of a project that has\n  [adopted that\n  convention](https://github.com/kubernetes/community/blob/HEAD/contributors/devel/sig-instrumentation/migration-to-structured-logging.md#name-arguments).\n\nWhile key names are mostly unrestricted (and spaces are acceptable),\nit's generally a good idea to stick to printable ascii characters, or at\nleast match the general character set of your log lines.\n\n#### Why should keys be constant values?\n\nThe point of structured logging is to make later log processing easier.  Your\nkeys are, effectively, the schema of each log message.  If you use different\nkeys across instances of the same log line, you will make your structured logs\nmuch harder to use.  `Sprintf()` is for values, not for keys!\n\n#### Why is this not a pure interface?\n\nThe Logger type is implemented as a struct in order to allow the Go compiler to\noptimize things like high-V `Info` logs that are not triggered.  Not all of\nthese implementations are implemented yet, but this structure was suggested as\na way to ensure they *can* be implemented.  All of the real work is behind the\n`LogSink` interface.\n\n[warning-makes-no-sense]: http://dave.cheney.net/2015/11/05/lets-talk-about-logging\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-logr%2Flogr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgo-logr%2Flogr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgo-logr%2Flogr/lists"}