Guidelines regarding structured logging usage #722
Labels
No labels
bug
duplicate
easy
enhancement
help wanted
invalid
question
rfc
structured-logging
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gh-rust-lang/log#722
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What data should be put into structured logging attributes? Everything? Or only unimportant meta-information? If everything, should text message duplicate data already included in KV attributes?
env_loggerin its default configuration, will print both message and KVs to the console, which is redundant and ugly. However, it works fine forsystemd-journal-logger:journalctlprints only the text message by default (+ timestamp). And you can also filter by KV attributes, as they're stored as custom journal fields.systemd-journal-logger, will store only the text message intoMESSAGEjournal field, and store the KVs as custom journal fields.journalctlwill only show the text, and important error details can only be retrieved withjournalctl -o verbose, and its output is too noisy to use for typical log-reading activities. In contrast, this record is rendered perfectly byenv_logger:message key1=value1 key2=value2 ...I think
logis missing concrete guidelines regarding stuctured logging usage. Although output format is configurable in many loggers, it isn't insystemd-journal-logger, and you're still out of luck if combine different libraries that log using different conventions./cc @swsnr
I agree that some guidelines on how to use key-values would be useful. I'll write down some of my thoughts to get started. Feedback is welcome!
From my point of view the most important thing for this (or pretty much anything related to logging) is what are you interested in doing once the logs are written? For example, do you want to be able to quickly filter the logs on a given value? Then it makes sense to log that as a separate key-value pair. If the value don't have a know set of values, i.e. it's not a number or a (kind of) enum, then I think it makes more sense to put it in the log message.
To give a concrete example let's start with errors. The error's text message you'll likely want to include in the log message, i.e.
log::error!("some error happened: {err}"), and not as separate key-value pair. If your error also contains something like an error code (e.g. Rustc error codes) that should probably be logged as a separate key-value pair so that you can use that as a filter when querying the logs.Another thing to consider is if the data logged in a single record can be logically separated. For example when logging HTTP requests you might want to add the HTTP method, path, version, etc. as key-value pairs as they can be seen as separate data (about the same thing). And again it makes for easier filtering/querying of logs.
Finally, I would also suggest to think about any processing you want to do with the logs. Keeping with the HTTP requests logging example, you might want to calculate the min/max/avg/etc. response times. Logging the response times as a key-value pair would make this much easier than having to extract that from a text message, i.e.
log::info!(response_time; "request");overlog::info!("request, response_time={}").I'd still put errors into structured attributes, even in cases they're not really simple enums (e.g. plain OS error enriched with dynamic
anyhowcontext). Even if you have to resort to text grep, it's still better to grep a single attribute rather than grepping entire completely unstructured message.Great question @WGH- 👍
Personally, I'd advocate for 2: duplicate any relevant properties interpolated into your text message as structured fields too. This is probably something we'd do in the macros for you, were it not for
macro_rulesgiving us no way to introspect the format string. They're in that privileged text message because they're the most important pieces of information for understanding the log event, so chances are they're also useful as structured data too.For errors, you can capture them as an instance of the
Errortrait using the:errcapturing modifier:Targets can then specialize their handling of that
errproperty to print a full cause chain for example based on itsErrorimplementation instead of just having to work with whatever itsDebugorDisplayimplementation had.As for what to include as structured properties, I think @Thomasdezeeuw summed it up nicely; capturing anything that will assist analysis on the logs later, without having to try parse relevant pieces of information out of raw text logs. Contextual information that correlates multiple logs together is especially useful.
I think we could also benefit from some recommend conventions on naming for various well-known properties too, like errors, trace/span/correlation ids etc.
Talking about naming we should also add some basics, like what kind of casing should be used. I've always used snake casing (i.e.
some_field) to match the naming of variables.Hi @WGH-!
I'd share a data point from
logforthwhere we use it to report our database's (ScopeDB's) logs to multiple destinations (stdout, files, opentelemetry endpoints).Typically, our log statements looks like:
We previously tried to put
errin the kvs but noticed that when developing applications that finally related to multiple languages, whether the field is callederr/e/erroris too hard to unified. And typically theerris a message to search. So putting the error message into the message part works well.Besides, we leverage
logforth's diagnostic to inject common kvs (as diags):The
StaticDiagnosticis similar to Go's slogHandle.WithAttrsandFastraceDiagnosticintegrated withfastraceto connect logs and traces.A sample final view looks like (some internal spans redacted):
Logs
Log Details
Integrated with Traces
Refs:
This is the sort of thing we should at least standardise naming for in
logso integration libraries can confidently map it to other schemas, like OpenTelemetry.The fields that come to mind for me that we don’t handle explicitly in
logare:One more came to mind:
Something that is more logging infrastructure specific but maybe worth considering: