-
Notifications
You must be signed in to change notification settings - Fork 298
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
add source for log messages #135
Conversation
Do we want to provide a default factory? E.g. |
Checking this out today in a project; I have some concerns but let's see how it goes. |
66b6c92
to
e2889a2
Compare
@inlinable | ||
public func debug(_ message: @autoclosure () -> Logger.Message, | ||
metadata: @autoclosure () -> Logger.Metadata? = nil, | ||
source: @autoclosure () -> String? = nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's some copy/paste leftover: we use self.source
, and not the one provided here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for all other methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These signatures are wrong in one or another way I guess indeed:
a) We want to keep exact same method signatures as Logger:
- so we keep
source: @autoclosure () -> String? = nil,
- impl has to
source: source() ?? self.source
- it must allow `sourcelogger.info("", source: "actually-more-specific-ad-hoc")
- if we want to keep this parameter here indeed
or
b) Like Anton mentions, this parameter should go away on all functions on LoggerWithSource
b) seems to make more sense, as Anton says. Either's fine tbh, but the current impl is not consistent with what the signature advertises we're able to do with these functions (override the source anyway)
public func trace(_ message: @autoclosure () -> Logger.Message, | ||
metadata: @autoclosure () -> Logger.Metadata? = nil, | ||
file: String = #file, function: String = #function, line: UInt = #line) { | ||
self.logger.trace(message(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure about this, but I think LoggerWithSource.trace
should delegate to LoggerWithSource.log
, and not to Logger.trace
. Otherwise LoggerWithSource
seems inconsistent with Logger
, and the reason for LoggerWithSource.log
is unclear. WDYT? Same for other methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@avolokhov From the generated code's perspective this is indistinguishable so we can change at any point in time.
The reason I did add log
with LoggerWithSource
is that some people may be using log(level: .trace, ...)
instead of .trace
and I want it to be compatible.
Quoting @weissi from #133 (comment)
That's a good point, it actually works out well there 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried it out "in action" and do think it'll work well enough 👍
Glad we talked through the use-cases and I think this now can serve all patterns.
I'm a bit worried about the string parsing module workaround... but otherwise looks good; just minor comments on API consistency and some docs 👍
Tests/LoggingTests/LoggingTest.swift
Outdated
logger.critical("yes: critical") | ||
|
||
testLogging.history.assertExist(level: .critical, message: "yes: critical", source: "source") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@inlinable | ||
public func debug(_ message: @autoclosure () -> Logger.Message, | ||
metadata: @autoclosure () -> Logger.Metadata? = nil, | ||
source: @autoclosure () -> String? = nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These signatures are wrong in one or another way I guess indeed:
a) We want to keep exact same method signatures as Logger:
- so we keep
source: @autoclosure () -> String? = nil,
- impl has to
source: source() ?? self.source
- it must allow `sourcelogger.info("", source: "actually-more-specific-ad-hoc")
- if we want to keep this parameter here indeed
or
b) Like Anton mentions, this parameter should go away on all functions on LoggerWithSource
b) seems to make more sense, as Anton says. Either's fine tbh, but the current impl is not consistent with what the signature advertises we're able to do with these functions (override the source anyway)
@ktoso To this point, wouldn't it make |
Well, it's unchangable. So if you implemented metadata swapping, the label would not be swapped. Also we don't have required metadata fields right now. But I agree it's kinda similar. |
I'm a bit worried that |
Yes, we can't enforce this at this point in time anymore. |
I'm not sure that's the answer either, there's much to say for "I really don't care, I'm just a simple app" use cases where the default We discussed options at length with Johannes but let me note down some of them here and why they break down and we're left with a source thing. Strawman 1: What if... we allowed changing labelOne could argue that "use label, make it changeable" which i think betrays intentions though, since Johannes wants to "create one logger and pass it to sub-systems" which e.g. would be one such library which has strong opinions about subsytems / sources. If we just said, "just allow to change the source" the following API is kind of lying IMHO:
so... when users passed AwesomeApp, they hare the reasonable expectation this label will show up in logs and can grep or search by it. But... if label is changeable they couldn't -- since that's the "one spot" for log sources... That's very bad / confusing. So... making label changeable is not an answer. ❌ Strawman 2: label "IS" sourceTo be honest that's how I've seen it, but it requires libraries which have strong opinions about "what is logging right now" to create loggers in the library. In order to avoid the global factory and allow swapping impls per library instance (that's a hard requirement IMO), one has to in the library then set a This works if configuration suits people; and while it suits me, there are others which really like the "create a root logger and pass it into libraries" If we did this with the "label is the source of the entity that is logging" then it breaks down -- label can't be changed. ❌ Strawman 3: Just metadataAlso an option I guess... though we can't do the "please DO set a source" maybe it's harder to get right than the type at all... It would be better (?) in the sense that it does not cause another type to be created for the "i know the source" cases. Though on the other hand when I ported my work to use this WIP PR the "I know i have a specific source" type was useful during library development... Though I kind of expect few developers to care about this? I do imagine it matter a lot through in very concurrent settings where one wants to log "which threadpool" or whatnot is performing the actions, so the log would contain these as sources perhaps... All in all, it's helpful if source is a "real" thing we talk about, and not just a pattern. What convienced me was Johannes's point recently
❌ Strawman 4: "never use the create 1 root logger and pass it into libraries"For the record that was my initial stance: all libs would have to make customization points for log level, "root" metadata, and "mocking" a logger out... So libraries have to expose While I trust libraries we build could get this right and maybe even consistent if we try hard enough, it's a lot of specific things a lib has to expose, and it defeats a pattern some of us like (the "pass rootLogger into library". And I do agree it'd probably never happen that "random libraries" use the same specific pattern and ways to configure this...At-best they might develop the same customization points, but all in slightly different syles and names... or at worst ignore this completely ("global state? what's the problem!" :P) and we're back in global state land making logging when many instances of a lib in one process exist difficult. ❌ So... we ended up with "what if we add source" which is the library controlled "source" meaning "the entity that is logging". It's default being the module is good enough and kind of the right thing always for small libraries, and larger libraries with subsystems and background tasks, thread pools etc can control if it they want to. It's in the control of library authors, but really that's what we're after here -- the user controlled label "MyCoolSystem" is "label", and "source" is up to library internals. I.e. no "top level logger" should ever be the Uff, so yeah, that's pretty much a summary of the various takes we had on this I think. I've had my reservations but after many chats and also trying it out in action now I guess it's an okey tradeoff. It does still mean setting that then passing "between" / "through" libraries the source is not really propagated... it's always the lib's "own" sources we'll see. So for execution contexts/thread pools I guess one has to set the metadata anyway always after all 🤔 Otherwise I would not know that I'm on pool X, and executing thingy library Y, since it accepted So... perhaps source is not solving the problem after all, or is it...? 🤔 |
Oh, I think we should emphasise that |
Yeah good point, likely we should document that hint. |
Minor additional point I just noticed, I seem to do a lot of log.logger.withSource("serialization") where |
2ba7a52
to
65d928b
Compare
So in general this looks good to me. I remain worried about the "faking that we have #module" esp with longer module names... Will try to sanity check this soon. Do you want to merge this soon or would we want to know what way forward wrt #module (or similar) is? Context: I have plenty of logs on .trace level and I'm worried that that source workaround would actually allocate/slow-down runtime a lot in my lib. What if a real thing never lands etc... So I'd like to sanity check that before LGTMing. (or some benchmark in here etc) |
Minor, what about: #135 (comment) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized my main concern was not necessary in #135 (comment) thanks again @weissi
LGTM as far as I can tell 👍
@ktoso / @tomerd API digester is wrong: https://bugs.swift.org/browse/SR-11859 |
@avolokhov for some reason, Github's "request re-review" button doesn't seem to work for you so I'm @-ing you |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I accidentally requested changes on the very first iteration. Looks good!
Does the API check being wrong though mean we'll always have it fail when we merge this since it compares prev-release to current? In Akka (the MiMa tool) we had "ignore these checks" if we indeed "broke" but knew the change is ok for some reason: https://github.com/akka/akka/blob/master/akka-actor/src/main/mima-filters/2.5.2.backwards.excludes do we need a similar thing to keep the api-digester being always wrong and highlighting such things? |
@ktoso no, it'll only fail on this very PR. It always compares commit N vs. N+1. |
Okey, seems we're good then |
@tomerd mind having a look here |
thanks for putting this together @weissi As we discussed in the original pitch and proposal, there are many usage patterns for SwiftLog (propagation, local or global to name the main ones), and a key requirement from it is to be able use configuration [at the logger-backend level] to control logging verbosity of specific subsystems and libraries while troubleshooting. I think the combination of label + source is a good way to drive such configuration, even better than just label + metadata that we have now, so +1 on adding this. I do think it would be beneficial to better document this concept to help guide logging-backend authors to offer such configuration options. As t stands this concept is buried in a test utility which is not used in practice, so hard to find. With that said, I do not think we should expand the API surface area with cc @ktoso |
I agree with the sentiment, let's take it slow with adding the new type. I initially thought i'd need it but in reality only used it in 2 source files so it's not been as crucial as I thought initially, happy to make the wrapper myself. We can make a ticket explaining what it is in case people will bump into it and +1 that ticket. |
Will rebase and merge this one. The LoggerWithSource I'll pull out as proposal / ticket for now. The ticket with the |
Removed the new Once we release we'll need to write up guidance about implementing 1.3.0 "new handler function", backends can do this "whenever they're ready to" |
Motivation: - When `source:` was added in apple#135, the API was accidentally broken despite source compatability. Modifications: - Add back source-less API calls which defer to the decls with source. - Note we can't deprecate the calls we're adding back: `log.info("some message")` refers to the implementation we're adding back rather and would therefore require users to explicitly specify a source to suppress the deprecation warning message. Result: - API is no longer broken.
Motivation: - When `source:` was added in apple#135, the API was accidentally broken despite source compatability. Modifications: - Add back source-less API calls which defer to the decls with source. - Note we can't deprecate the calls we're adding back: `log.info("some message")` refers to the implementation we're adding back rather and would therefore require users to explicitly specify a source to suppress the deprecation warning message. Result: - API is no longer broken.
Motivation: - When `source:` was added in #135, the API was accidentally broken despite source compatability. Modifications: - Add back source-less API calls which defer to the decls with source. - Note we can't deprecate the calls we're adding back: `log.info("some message")` refers to the implementation we're adding back rather and would therefore require users to explicitly specify a source to suppress the deprecation warning message. Result: - API is no longer broken. Co-authored-by: Konrad `ktoso` Malawski <ktoso@apple.com>
Motivation:
We noticed that we need log source information, which is a stable and greppable identifier for where a log message originated from. But less precise than
file
/function
but about as or slightly more precise than the current module.Some folks used
label
for this purpose but instantiating new loggers everywhere just to get a good label means the following sub-optimal things:logLevel
for a called library not possible (unless extra parameter passed)label: "MyApp.ServiceA"
andlabel: "MyApp.ServiceB"
to separate messages originating from AHC.All of these things are solved and behave very nicely if you pass a "root logger" around to all sub-systems. The problem however is if you cannot use
label
as source information, where do you get source information from?Modification:
source
field for every log message that defaults to the calling module (currently parsing#file
until SR-12920 is resolved)LoggerWithSource
which wraps aLogger
always attaching a non-standard source. For example in AHC you may want to make aLoggerWithSource(obtainedLogger, source: "AHC.ConnectionPool")
for logs in the connection pool.Result:
Proper
source
identifier.