-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Accept context
when logging.
#116
Comments
I guess adding a |
The alternative is to add Another idea: But the problem in all of these cases is that all log calls must be updated. Do you control the entire code base? What about third-party code that accepts a Logger but then emits log messages with the existing The portable solution would be to do a bit more work when adding these values to the context:
|
I would assume that the Passing a context for every single log call does not really make sense and makes the API uglier. It would also require updating every single call site to pass a context explicitly, or the It is possible to construct a Logger with the request context for every request, but then every part of the code that handles a new request needs to mess with creating loggers, instead of just defining this once in the main package. |
I just realized that in order to pass the proper |
Is that sufficiently different from the already supported solution via |
I would prefer to add "InfoC and ErrorC", and here is the reason:
|
|
It is not ok for me, see an example code, written for Zap but applies for this API as well:
How would you refactor this App with "WithValue" |
Let's also consider how such code would be used and how it would interact with other code that does logging. Presumably these component get called in a for loop where requests are generated and spans are added, something like this:
This leads to my first question: how is logging of the context values expected to be handled? Is the expectation that the program uses a logger implementation that knows about tracing spans? That adds additional dependencies to logging implementations that not everyone needs or limits the choice of a logging implementation when this feature is needed. Or is the tracing library also providing the logger? A full implementation or a wrapper for some other logger? |
Let's also assume that
With the proposed logging of additional context values via InfoC,
Agreed or would you solve this differently? |
So here's how I would address this problem:
You chose to use Regarding the handling of tracing IDs, I see the following advantages of this approach:
The disadvantage is the overhead caused by more |
After giving it some more thought, I agree that explicitly passing any trace and span IDs as values would be the best use of the interface. Binding a new context after every new span would be error prone and non-obvious. I cannot think of any real use for methods like |
I will try to summarize here:
I can think of a logger implementation that allows users to config an "enhancer/hook" like
More about why passing the "logger" via Context is an anti-pattern. See https://pkg.go.dev/context:
As the official documentation says we should not pass arguments via Context, as well as we should only pass "request-scoped" data, the "logger" is not a request-scoped data (As a joke: this trick of passing the logger via Context started from the idea of "using global instances is bad" let's add it to the Context :)), but what do you add to the Context is still a global). The main idea is that "logger" should not be passed in the Context, and instead other "request scoped" data should be which sometimes you want to log them. |
Here I disagree. "Use this logger for this request" very much is request-scoped information and enables the caller to choose additional names and values for the request. |
What about then "use this logger for this component" part? Which one is more common? you cannot have both. Traditionally logger was "per component" to allow finer control at component level. |
My point being you cannot have both in an application, so I think there is a need to pick one winner and facilitate the other via API. Imagine that I want different logging level for ComponentA than ComponentB, or to have different instances of loggers etc. then this is limited because I have to use that logger for the entire request. |
That's true. I can see some value in having per-component loggers with different names because it's a better explanation of where a message was generated than the source file name. But for the kube-scheduler case that I have been looking at it's more important to have per-request values attached to each log message. Using the name of the scheduler framework plugin also seems more useful to me than "framework" or "client-go" (= components called by the plugins) because it provides a much better context for the individual log message ("while processing pod xyz with plugin foo, this happened"). For Kubernetes, I intend to propose a KEP in the next development cycle about "contextual logging" that will have more use cases and details. No changes will be needed in go-logr for that.
How would a user configure that? Which program allows fine-grained control over logger configuration for individual program modules? In klog, there is If different log levels are desired, are they for modules ("I am debugging client-go") or for context ("I am focusing on a specific request with certain values", or "I am interested in a certain scheduler plugin and anything it invokes, including client-go")? IMHO both are valid approaches and thus cannot be used to pick one logger setup over the other. |
You can have extra "request specific data" like what opentelemetry/w3c calls baggage (see https://www.w3.org/TR/baggage and https://github.com/open-telemetry/opentelemetry-go/blob/main/baggage/baggage.go), essentially [key, values] pairs associated with the Context and one of them can be "plugin" and with same "hook/enhancer" mechanism you can extract tracing data like span_id as well as other infos like "plugin" from other context entry. Also that "plugin" entry from the Context baggage may be used by other telemetry signals like a label/dimension in metrics recorded by one of the downstream component. In my opinion that is the "request specific data" the fact that the caller plugin is Foo. Another use-case that you need to keep in mind is that by passing the context you can have applications that use a "global" logger like in the k8s example you gave to me, and if they pass the Context they can also get context data to log entries.
I am not sure why that is slow, but I think a pretty optimal implementation can be done by having a Logger instance that supports a sampler and every time you do Logger.With("component", "foo") it can say if Logger has attribute "component = foo" sample 1/1000. Or any other attribute the application owner decides. ** EDIT: ** |
It has to do source-code lookups for each call site before even deciding about whether the log message should be logged. There's a cache, so perhaps it's okay. I don't have hard data.
The same can be done when names get added along the call path (my scheduler example). That and configuration by values seem useful.
Glad to hear that and thanks for the offer. I'll link it here when it's ready. |
Yes, so to summarize your proposed solution (will work):
|
I intend to have a global logger, but only as fallback. A library that supports retrieving a logger from context then still works fine inside applications which only initialize a global logger.
The gRPC case probably can be handled once for all calls by an interceptor which extracts the third-party data and updates the logger. Then neither gRPC nor the logger implementation need to know about each other. It's the application developer who integrates them. |
I can give you other examples where libraries add things to the Context and do not offer a "interceptor" mechanism. |
I do not want to add I'd probably reach for bespoke wrappers here. Either write your own:
or make your own Logger type which embeds I could MAYBE be talked into something like:
The point being simplicity - register any number of datum-extractors (after But I'd want to see why wrapping doesn't work? |
@bogdandrutu: I have published a PR with the KEP and intend to discuss it at this week's SIG Instrumentation meeting: kubernetes/enhancements#3078 The first step will be to decide whether the SIG wants to tackle this problem. Under "Alternatives" I am summarizing the "per component logger" approach and refer to this issue here for a more thorough discussion. |
Closingfor now - if there's new info or something, I'd love to hear it. |
Is it possible to extend the API to accept
context
when logging? The reason is that we want to extract information from the context like opentelemetry trace ids (trace_id/span_id) or other information that we propagate in the app using thecontext
.The text was updated successfully, but these errors were encountered: