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

Context logging #39

Merged
merged 8 commits into from
Apr 3, 2022
Merged

Context logging #39

merged 8 commits into from
Apr 3, 2022

Conversation

jessepeterson
Copy link
Member

Adds a simple ctxlog package which permits associating multiple CtxKVFuncs with a context. Then, when it's time to log, these are ran using the request context to pull out logging key-value pairs and create a new logger.

The end result is something like this for a typical NanoMDM push request.

Before:

2022/03/16 14:03:09 level=info handler=log addr=::1 method=GET path=/v1/push/D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8 agent=curl/7.64.1
2022/03/16 14:03:09 level=info service=push msg=retrieved push cert topic=com.apple.mgmt.External.deadbeef-1f17-4c8e-8a63-dd17d3dd35d9
2022/03/16 14:03:09 level=debug handler=push msg=push count=1 errs=0
2022/03/16 14:03:09 level=info handler=log addr=192.168.0.2 method=PUT path=/mdm agent=MDM-OSX/1.0 mdmclient/1455 real_ip=2301:602:8a02:9b0f:422:25a8:481b:bd42
2022/03/16 14:03:09 level=info service=nanomdm status=Idle id=D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8 type=Device
2022/03/16 14:03:09 level=debug service=nanomdm msg=no command retrieved id=D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8

After:

2022/03/16 14:02:44 level=info handler=log trace_id=84172eff419691ca addr=::1 method=GET path=/v1/push/D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8 agent=curl/7.64.1
2022/03/16 14:02:44 level=info service=push trace_id=84172eff419691ca msg=retrieved push cert topic=com.apple.mgmt.External.deadbeef-1f17-4c8e-8a63-dd17d3dd35d9
2022/03/16 14:02:44 level=debug handler=push trace_id=84172eff419691ca msg=push count=1 errs=0
2022/03/16 14:02:44 level=info handler=log trace_id=c35015253617eb34 addr=192.168.0.2 method=PUT path=/mdm agent=MDM-OSX/1.0 mdmclient/1455 real_ip=2301:602:8a02:9b0f:422:25a8:481b:bd42
2022/03/16 14:02:45 level=info service=nanomdm trace_id=c35015253617eb34 id=D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8 type=Device status=Idle
2022/03/16 14:02:45 level=debug service=nanomdm trace_id=c35015253617eb34 id=D7083AF3-DCCB-5661-A678-BCE8F4D9A2C8 type=Device msg=no command retrieved

Note the trace_id value which tracks each log's request from its origin. Note also that id and type in the service=nanomdm layer is also a context value rather than explicitly logged now.

Copy link

@kilpatds kilpatds left a comment

Choose a reason for hiding this comment

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

Is there a way to up the log level for a specific request/other selector?

@@ -196,6 +200,8 @@ func main() {
w.Write([]byte(`{"version":"` + version + `"}`))
})

rand.Seed(time.Now().UnixNano())

Choose a reason for hiding this comment

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

Is it possible to make this configurable for testing purposes?

Also, is it possible to get a separately-seeded/used sequence, so that this will only be used for the trace-ids?

(If you need cryptographic strength here, this isn't a good enough seed, and also not a good enough package...but it doesn't look like you need it)

(looks like you can create your own source/random object... just can't be easily used concurrently)

Copy link
Contributor

Choose a reason for hiding this comment

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

main.go is the "reference" nanoMDM implementation afaik. Any other implementer who uses the NanoMDM library could do this in a separate way, so I don't think this is an issue.

Choose a reason for hiding this comment

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

It's a testability consideration, not anything else.

Choose a reason for hiding this comment

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

(As far as I can tell, the only security implication of UUIDs is preventing collisions. So ... any seeding is "good enough", and use of a cryptographic PRNG is unnecessary. There's no harm if someone can guess a UUID.

So I'm just asking for "creation of unit test" purposes: if you can control the PRNG seed, you can get repeatable sequences out which makes testing easier.

If this isn't where anyone would actually test this, then ... yeah. Nevermind/drop)

func Logger(ctx context.Context, logger log.Logger) log.Logger {
if ctx == nil {
return logger
}

Choose a reason for hiding this comment

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

...

Do you have benchmarks for how long it takes (on average) to log a line? This seems expensive for logging, and I'm wondering if this can be precalculated

}
var acc []interface{}
ctxFuncs.RLock()
for _, f := range ctxFuncs.funcs {

Choose a reason for hiding this comment

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

Similarly in the annals of "premature optimization", is it possible to avoid doing this if the message won't actually be logged because the current log level is too high for the message? (or am I totally missing how this is implemented?)

@jessepeterson
Copy link
Member Author

@kilpatds re: log levels: I'm not sure if I'm reading you right, but there are only two log levels: Info and Debug. Wether those are logged or how is a detail left to the logger implementation and this ctxlog package just adapts a logger to add context (or not). The actual log line does happen until .Info() or .Debug() is called (where an implementation might decide to log or not). In NanoMDM's default case is handled by the stdlogfmt package whenever a Debug log is formatting:

if l.logDebug {

For more details/philosophy on the simplistic logging "levels" see https://pkg.go.dev/github.com/jessepeterson/go-log#section-documentation and https://dave.cheney.net/2015/11/05/lets-talk-about-logging.

Hope that helps. :)

@kilpatds
Copy link

'''The log package should support fine grained control to enable or disable debug, and only debug, statements at the package or possibly even finer scope.'''

A pattern I've seen far too often around logging is something like

log.debug(context, <function that outputs 2k of text>())    

where it's a shame to generate 2k of text output just to throw it away because you don't actually log because debug logs aren't on. So ... that loop that provides context: if those functions are expensive, it's a shame to waste them on debug log lines that aren't actually enabled? Or again, am I totally missing the boat?

@jessepeterson
Copy link
Member Author

@kilpatds I think I see what you're saying now.

Yeah so the general pattern we follow is that we unconditionally call logger.Debug() and then the implementation decides whether to actually log/output or whatever. I'm not aware of us generating huge amounts of debug code so we're just passing references to existing variables. That said: I do see the potential for a problem there. Perhaps the https://github.com/jessepeterson/go-log should add a simple func IsDebug(): bool interface so that you can conditionally do debug log stuff only when you'll actually log it. Food for thought. However: I'd say that doing expensive debug stuff is different from logging debug stuff: i.e. the module/package doing an expensive debug call that runs 2k of stuff ought to have its own debug mode, really. We don't really have anything doing expensive debug stuff (yet) though so none of our packages have separate debug modes — we just rely on the logger to do that for us (for now, at least).

Regarding computing the logger context stuff each invocation/being expensive: yes, I see that concern too and it's semi-addressed in two ways: one is that for some cases (Info logging) we only generated when we're going to log. The other cases, I'm just not really concerned about now.

In summary: yes, good points, I hear you, but not a concern and/or pre-mature optimization at this point. IMO, at least. :)

@jessepeterson jessepeterson merged commit 6e02355 into micromdm:main Apr 3, 2022
@jessepeterson jessepeterson deleted the ctxlog branch April 22, 2024 19:54
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.

3 participants