Many years ago when I started my first job as a developer, one of the things I noticed was that it was always difficult to solve an issue our end users were having. Often, we’d try to replicate the problem, be unable to, and end up having to call the users and ask them to share their screens with us, go over to watch them do whatever they were doing, or call us back whenever it came up again.
Another common trend was the sheer amount of time it took to track down the root cause of a bug that could be replicated. Particularly with complex, data-driven systems, tracking down bugs would be time consuming and difficult and result in a lot of late nights and sometimes weekends to make things usable for clients in a timely manner.
Does any of this sound familiar? At first, being inexperienced, I chalked it up to just the way it was. Complex programs are more likely to be difficult to maintain after all. My younger self thought it was just the cost of doing business.
It didn’t take all that long for me to realize there had to be a better way to do things. It finally dawned on my inexperienced self that the problem was our software wasn’t transparent enough about what it was doing. A lack of information was the culprit of all the time sinks, so we leaned on customers to tell us about what they were doing or we looked through the code repeatedly to try and find a problem. I realized none of our software was effectively logging what was going on behind the scenes — it just wasn’t part of the way we wrote software.
That realization changed the way I write code. One of the first questions I have now when I start a new project is, “What logging mechanisms are we going to rely on?” I’m not going to go in depth here about a logging framework, as there are many excellent ones. I’m going to focus on several types of logging you can utilize and why, particularly thinking about solving the types of problems I used to experience.
So what types of logging are must haves?
The idea behind error logging is to capture details of any unhandled exception that occurs within the software.
Ideally, we should capture all the details we can about the exception to review, including the exception message and stack trace. The great thing about capturing detailed exception data is that we can usually pin down an exact line of code that’s failing. The other thing that’s nice about error logging is it’s usually pretty easy to set up. Many programming frameworks have a built-in method for capturing unhandled exceptions so you can record information about them. There are also third-party tools you can install, such as Sentry and Application Insights, that give detailed exception data in a palatable way.
Error logging is a must — a bare minimum that every program must have. It’s a huge red flag to me if a piece of software doesn’t capture errors for developers to review when there’s a problem. Setting up a database table to store unhandled exception data is a fairly simple task that can save hours trying to track down a problem.
To sum up: Error logging helps us find where code is failing.
Trace logging is the process of saving informative statements about what a program is doing at any given time.
Trace statements may be simple statements of fact such as “User xxx accessed cart,” or they may include entire data structures/objects that are being worked with at a given time in the code. Trace statements are a way for developers to see the path that code is following when they aren’t the ones running it. This often eliminates that call to the user to have them walk through the issue. The trace logs can be looked up, often revealing the problem to the developers and allowing them to see what was happening.
Many frameworks support trace logging, and it’s simple to set up trace logs to save to a database or text file. We’ll often set different status levels on trace logs, such as “information,” “warning,” etc., to be able to identify and filter these logs more granularly.
When first starting a project, I often put in tons of trace logging statements (probably far more than necessary). I’ll log what’s going on, the state of different objects in my code, and so on. I often pare the trace logging down later once I’ve had a chance to test it and see what logging is valuable and what isn’t. By making a habit of writing trace logs while I write my code, I end up with better trace logs that aren’t an afterthought. They reflect the intent of the code.
There are one or two issues that can arise with trace logging. If overdone, it may fill too much space in a database or on a hard drive. Make sure you clean these logs up so you don’t suddenly run out of space. There’s also the danger we might log something sensitive and that someone looking at that log could see something they shouldn’t. If you’re cognizant of this, it’s usually not an issue, but it is something to keep in mind.
To sum up: Trace logging helps us find the why code is failing.
The final type of logging I’m going to talk about is audit logging, the process of formally logging actions users take within the system.
I like to think of audit logging as an insurance policy. Any time a user takes an action that impacts data the software uses, this should be logged. Not only can this be helpful information for trace logging, it also holds users accountable for their actions and makes it easier to figure out if something in your system isn’t working due to a bug or a user mistake.
As with the other two, audit logging is usually easy to set up. There are frameworks and tools that can help with it if needed. However, I’ll often set up a simple table specifically designated as the audit log that tracks users actions and how and when those actions affected system data. I make this logging mechanism an initial development step on projects so I can add audit log data as I go, whenever it makes sense.
To sum up: Audit logging helps us find who has touched code that is failing.
So there you have it. By making error logging, trace logging and audit logging a priority in my development process, I’ve eliminated most of those late nights, weekends and frustrating user phone calls.
It seems like such a simple thing, but it’s something I used to take for granted and that I’ve seen many other developers ignore in the name of saving time or shipping faster. The truth is the minuscule amount of extra time and effort made to have a good logging strategy in your development process will save you lots more time down the road when it comes to supporting your application. That’s why logging is a developer’s secret weapon.
John Kuefler started in technology at a young age, making simple websites at the age of 7. His passion grew from there, and he moved on to earn a bachelor’s degree in computer information systems from Pittsburg State University, followed by an MBA. John worked in the public sector as a programmer, software architect and manager of development teams. John then started providing freelance software development services, which soon grew into DevSquared, a software development company acquired by LimeLight Marketing after three years. John is now director of technology and partner at LimeLight Marketing.