Skip to content
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

numeric verbosity and configurable error field name #37

Merged
merged 1 commit into from
Aug 24, 2021

Conversation

pohly
Copy link
Contributor

@pohly pohly commented Aug 9, 2021

This is needed to make zapr usable for Kubernetes. Currently
Kubernetes uses a forked zapr with "v" for the numeric verbosity and
"err" for Logger.Error. Compared to the implementation in Kubernetes
there's one functional difference: Logger.Error is logged with v=-1
and internal panics with v=-2 to distinguish the log messages from
normal info messages where v >= 0.

The unit tests were derived from
https://github.com/kubernetes/kubernetes/blob/cff40a7bcc170c473c223081fd0103fd042dc44b/staging/src/k8s.io/component-base/logs/json/json_test.go
and both modified (better error messages, t.Run for each test case)
and extended to enhance coverage.

Several new edge cases are now covered (for example, error logging
at non-zero verbosity levels) and new tests for WithName,
WithCallDepth and WithValues were added.

Fixes #36

@pohly
Copy link
Contributor Author

pohly commented Aug 9, 2021

@serathius: do you agree with the decision to use -1 for Logger.Error and -2 for panics?

I have tested this updated zapr in Kubernetes (see kubernetes/kubernetes#104103) and it passes the existing tests with only minor changes to those tests (v=-1/-2, additional fields, different (correct?) source location for panics).

@serathius
Copy link

serathius commented Aug 10, 2021

I don't think we should mix verbosity and log type (info vs error). My understanding is that logr uses Info and Error to allow attaching additional information like stack trace. https://github.com/go-logr/logr/blob/622c97b4eb90a99cc628f4773662eb0015ce0f2a/logr.go#L45-L47

I don't treat Error as log more important then Info with V(0). Both are logged always and should be always useful to operator. Difference is that administrator might prefer to send Errors to error tracking systems like Sentry.

As for dPanic, they should not be treated as panics with higher verbosity. dPanic method is way for zap to report internal errors that need to be handled more carefully. It's different call path as you cannot just log internal log as a normal log, that would lead to recursion :P. I would also not treat them as more critical than errors reported by caller.

Refering to @thockin to get authoritative response.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

I don't think we should mix verbosity and log type (info vs error). My understanding is that logr uses Info and Error to allow attaching additional information as stack trace.

Errors have a different verbosity semantic than info messages. They always get logged regardless of the current verbosity level of the logger, in contrast to info messages:

https://github.com/go-logr/logr/blob/622c97b4eb90a99cc628f4773662eb0015ce0f2a/logr.go#L206-L210
vs.
https://github.com/go-logr/logr/blob/622c97b4eb90a99cc628f4773662eb0015ce0f2a/logr.go#L220-L222

Logging an error with v=5 therefore doesn't make sense to me.

I don't treat Error as log more important then Info with V(0). Both are logged always and should be always useful to operator.

Both are logged only if the threshold is >=0. Theoretically a logger could also be configured with -1 as threshold and then only log errors, but not V(0) info messages. Therefore I think that one can argue that errors have either "higher" verbosity than info messages or no verbosity at all.

Panics are yet another thing. They are not errors encountered by the application nor info messages. Therefore it seemed useful to log them differently. We can also always log them without numeric verbosity.

@serathius
Copy link

As Error can also be used with verbosity, it would be good to agree on how it should work. Some examples about my expectation:

For Info with verbosity

err := fmt.Errorf("Error")
logger.V(2).Info("Something happen", "err", err)

Should write:

{"v": 2, "msg": "Something happen", "err": "Error"}

Same with Error

err := fmt.Errorf("Error")
logger.V(2).Error(err, "Something happen")

Should write:

{"v": 2, "msg": "Something happen", "err": "Error"}

They are exactly the same, difference is that we might decide to add a flag to enable adding stacktrace information to Error.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

As Error can also be used with verbosity,

You can write code like that, but semantically logger.V(5).Error and logger.Error are exactly the same. That's simply how logr works.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

logger.V(2).Error(err, "Something happen")
Should write:
{"v": 2, "msg": "Something happen", "err": "Error"}

That was possible in logr v0.4.0, but is not possible anymore with v1.0.0 because LogSink.Error isn't passed any verbosity level.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

That's simply how logr works.

... and is intended to work. Looking at the API documentation for Logger.Error, it's probably something that should be called out explicitly. it's currently not documented there.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

FWIW, I don't care too much about v=-1 and v=-2. It just seemed to be useful to use that field to distinguish between the three different sources of log messages because the field otherwise has no meaning for errors and panics. I'm also fine with not adding v to errors and panics.

Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why print a "lvl" on errors? Using negative values feels like a hack. Would it be better to have 2 keys, like "is-error" and "lvl" or something?

I don't follow the dpanic - how would a user get dpanic into the LogSink in the first place if we map log.Error() to zap.ErrorLevel?

zapr.go Outdated
// each log message. The empty string disables this, a non-empty
// string is the key for the additional field. Errors use -1 and
// panics -2.
func WithLogNumeric(key string) Option {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using With*() here feels confusing. I'd make this sound imperative. e.g. LogNumericLevel(key) or LogInfoLevel(key)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm influenced by gRPC here. It uses the With prefix for options. https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis doesn't use it and in logr we have some other convention around With, so I agree, let's simplify.

zapr.go Outdated
// panics -2.
func WithLogNumeric(key string) Option {
return func(zl *zapLogger) {
zl.logNumericFieldName = key
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wordy for not much value. I'd streamline the name like numericLevelKey or levelKey

zapr.go Outdated

// WithErrorFieldName replaces the default "error" field name
// used for the error in Logger.Error calls.
func WithErrorFieldName(name string) Option {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ErrorKey(key) ?

@thockin
Copy link
Contributor

thockin commented Aug 10, 2021

On negative V() levels. The doc-comments for V says 2 things of note:

  1. "V-levels are additive."

  2. "Negative V-levels are treated as 0."

The code backs this up:

func (l Logger) V(level int) Logger {
	if level < 0 {
		level = 0
	}
	l.level += level

The idea here is that I might pass a Logger to some other function with a bias in its V(). E.g.

// libfoo logs too much, so make it's notion of V(0) => my V(1).
foo := libfoo.NewFoo(log.V(1))

...and that other function can't undo what I have done.

Regarding V(4).Error(), yeah it's a little confusing, but it doesn't mean error verbosity, it just happens to fall out from the API. Historical note: at some point we had type Logger and type InfoLogger but it was clumsy when doing the above bias. Happy to take a PR to clean up the comments there.

@pohly
Copy link
Contributor Author

pohly commented Aug 10, 2021

Why print a "lvl" on errors? Using negative values feels like a hack.

Okay, I'll drop that.

Would it be better to have 2 keys, like "is-error" and "lvl" or something?

That's basically a new feature request ("distinguish between error and info messages"). Let's postpone that until someone actually needs it and the criteria "only info messages have v" is not enough.

I don't follow the dpanic - how would a user get dpanic into the LogSink in the first place if we map log.Error() to zap.ErrorLevel?

It's the log sink itself which generates a panic log message and sends it to the underlying zap logger. As for errors let's simply not add a "v" field to those messages.

zapr.go Outdated Show resolved Hide resolved
@pohly pohly force-pushed the verbosity-and-error-options branch from f46ce7d to 8d22d6f Compare August 11, 2021 11:10
@pohly
Copy link
Contributor Author

pohly commented Aug 11, 2021

I've force-pushed an update. Key changes:

  • rebased to resolve conflict in go.mod
  • errors and panics never get the extra numeric level field
  • options for panic messages and allowing strongly-typed Zap fields
  • names simplified as suggested by @thockin

Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly typos, a few small things.

example/main.go Outdated Show resolved Hide resolved
zapr.go Show resolved Hide resolved
zapr.go Outdated Show resolved Hide resolved
zapr.go Outdated Show resolved Hide resolved
zapr_test.go Outdated Show resolved Hide resolved
zapr_test.go Show resolved Hide resolved
}

// TestInfo tests the JSON info format.
func TestInfo(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty complex test. I won't demand it now, since I know it is ported, but it would be nice to have 1 test that proves Info() works with no options. Then a test that proves that LogInfoLevel works. Then a test that proves ErrorKey works. etc. I feel like each test should be much simpler to comprehend

go.mod Show resolved Hide resolved
@pohly pohly force-pushed the verbosity-and-error-options branch from 8d22d6f to 2e8c031 Compare August 12, 2021 08:33
Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm willing to merge as-is, one point to discuss (naming, optional change IMO)

zapr.go Show resolved Hide resolved
zapr.go Outdated Show resolved Hide resolved
@pohly pohly force-pushed the verbosity-and-error-options branch from 2e8c031 to c13ca57 Compare August 13, 2021 06:12
zapr.go Outdated Show resolved Hide resolved
Copy link
Contributor

@thockin thockin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a followup, maybe change the example to use these properly?

@pohly pohly force-pushed the verbosity-and-error-options branch 2 times, most recently from 8b94353 to ba50232 Compare August 17, 2021 19:01
@pohly pohly changed the title numeric verbosity and configurable error field name WIP: numeric verbosity and configurable error field name Aug 17, 2021
@pohly
Copy link
Contributor Author

pohly commented Aug 17, 2021

This PR depends on a logr release with go-logr/logr#74 logr v1.1.0-rc1.

@pohly pohly force-pushed the verbosity-and-error-options branch from ba50232 to 46f84fd Compare August 23, 2021 08:16
limitations under the License.
*/

package main
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a followup, maybe change the example to use these properly?

Added that as example/example_test.go. I'm just not sure whether godoc will pick up the example code there or whether it needs to be one level up in the same package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turned out that they are not found inside the example directory.

It worked for funcr when in the same directory as the code (https://pkg.go.dev/github.com/go-logr/[email protected]/funcr#pkg-examples) but not for zapr (https://pkg.go.dev/github.com/go-logr/[email protected]).

I'll move the file in a new PR.

@pohly pohly force-pushed the verbosity-and-error-options branch from 46f84fd to 566ac59 Compare August 23, 2021 08:25
@pohly pohly changed the title WIP: numeric verbosity and configurable error field name numeric verbosity and configurable error field name Aug 23, 2021
@thockin
Copy link
Contributor

thockin commented Aug 23, 2021

1.1.0 is tagged

@pohly pohly force-pushed the verbosity-and-error-options branch 2 times, most recently from 78b6279 to e74c9d4 Compare August 24, 2021 17:54
This is needed to make zapr usable for Kubernetes. Currently
Kubernetes uses a forked zapr with "v" for the numeric verbosity and
"err" for Logger.Error. Handling of invalid calls remains compatible
with zapr v1.0.0 (= panic log messages are emitted, strongly-type Zap
fields are not allowed), but new options allow changing those
defaults.

Those panic messages now log the source code of the caller, not the
zapr.go file where the panic message is logged.

The unit tests were derived from
https://github.com/kubernetes/kubernetes/blob/cff40a7bcc170c473c223081fd0103fd042dc44b/staging/src/k8s.io/component-base/logs/json/json_test.go
and both modified (better error messages, t.Run for each test case)
and extended to enhance coverage.

Several new edge cases are now covered (for example, error logging at
non-zero verbosity levels). New tests for WithName, WithCallDepth and
WithValues were added.
@pohly pohly force-pushed the verbosity-and-error-options branch from e74c9d4 to 13bdf6b Compare August 24, 2021 18:01
@thockin
Copy link
Contributor

thockin commented Aug 24, 2021

LGTM

@thockin thockin merged commit bc76e7d into go-logr:master Aug 24, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

panic messages use keys with spaces
3 participants