go-logr/logr: a minimal logging API for Go libraries
A simple logging interface for Go
At a glance
- What is it?
- go-logr/logr defines a small logging contract that separates code producing records from the backend writing them. It suits Go applications and libraries that need configurable structured logging without depending on one logging framework.
- Who is it for?
- Adopt go-logr/logr when a Go library or application needs a compact logging contract and the freedom to select or replace its backend. Do not treat it as a complete logging system because it does not write output itself.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 5 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why go-logr/logr separates logging producers from backends
go-logr/logr addresses dependency coupling in Go logging. Library code often needs to report events, but hard-coding a logging framework forces users of that library to accept the same framework or build an adapter. `logr.Logger` gives application and library authors a common API for emitting records, while `LogSink` gives logging framework implementers a lower-level interface for handling them.
That split supports a practical arrangement. An application selects an implementation near its entry point, creates a root `logr.Logger`, and passes it to the rest of the program. The logger can be stored in a struct, supplied to another package, or kept as a package-global variable when appropriate. Other packages do not need to know the backend name because they work against `logr.Logger`.
This makes the project different from a logging library such as an output formatter or logger implementation. The repository includes integrations associated with `funcr`, `testr`, `glogr`, `klogr`, `zapr`, `stdr`, `logrusr`, `genericr`, and `logfmtr`, among others. Its role is primarily the contract connecting those implementations to consumers. That makes it relevant to library maintainers, application developers, test authors, and implementers of logging backends.
The API deliberately limits its scope. According to the README, its two principal log types are informational messages and errors. Informational records can carry numerical verbosity, while error records are always logged and do not have a verbosity level. A project that needs several named severities or a complete configuration model should not assume that this narrow API satisfies those requirements by itself.
How Logger values and LogSink backends process records
The high-level `Logger` is passed by value. Its methods carry a message and optional key and value pairs. The backend does not receive an instruction to write a preformatted line. Instead, it receives the message and fields through the lower-level `LogSink` contract and decides how to represent them.
The README explains that stack unwinding belongs to the `LogSink`. Implementations can use the supplied depth or their own call-depth mechanisms to identify the caller. The API also includes `WithCallDepth` and `WithCallStackHelper`, unlike the Go `slog` comparison in the README. This is significant for libraries because a useful log record often needs the application or library call site, not merely the logging wrapper that forwarded the record.
`WithName` attaches a name to a logger, while `V` changes verbosity for informational records. The numerical convention is the reverse of many systems: values must be at least zero, and a higher value means less important information. Increasing verbosity therefore permits more detailed output. Error records remain exceptions because they are always logged.
Logr also defines `Marshaler` for generating a loggable value on demand. This avoids forcing a representation to be constructed when the chosen sink or verbosity setting will not need it. The repository contains discard, testing, `sloghandler`, and other implementation-facing files, but the README does not claim that every feature is required in every use case.
Creating the root logger and passing it into Go application code
The README does not provide installation commands for adding `github.com/go-logr/logr` to a project. It points readers to the package reference linked by the README and states that the application chooses a logging implementation early in its life.
The first use begins in `main()` or another composition root. The README uses `logimpl` as a placeholder for a chosen backend. Its constructor receives implementation-specific parameters and returns a `logr.Logger`.
func main() {
// ... other setup code ...
logger := logimpl.New(param1, param2)
// ... other setup code ...
}The reader should expect the root logger to be created after the application has selected its logging implementation. `logimpl.New` is an example name rather than a package documented by the repository.
The logger can then be passed into the application object. This keeps backend selection near setup while allowing the rest of the program to receive only the common API.
app := createTheAppObject(logger)
app.Run()No package involved after this point needs to know which implementation produced `logger`. That is the principal architectural benefit described by the README.
Emitting messages, timestamps, and contextual values with Logger
An application object stores the logger as a field and calls it without importing a concrete logging framework. This example comes directly from the README.
type appObject struct {
// ... other fields ...
logger logr.Logger
// ... other fields ...
}
func (app *appObject) Run() {
app.logger.Info("starting up", "timestamp", time.Now())
// ... app code ...
}The key and value arguments are structured data. The selected `LogSink` decides whether the eventual output resembles JSON, logfmt, text, or another supported representation. The producer does not control the destination or formatting through this call. That separation matters when the same library is used by applications with different logging requirements.
For component naming, `WithName` adds a name to a logger. For detailed informational output, `V` selects a numerical verbosity. The README gives higher `V` values a lower importance, so a sink configured to show only values above a chosen threshold will omit less important informational messages. Error records bypass this informational verbosity arrangement.
The API also supports context helpers, `NewContext` and `FromContext`. These functions allow logger values to travel through Go’s context mechanism. The README does not claim that every logr method accepts a context directly, which is one of the differences from the newer `slog` API.
Comparison with Go slog and the limits of logr’s feature set
The README positions logr and Go’s `slog` as different APIs that can coexist. It explicitly states that `slog` was intended to support multiple high-level APIs layered over a shared `slog.Handler`, while logr is one such alternative API. The repository includes conversion code and tests for interoperability, but the APIs are not identical.
`logr.Logger` is passed by value, while `slog.Logger` is passed by pointer in the comparison table. Logr assigns stack unwinding to `LogSink`; slog performs it in `Logger`. Logr provides `WithCallDepth` and `WithCallStackHelper`, which the table says slog’s `Logger` does not support. Logr also offers `NewContext`, `FromContext`, `WithName`, and verbosity changes through `V`. The table lists no corresponding slog methods for those features.
The direction is different in other areas. Slog supports grouping key and value pairs with `WithGroup` and `GroupValue`, while logr does not. Slog has context-aware variants such as `InfoCtx`, while logr has no equivalent method family. Their level conventions also differ. Logr levels are at least zero and higher values mean less important records. Slog levels are positive or negative, with zero representing info and higher values representing greater importance. Logr errors are always emitted, whereas slog treats errors as normal records at `LevelError` or above.
These differences make adapters necessary when porting between APIs. A team should not assume that every slog feature transfers directly through the documented conversions. Conversely, a library that needs named loggers, call-depth handling, context retrieval, or numerical verbosity may find features in logr that slog’s primary API does not expose.
Adoption constraints, backend choice, and maintenance obligations
The clearest wrong-tool case is a requirement for a standalone logging platform. Logr defers writing files, stdout, and other output to a `LogSink`, so adopting the API does not provide a backend, destination configuration, rotation policy, or formatting configuration. The application must select an implementation. The README’s implementation list includes function-based output through `funcr`, test output through `testr` and `ktesting`, and adapters associated with `glogr`, `klogr`, `zapr`, `stdr`, `logrusr`, `genericr`, and `logfmtr`.
The implementation list is labeled non-exhaustive. It also does not claim that every adapter has identical feature support. A backend may have its own level mapping, formatting choices, concurrency behavior, or context behavior, but those details cannot be confirmed from this repository’s README. They need to be checked in the selected implementation’s documentation.
For test output, `testr` is described as producing JSON-like output with `testing.T`, while `ktesting` is described as producing klog-like text. This illustrates why backend selection is part of application design rather than an incidental replacement. Changing from one implementation to another should keep producers on `logr.Logger`, but it does not guarantee identical output semantics.
The repository is not archived and the last push was on 2026-10-11. Release v1.4.4 was published on 2026-07-20. The supplied `go.mod` declares module path `github.com/go-logr/logr` and Go version `1.18`. Apache-2.0 licensing permits use and modification under that license’s terms, but license review for a specific distribution remains outside what this repository excerpt can answer.
The main upgrade cost is API review at integration points. Code that stores `logr.Logger`, creates named loggers, changes verbosity, marshals values, or retrieves loggers from contexts should be checked when moving across releases or swapping sinks. Code that only emits basic `Info` and `Error` records should have a smaller adaptation surface.
Editorial conclusion
Adopt go-logr/logr when a Go library or application needs a compact logging contract and the freedom to select or replace its backend. Do not treat it as a complete logging system because it does not write output itself. First verify the selected `LogSink`, the context behavior your packages require, and whether slog interoperability meets your application’s level and grouping needs.
Frequently asked questions
Is go-logr/logr a logging implementation?
No. It is a minimal logging API consisting primarily of `Logger` for producers and `LogSink` for logging framework implementers. Actual output is handled by a separate implementation.
How does go-logr/logr support structured logging?
The `Logger` API accepts messages with key and value pairs, as shown by `app.logger.Info("starting up", "timestamp", time.Now())`. The selected `LogSink` determines how those records are written and formatted.
Can go-logr/logr work with Go slog?
The repository includes slog-related source files and the README says interoperability is provided by conversion functions. The APIs are not identical, because the README documents differences in levels, grouping, context-aware methods, and helper-function support.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/go-logr-logr)Was this useful?