Logging vs. instrumentation
peter.bourgon.org
peter.bourgon.org
> In my opinion, my thesis from GopherCon 2014 still holds: services should only log actionable information.
This assumes that I have future knowledge of which logged events will be actionable.
I used to work in the classifieds department of a newspaper. The rule of thumb was that the cover of the newspaper was worthless by 11am, the classifieds by 2pm.
But that's not quite true. Every single edition of a newspaper retains a small probability of turning out to be very important at some unknown future time. Hence the practice of archiving.
So it is with logs. In my day job I have on several occasions turned to raw logs in order to reconstruct the history of unexpected events.
What metric, to take an actual example, tells me that a user was trying to evade export control by hopping to a VPN halfway through a session? Which gauge tells me that an error was caused by the interaction of third party javascript and an obscure web browser?
For myself, I draw the boundary differently. Logging is for events with identity, events that will be inspected individually. Metrics are for events that will never have identity and will only be understood statistically.
> Finally, understand that logging is expensive. I’ve seen entire teams of absolutely brilliant engineers spend years building, managing, and evolving logging infrastructure. It’s a hard problem, made much harder by overburdening the pipelines with unnecessary load.
It's ultimately a piece about engineering cost versus benefit. There's a real cost associated with logging every event with identity, to create an audit trail. Maybe that's necessary in your domain, for compliance reasons or otherwise. In which case it falls into the bucket of machine-read structured logging, and you design for it. But for lots of domains, it isn't strictly necessary.
Log as much as you can afford.
Given that the cost of logging is low and continuously falling, the default decision should be "log everything".
"Log only actionable messages" is analogous to advice like "always use bit masks instead of booleans", or only "use two digits to encode years". It assumes algo-economics that are no longer the normal case.
Everything gets cheaper in time, but the cost of logging is actually quite high.
One immediate example that comes to mind was code that had a bunch of logging statements like log.debug(boost::format("%d %x etc", param, param)). Normally the code would run with debug logging turned off, but there's a pretty brutal problem there! The formatted string is always going to be processed, which the logger will immediately discard. Turns out one of these formatted strings actually took a fair bit of processing to generate and was hurting the system.
If I was writing an embedded system, or a system with soft-to-hard realtime requirements, or one where flat straight line performance was the only requirement that counted, sure, I might ditch or constrain logging.
These systems might be common by manufactured volume. But most programmers will never touch one.
You're probably thinking of I/O costs. Again, these aren't as a brutal as they used to be -- SSDs swallow bursty writes with aplomb and when those aren't enough, log-structured filesystems are good at smoothing out writes.
As for myself, in the work I do in my day job, I print out to STDOUT/STDERR and let the platform wick those lines away to a central firehose.
I think this is the correct approach, but (to the nearest approximation) ~every organization I've worked at that does this has hit saturation limits of "the platform" in extremely short order. Especially if you're doing audit-trail-style logging, in my experience, this becomes the primary bottleneck of your infrastructure very quickly.
> If I was writing an embedded system, or a system
> with soft-to-hard realtime requirements, or one
> where flat straight line performance was the only
> requirement that counted, sure, I might ditch or
> constrain logging.
I've worked on embedded systems, and logging is still extremely important, if not more important because of the difficulty of seeing what's going on (it's hard to reproduce events that only happen in a truck while you're inside your office).In one case, a team had been trying for months to debug a problem, but when proper logging was added, the issue was easily fixed.
Throughput gets better but latency's a tougher nut to crack.
There's a defacto standard for many daemons using SIGUSR1 as a prompt to close/reopen log files. You could settle on some other non-used signals to change the log level.
eg, HTTP access logging is good and useful (vs non-structured debug timings on every fragment rendering)
eg, A mail server reporting delivery of a particular message into a mailbox.
I think your "events with identity" description is a nice succinct version of this same idea.
Consider why there are cockpit voice recorders and flight data recorders. Almost all of that data is thrown away.
In a more extreme example than the newspaper, consider some sort of data breach investigation.
E.g. it is very useful to collect all user actions and other major actions. So later when user face some problem and sends support a message you know what was going on. You can send user a message that his issue was fixed instead of asking the user for a reproduction steps.
The aggregate numbers poorly tell you a story what has happened to this particular user and at scale there are bugs that just some ppl will hit.
You can process logs and produce the metrics out of them, but you can't do the opposite. Saying logs are just human readable actionable errors seems backwards to me.
But what if I need some information to debug a problem in the future? I log some related information and when error happens, I can look to the logs and get that information. This might help to debug a problem. Ideally I would like to have a values for all variables at any moment of time. Practically I can't have it, but I can get some values, which, I think, are more important. So later, when I debug a problem, I'll have more information. And if I don't ever need that, no problem, disks are cheap and logs are small (if done properly, there's nothing worse than searching for a needle in a 100GB log file).
For my programs I developed a simple rules. Log everything important with DEBUG level (but it shouldn't be too much, don't log 10MB data for each HTTP request). Log everything that happens in the system at a "higher" level with INFO level (like "add record with text {}"). Log everything unusual with WARN level (like user validation failed). Log every unexpected but recoverable (application can serve other requests) error with ERROR level. Log unrecoverable error with FATAL level and shutdown application. Works fine for me. And TRACE level just for debug, it's usually turned off, so it might produce any amounts of data.
I follow these guidelines (similar to yours):
- ERROR: Failed assertions, unrecoverable conditions
- WARN: Exceptions (request is toast, but system works)
- INFO: Important actions (entered a certain state)
- DEBUG: Information associated with the actions above
Note that we too don't log full requests, etc. at DEBUG - that's way, way too much. If you really want to do that? TRACE, but that's usually important for the developers of those libraries. We're also lucky in that we use logback, so we can change the log level of the system at runtime very easily.In production we usually run on INFO and above and the logs aren't overwhelming - though we're constantly trying to improve the quality and consistency of the messages.
2. You almost want your log entries to tell a story, you want to spend nearly as much time on your log entries as on your code. I think the more you get this right, the better your software design will be.
3. You also want to be able to grep your logs easily using a transaction and user ID. For example, if you just fetched a batch of emails over IMAP, you want to have a transaction ID to describe the session, and include this in all related log entries for the session. You also want to include an account ID in all related entries so you can see all fetches for a given user.
4. Your log entries should do as much work for you as possible. For example, if your log entry logs a filename or string that contains unusual Unicode characters, then you might want your log function to also log what Unicode normalization form the string contains (NFC, NFD, Mixed). This can make otherwise complex bugs trivial to solve.
5. Logs should also be distributed, in the sense that you don't ship all your logs to a central location, but rather have each server manage its own logs. If you need to query your logs, then send a query out to the relevant servers and get them to do the work for you. If logs are always a fraction of a server's data output then you can easily manage gigabytes of log data per server this way.
There's a huge operational and engineering cost buried in that statement, which the article is trying to unearth and provide better solutions for.
> 5. Logs should also be distributed, in the sense that you don't ship all your logs to a central location, but rather have each server manage its own logs.
Though it does give you some nice properties, this is counter to a lot of accepted best practice... is there a good distributed log query system? `dsh` doesn't cut it, for the record :)
Sure, there's always an operational and engineering cost to logging. I think the original comment makes that plain.
Perhaps there was an unstated assumption on your part, but how are my app servers to know which one handles which query? Is there some sort of log coherence system going on? Are you running Hadoop on your app servers?
You keep on using that word... but seriously, thanks for the non-answer, now I can be confident you don't know what you're talking about.
I guess your original comment made clear that you weren't really interested in an answer beyond dealing with "unstated assumption" and that you want to persist in thinking of logging from a centralized point of view. You just wanted to be "confident".
But if you're seriously interested in discussing further to see what I'm working on, feel free to drop me an email on joran@ronomon.com and we can setup a Skype face-to-face.
Logging is figuring out how to keep any and everything in a small to mid-sized time window which you hope (but don't really know) may be useful later in a very small time window.
Monitoring is having something watch instrumentation (metrics) for anomalies. This may include views you build up over time on the previously mentioned logs.
If there was ever an industry that could benefit from AI, it's logging.
1/ Deviare In-Proc (like Microsoft Detours): https://github.com/nektra/Deviare-InProc
2/ Deviare Hooking Engine (even novices can instrument apps): https://github.com/nektra/Deviare2
3/ RemoteBridge (access internal COM and Java apps) https://github.com/nektra/RemoteBridge
All these libs include examples and there are additional use cases in our blog http://blog.nektra.com
I must confess, I'm currently trying to get hard data on how our users are using our product and the thought of logging every request had crossed my mind. As an entry point to to finding the right solution and also because our product is hosted on the client's server and rarely under heavy load (we never have more than a dozen users connected at the same time).
I'm now going to look into instrumentation...
For what it's worth, FOSDEM itself uses it for the real time monitoring of its infrastructure.