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

Modify output method of loggingT to handle Logger/InfoLogger correctly #118

Conversation

vanou
Copy link

@vanou vanou commented Feb 17, 2020

What this PR does / why we need it:
This commit modifies output method of loggingT to handle l.logr or argument log correctly, if set.

In this commit l.logr is checked first, because if loggingT.logr, which is only receiver value of output method currently, is set, it should be used unconditionally as comment of logr field of loggingT says.

Till this commit applies, output method is only used with global variable logging as receiver and logging.logr/Verbose.logr as argument log.
To maintain backward compatibility, in this commit, output method only checks whether argument s is errorLog or not when l.logr is not nil.

Which issue(s) this PR fixes:
None

Special notes for your reviewer:
None

Release note:
None

@k8s-ci-robot k8s-ci-robot added cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Feb 17, 2020
@k8s-ci-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: vanou
To complete the pull request process, please assign lavalamp
You can assign the PR to them by writing /assign @lavalamp in a comment when ready.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

klog.go Show resolved Hide resolved
@vanou vanou force-pushed the modify_output_method_to_handle_logr_Logger branch from df859ab to fcbe797 Compare February 18, 2020 13:03
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Feb 18, 2020
@vanou
Copy link
Author

vanou commented Feb 18, 2020

Sorry @neolit123 . I have made test function. However it becomes little big.

If you review it, it's very thankful.

klog_test.go Outdated Show resolved Hide resolved
}
} else if log != nil && s == infoLog {
log.Info(string(data), "severity", severityName[s], "file", file, "line", line)
Copy link
Member

Choose a reason for hiding this comment

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

the above branching looks mostly correct to me, but i pinged the #klog channel of k8s slack for more reviewers.

Copy link
Author

Choose a reason for hiding this comment

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

Thanks!

This commit modifies output method of loggingT to handle l.logr
or argument log correctly, if set. And add test function of
output method of loggingT.

In this commit l.logr is checked first, because if loggingT.logr,
which is only receiver value of output method currently, is set,
it should be used unconditionally as comment of logr field of
loggingT says.

Till this commit applies, output method is only used with global
variable logging as receiver and logging.logr/Verbose.logr as
argument log.
To maintain backward compatibility, in this commit, output method
only checks argument s is errorLog or not when l.logr is not nil.
@vanou vanou force-pushed the modify_output_method_to_handle_logr_Logger branch from fcbe797 to d2e1629 Compare February 18, 2020 16:46
}
} else if log != nil && s == infoLog {
Copy link
Member

Choose a reason for hiding this comment

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

It should technically never be possible to get into a state where log == nil, l.logr != nil and s == errorLog right? Because there is no Error method on an InfoLogger. I ask because otherwise there is a risk we drop errors here 😄

This logic looks right to me :) just trying to validate my own assumptions!

Copy link
Author

Choose a reason for hiding this comment

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

Thanks @munnerz .

Yes, that is underlying idea of this line. Ideally, it should not happen.

Copy link
Member

Choose a reason for hiding this comment

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

Makes sense. If l.logr == nil, log != nil and s is not infoLog, it seems like log messages will be silently dropped. I'm not sure if this is a feasible situation to be in, but with the previous code we'd always log unconditionally if log != nil. Is there a reason for the s == infoLog check on this line?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, as you said, if l.logr == nil, log != nil and s is not infoLog, log messages will be dropped.
I read logr package's README and thought it's good approach that klog only handles infoLog or errorLog when logr.InfoLogger or logr.Logger is used. Because user of logr.InfoLogger & logr.Logger only expects Info or Error, I think.
So, future development of klog should care about this logr's idea.

As I wrote in klog_test.go, currently this commit doesn't hurt klog package's code. And there is no public method/function with which user can specify severity value of output method.

Copy link
Member

Choose a reason for hiding this comment

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

Because user of logr.InfoLogger & logr.Logger only expects Info or Error, I think.

There are log levels in between (e.g. warning) that do not have direct equivalents in a logr world (as logr only has levels). The thinking is to treat any un-levelled log message that specifies a non-info level severity as an Info message and pass along the severity as a 'key/value' in the log line (i.e. severity=warn) (except for Errors, which are always passed to the root Logger via the Error method). The reason this severity k/v pair wasn't added when SetLogger was originally added can be seen here: #20 (comment)

Mostly there wasn't clear consensus as to what 'keys' should be used and what the onus is on implementors of the logr interface wrt setting these keys so other consumers can consume them reliably 😄

Copy link
Author

Choose a reason for hiding this comment

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

I understand why k/v isn't added. It's hard problem, until some set of standard keys is established.

Thanks!

@munnerz
Copy link
Member

munnerz commented Feb 21, 2020

In this commit l.logr is checked first, because if loggingT.logr, which is only receiver value of output method currently, is set, it should be used unconditionally as comment of logr field of loggingT says.

I'm not actually sure if this has been interpreted correctly:

	// If set, all output will be redirected unconditionally to the provided logr.Logger
	logr logr.Logger

The wording here was to explain that if SetLogger has been called, all output will be sent to this logger.
If the log argument to output is provided, it indicates that the call-site has 'scoped' that particular message (i.e. through a call to V).

For example, here:

klog/klog.go

Lines 1223 to 1245 in d2e1629

// Info is equivalent to the global Info function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Info(args ...interface{}) {
if v.enabled {
logging.print(infoLog, v.logr, args...)
}
}
// Infoln is equivalent to the global Infoln function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infoln(args ...interface{}) {
if v.enabled {
logging.println(infoLog, v.logr, args...)
}
}
// Infof is equivalent to the global Infof function, guarded by the value of v.
// See the documentation of V for usage.
func (v Verbose) Infof(format string, args ...interface{}) {
if v.enabled {
logging.printf(infoLog, v.logr, format, args...)
}
}

The second argument (v.logr) is a levelled logger, created in newVerbose here:

klog/klog.go

Line 1170 in d2e1629

return Verbose{b, logging.logr.V(int(level))}
.

With your change, the logging.logr will be used instead of the passed down, levelled logger that should be used.

This method should:

  1. always log to the argument log if it is provided
  2. if the severity is error, the struct member Logger is used instead of log, because log is an InfoLogger and so does not have an Error method
  3. otherwise, print to log.Info (or l.log if log isn't provided)

What's the actual incorrect behaviour you're seeing that has lead to this PR? I think I'm struggling to see the motivation for it now I've dug in more.. 😅

@vanou