-
Notifications
You must be signed in to change notification settings - Fork 48
chore: use semver to determine API compatibility #6
Conversation
1bbed91
to
73bd4da
Compare
Codecov Report
@@ Coverage Diff @@
## main #6 +/- ##
==========================================
+ Coverage 89.82% 94.79% +4.97%
==========================================
Files 33 37 +4
Lines 452 519 +67
Branches 77 82 +5
==========================================
+ Hits 406 492 +86
+ Misses 46 27 -19
Continue to review full report at Codecov.
|
@MSNev the biggest changes here are related to the diag logger because it behaves differently than the other APIs so I would appreciated a peek |
@MSNev While trying to get together an implementation that would not allow changing log levels in the global logger, I came across the test titled edit: I resolved it by doing this. Can you please take a look? dyladan#1 |
The test is asserting that if you just create a new level of "X" but the user / application set the level higher globally AND you have created a filtered logger using the default (without specifying a logger), it will default to delegating the logging api.diag, which will then obey the global logging level request. And if you want to override the global level then you need to specify the specific logger, so you need to get the underlying (unfiltered logger) via This is documented explicitly in the TS doc of the createLogLevelDIagLogger() |
That's quite confusing to me. I would expect that if I called I preserved your behavior in dyladan#1 for now |
Yes, I agree. It came about from a comment from Bart where if the user has set the global logging level to NONE then he did not expect that we (by default) would override their level request. But we still had code (now removed as part of the config) that wanted to override and set directly. |
@obecny do you mind if we reverse this behavior? |
which comment ? |
I said:
Nev said:
Would you be ok if that was the behavior? |
@dyladan I would expect the behaviour you exactly described, if you pass some params to a function it should respect this params, otherwise it doesn't make sense to insert any params......, not sure why it would be different. But then @MSNev said that it is working differently because of my comment, so @MSNev which comment I did that it is working differently ??? |
I found the original comment and sorry @obecny I miss-attributed (and slightly miss-remembered) it to you when it was actually @vmarchaud on this comment. The comment was around having the "forcedInfo" and specifically this bit
open-telemetry/opentelemetry-js#1880 (comment) So while this is in relation to forceInfo(), I read the inferred the implicit log level request vs setting and recalled it as I mentioned above. |
@vmarchaud do you have an opinion on this? |
I was refering to the fact that we couldn't (from my understand of the proposal) silence "forcedInfo". I understand why it oppose with my previous comment, but i think we should never construct a diag logger in any sdk components, only users should use this, so we can't really have a situation where the logger is using a level that wasn't choosen by the user |
I TOTALLY agree with this, having the constructors / helpers is a "nice' to have for SDK / component developers during development ONLY. |
@@ -44,6 +44,7 @@ module.exports = { | |||
"files": ["test/**/*.ts"], | |||
"rules": { | |||
"no-empty": "off", | |||
"@typescript-eslint/ban-ts-comment": "off", |
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.
We need ts comments for asserting a type check will fail. This allows it in tests only
@MSNev I marked our previous conversation around globals as resolved because it was a long conversation largely about code that has been removed. If you still feel my use of globals is invalid please open a new comment thread. Otherwise I would appreciate another review now that we've done some iterating. |
@@ -95,28 +70,23 @@ export class DiagAPI implements DiagLogger { | |||
// DiagAPI specific functions | |||
|
|||
self.getLogger = (): DiagLogger => { | |||
// Return itself if no existing logger is defined (defaults effectively to a Noop) | |||
return _logger; | |||
return getGlobal('diag') || _noopLogger; |
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.
Thought: Do we even need this anymore?
Can we just have this as the getChildLogger(), as there is no need to call getLogger() to bypass the filtering (as it won't) and the DiagAPI is a logger so you don't need to call api.diag.getLogger().debug(...)
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.
Especially, as this getLogger() was previously just here to return the child logger.
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.
And this would mean that the usage on L56 would have to change.
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.
Do we need the diag API to understand the concept of filtering at all? We could just have the DiagLogger
interface, and require the user to construct a valid logger for their use case like diag.setLogger(logLevelFilteredLogger(level, console))
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.
The approach I was taking is that users and creators of loggers shouldn't need to worry about log level filtering as this should be controlled by the DiagAPI and any initialization configuration (as it should only be set once).
So I prefer what you have done in the setLogger registerGlobal('diag', createLogLevelDiagLogger(logLevel, logger), true)
as it continues to hide the complexity.
@@ -59,6 +59,10 @@ export interface DiagLogger { | |||
verbose: DiagLogFunction; | |||
} | |||
|
|||
export interface FilteredDiagLogger extends DiagLogger { |
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.
Question: Do we really need a new interface for this?
What about just adding this as an optional function, while it makes the usage a little more tricky we have to do this now if we don't already know (because of type enforcement) that this isn't a filtered logger. And the usages of this should be minimal anyway.
Would also suggest that if you want to keep it the name might be better as ParentDiagLogger
with it's function of getChildLogger
logLevel: DiagLogLevel = DiagLogLevel.INFO | ||
) => { | ||
logger = logger === self ? self.getLoggingDestination() : logger; | ||
registerGlobal('diag', createLogLevelDiagLogger(logLevel, logger), true); |
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.
Might be worth adding a comment that we are allowing the override of the default to both replacing any existing logger AND to avoid any possible endless loop (because of someone refactoring the code as the existing implementation doesn't have an issue) -- just so it make anyone touching the code in the future to think about that scenario before just blindly changing it.
@@ -50,8 +50,7 @@ describe('LogLevelFilter DiagLogger', () => { | |||
|
|||
beforeEach(() => { | |||
// Set no logger so that sinon doesn't complain about TypeError: Attempted to wrap xxxx which is already wrapped | |||
diag.setLogger(); | |||
diag.setLogLevel(DiagLogLevel.INFO); | |||
diag.disable(); |
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.
You may still need to reset the global log level as part of this as I found when running tests locally and in the CI environment that the order of execution can pollute other tests when they don't manager (or expect) the logging level to have changed.
But I think this is clearer than the side-effect version of setting the logger to undefined.
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.
Since the log level is no longer stored as a separate property but is just enclosed in the registered filtered logger, this drops the level as well.
src/internal/global-utils.ts
Outdated
if (!allowOverride && api[type]) { | ||
// already registered an API of this type | ||
diag.error( | ||
String( |
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.
Isn't this just a complicated variant of prepending Error:
?
If you want the call stack you should use new Error('...').stack
.
It seems also that this path is not covered by a test. I wonder where this log is sent to when it happens during registration of diag.
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.
Ah yes I needed stack thanks.
It seems also that this path is not covered by a test. I wonder where this log is sent to when it happens during registration of diag.
I will add a test. If this happens during the registration of diag, it will be sent to whichever diag was already registered. We could send to both diag instances which may in some cases result in duplicate logs but in other cases might prevent the log from being swallowed by incorrect diag use.
edit: diag is the only global allowed to be registered again, this is the allowOverride
parameter. This is done so that the log level or destination may be changed by the user in the event they are experiencing an issue. Do you think it would be more clear to remove this special case and require the user to explicitly call disable
if they want to reregister?
Maybe create a separate PR for the diag refactoring? At least for me it's hard to follow this PR here which says it's about semver checks but more or less all discussions and a large amount of the changes are about diagnostics. Please update PR description in first message at some allow people to get some hints what this is good for without the need to read through the old PR. |
Sorry, the diag refactoring was a requirement for this. I'll split it into another PR to make this one more directed. |
Fixes #5