-
Notifications
You must be signed in to change notification settings - Fork 79
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
Rearch for performance: BREAKING CHANGES #42
Conversation
42bb35d
to
ecb1e88
Compare
It's been a crazy few months. If you (or others) have time to look at this (specifically the last commit) it would be super helpful |
I will have a look at it. This is a rather big breaking change, I need some time to digest it. |
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 need some more time to digest the implications.
I like that the change is mostly transparent to log callers, it just affects backends and log initialization code.
The concrete type also provides more possibilities for extending the interface in the future. For example, when adding a new method like WithFoo
:
- The
LogSink
interface could be kept the same to not break existing backends - The
Logger
concrete type can safely add the new receiver - This new function would test if the sink implements the new interface and provide some sensible fallback behavior for older backends.
logr.go
Outdated
// flags might be used to set the logging verbosity and disable some info | ||
// logs. | ||
Enabled() bool | ||
Enabled(level int) bool |
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.
At this moment it's only the level, but perhaps we would like to pull some other information into the concrete Logger
in the future, which would require breaking changes to pass it to the Logger
.
One alternative would be to pass it in a struct:
type LogContext struct {
level int
}
And then change the interface to:
Enabled(logctx LogContext) bool
Info(logctx LogContext, msg string, keysAndValues ...interface{})
...
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.
Perhaps this LogContext
can also contain the number of stack frames added by the log stack. This would allow the creation of middleware-like log sinks that delegate to other sinks, while keeping the call stack count correct.
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.
Perhaps this LogContext can also contain the number of stack frames added by the log stack. This would allow the creation of middleware-like log sinks that delegate to other sinks, while keeping the call stack count correct.
This is what WithCallDepth()
is for. I agree that assuming literal 1
in the LogSink is kind of a mess, though. Passing it to every call to Info()
feels a bit silly - it's not going to change, like the levels. It almost feels like logr.CallFrameCount()
(simple, ugly) or something like:
logr.New(sink)
calls sink.Init(numCallFrames)
, (probably a struct) which can choose to store that info if it needs.
In real code, the definition of the Logger would be far away from the logging call-sites. The benchmark now forces the compiler to NOT inline, even though it could. It also now tests logr.Discard() and funcr.
On compat: There are some things that are just awkward after this, like DiscardLogger (the type) no longer making sense (it's a LogSink). We could jump thru hoops to make things work (make DiscardLogger a struct that embeds a Logger?) or we could just make this conversion a big breaking change. MOST clients will be fine with a recompile, and a small number will hit edge cases. If we do that, we can EOL InfoLogger and a few other vestigial things. |
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 like that it appears to be non-breaking to most uses, except for custom implementations. The perf improvement for the V logger is nice. Theoretically, this could cause more work for immutable logging-level implementations (if you have an implementation that returns NullLogger
from V()` if it's disabled, we now have an extra check every time), but practically I'm not sure I'm concerned about that as much (and many of the logging implementations don't have that property for various reasons, anyway).
With the style of optional implementations you've got (extra potentially nil field in the concrete logger), if we grow several optional interfaces, this could cause an increase in the size of concrete Logger struct and I'd be curious to see if that has perf implications.
Convert Logger from an interfact to a concrete type that calls out to a LogSink interface. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! !! THIS IS A BREAKING CHANGE !! !! !! !! Most end-users just need to recompile. !! !! !! !! Logging implementations need to make code changes. !! !! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Before: ``` $ go test -bench='.' ./benchmark/ goos: linux goarch: amd64 pkg: github.com/go-logr/logr/benchmark cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz BenchmarkInfoOneArg-6 3013077 400.9 ns/op BenchmarkInfoSeveralArgs-6 1162088 1039 ns/op BenchmarkV0Info-6 754347 1418 ns/op BenchmarkV9Info-6 4605950 267.8 ns/op BenchmarkError-6 935028 1135 ns/op BenchmarkWithValues-6 5127631 232.5 ns/op BenchmarkWithName-6 12850569 106.5 ns/op ``` After: ``` $ go test -bench='.' ./benchmark/ goos: linux goarch: amd64 pkg: github.com/go-logr/logr/benchmark cpu: Intel(R) Xeon(R) W-2135 CPU @ 3.70GHz BenchmarkInfoOneArg-6 2465788 475.7 ns/op BenchmarkInfoSeveralArgs-6 893026 1226 ns/op BenchmarkV0Info-6 817473 1250 ns/op BenchmarkV9Info-6 8595180 155.1 ns/op BenchmarkError-6 1000000 1371 ns/op BenchmarkWithValues-6 3902214 292.0 ns/op BenchmarkWithName-6 11271037 106.6 ns/op ``` Logs that actually write are dominated by the cost of preparing the message. Logs which do not write (e.g. `V(9)`) are faster here (thog glog raw is much faster still). The main difference is the variadic slice setup. This is a candidate for Go to optimize - it now knows that the args are only used inside the conditional (`Enabled()`), and when that path is not executed, it can elide that work.
This allows logr to pass params about internal stuff (such as the number of call frames it adds) to LogSink implementations.
DiscardLogger became a LogSink, so leaving that as a type was only needed for NullLogger. NullLogger can be completely replaced with Discard(). InfoLogger is vestigial and since this is a breaking change anyway, let's kill it off. Not that Logger is a type, we do not need WithCallDepth() as a standalone anymore.
Added 2 new commits.
I'm feeling pretty good about this overall. I propose we adopt this PR, call it our last intentional breaking change, and call it 1.0.0. Maybe bolster the docs and tests a bit before that.
|
OK, I am taking silence as consent. I will look at docs and tests and call this v1.0.0-rc1 |
} | ||
l.prefix += name | ||
l2.prefix += name | ||
return l |
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.
Should this be returning l2
?
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.
Just spotted #47. Sorry for the noise.
Good eyes :)
…On Sat, May 29, 2021, 12:41 PM Chris Bandy ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In funcr/funcr.go
<#42 (comment)>:
> }
- l.prefix += name
+ l2.prefix += name
return l
Just spotted #47 <#47>. Sorry for the
noise.
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
<#42 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABKWAVHJCUPDHOZSJHKS7MDTQE7PRANCNFSM42JRPMIQ>
.
|
Relates to the breaking change go-logr/logr#42 released in logr v1.0.0.
* Update module sigs.k8s.io/controller-runtime to v0.11.0 * Run generate * Replace corev1.Handler by corev1.LifecycleHandler * Replace corev1.Handler by corev1.ProbeHandler * Use logr.Discard() Relates to the breaking change go-logr/logr#42 released in logr v1.0.0.
* Update module sigs.k8s.io/controller-runtime to v0.11.0 * Run generate * Replace corev1.Handler by corev1.LifecycleHandler * Replace corev1.Handler by corev1.ProbeHandler * Use logr.Discard() Relates to the breaking change go-logr/logr#42 released in logr v1.0.0.
Upgrades controller-runtime from v0.10.3 to v0.11.2. Breaking changes described here: https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.11.0 There was a breaking change to the `go-logr` module. Refactored our logging package in order to implement the new `go-logr` interface. Referred to the following code/PRs on how to do that: - kubernetes-sigs/controller-runtime#1709 https://github.com/varshaprasad96/controller-runtime/blob/958ff158cbb375164a9ddac0cdee8ba05ae232b4/pkg/log/log_test.go go-logr/logr#42 Bumped controller-gen from v0.7.0 to v0.8.0. https://github.com/kubernetes-sigs/controller-tools/releases/tag/v0.8.0
Upgrades controller-runtime from v0.10.3 to v0.11.2. Breaking changes described here: https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.11.0 There was a breaking change to the `go-logr` module. Refactored our logging package in order to implement the new `go-logr` interface. Referred to the following code/PRs on how to do that: - kubernetes-sigs/controller-runtime#1709 https://github.com/varshaprasad96/controller-runtime/blob/958ff158cbb375164a9ddac0cdee8ba05ae232b4/pkg/log/log_test.go go-logr/logr#42 Bumped controller-gen from v0.7.0 to v0.8.0. https://github.com/kubernetes-sigs/controller-tools/releases/tag/v0.8.0
* Update module sigs.k8s.io/controller-runtime to v0.11.0 * Run generate * Replace corev1.Handler by corev1.LifecycleHandler * Replace corev1.Handler by corev1.ProbeHandler * Use logr.Discard() Relates to the breaking change go-logr/logr#42 released in logr v1.0.0.
Playing with ideas from Russ Cox.
xref #40
Before:
After:
Logs that actually write are dominated by the cost of preparing the
message. Logs which do not write (e.g.
V(9)
) are much faster here(thog glog raw is much faster still). The main difference is the
variadic slice setup. This is a candidate for Go to optimize - it now
knows that the args are only used inside the conditional (
Enabled()
),and when that path is not executed, it can elide that work.
The real rub is that this is a breaking change for implementors (but not for consumers). We haven't declared 1.0 yet, but I know we have some users out there. @wojas