C# Logging Key Considerations with .NET
Logging, an essential part of application development functions like an airplane’s black box. Whenever something goes wrong, logs are a great place to start figuring out what happened and how to fix it. But despite that, many developers treat logging as an afterthought, lacking proper planning or understanding before implementation.
Logging, specifically logging in the .NET framework, needs more attention. Both seasoned developers and newbies to .NET must be able to spot and correct the kinds of errors that can creep up and harm the quality of their logs.
How can you investigate problems in your app without the necessary information good logging provides? And how can you deliver solid software without correcting any errors or fixing any bugs? You need good logging practices.
The Importance of C# Logging
Structured logging, which includes .NET libraries like Serilog, NLog or Microsoft’s default library, makes logging optimization possible even when you’re dealing with a high volume. For cloud native apps, the OpenTelemetry standard can also help provide full observability into your app. But these tools, as helpful as they are, depend on your ability to avoid making mistakes while logging.
Bad or incomplete logging makes searching your backend more difficult. Logging the right way, on the other hand, can help you understand a specific bug in production and find the details you need to patch or fix errors. Logging mistakes are common, especially APIs that are string-based, and the copy-paste-modify method of adding logging code lends itself to errors. These things can result in small, overlooked issues that become bigger problems down the line that cost organizations time and money.
Good logging helps developers move faster and respond more efficiently to problems that arise. In turn, that makes infrastructure and DevOps more effective. This practice shouldn’t be deprioritized by developers and organizations that want to deploy secure, strong, high-quality software. So, what are some of the biggest and most common mistakes you can make when it comes to logging, and how can you fix or avoid them?
Message Syntax and Semantics
C# requires a correct message with the right information in the right location. But because the APIs rely on strings and untyped arguments, it’s easy to slip up and make a mistake. Here are some of the most common errors:
- Incorrect message syntax: Structured logging in .NET needs a specific syntax. Omitting specific brackets can turn into undetected errors and lead to log outputs that don’t have the information you need. Incorrect placeholders can also lead to mistakes — for example, each placeholder needs a property, which must adhere to the standard syntax for identifiers in most languages, which must start with a letter or underscore and contain only letters, numbers or underscores. Be careful of typos and make sure to close all your brackets correctly.
- Placeholder duplication: All placeholders in a logging message have to be unique, tying back to structured logging’s goal of specificity. Don’t repeat names.
- Incorrect placeholder order: Your placeholder order must match the argument order, but it’s easy to accidentally make a switch when reworking code — and harder to detect later.
- Manually crafted messages: Before structured logging, logging messages were often crafted with string concatenation. Now, though, logging backends like ILogger use the structured approach, which helps with queries and facilitates more efficient storage and indexing. Thanks to these solutions, you don’t need to manually craft them anymore.
- Using the wrong overload: Most C# logging frameworks have specific overloads that allow passing in a log level, event ID or exception. Passing overload values can cause your code to do something unintended. Make sure to use proper overloads to achieve better outcomes.
- Wrong logger category: ILogger’s generic flavor indicates the type the logger is for. The log output shows which class generated a particular log entry by emitting the name of the type you passed. It’s a useful feature, but can lead to mistakes upon setup, so be careful when employing it. Copying and pasting can be responsible for these kinds of mistakes, leading to a wrong interpretation of logs and lost time in diagnosing problems.
- Unlogged exception: When it comes to exceptions, emitted logs should contain the exception. You want to include all the details of an exception so it’s easier to find a problem if and when the time comes.
Conventions and Consistency
Logging exists in your entire codebases, meaning you need to set conventions to keep everything consistent and to help prevent information overload. Proper conventions can help you “tune out” irrelevant details and concentrate on a core problem; they also help during code modifications by ensuring objects are where you expect them to be. Both teams and individuals benefit from this practice. Here are a few things to keep in mind with conventions:
- Consistent placeholder naming: While it’s important to use different placeholders, they should still follow a consistent naming convention, just like variables, for increased ease of use.
- Logger storage fields: Creating a policy for naming your logger’s storage field will help you more easily find it — it stays consistent across the code base. In projects with a multitude of injected loggers, you should always use the same field name.
Performance and Log File Considerations
There are still a few other things to think about when logging. Paying attention to these considerations will help keep your logs in a good place:
- Trace.Write… Methods: While we now have ILogger and other options for structured logging, it’s important to remember that our choices used to be limited. Legacy codebases may use the Trace and TraceSwitch class, meaning they require a different kind of attention. Developers should avoid the Trace.Write… family of methods specifically and stick with TraceInformation, TraceWarning or TraceError when needed.
- Overlogging: You can have too much of a good thing. Overlogging can lead to performance degradation, log file overload and code that’s difficult to read or understand. Be strategic with your logs. You can also log too many exceptions. Avoid both logging and rethrowing an exception and, instead, stick to one so your files are easier to use.
What Now?
Logging code involves several different moving parts and key considerations so you can avoid making expensive, time-consuming mistakes. However, we only covered a few things to know as you execute the practice.
Solid logs, though, help ensure easier mitigation of problems and patching of bugs down the line so that you’re delivering software quickly and seamlessly. Reading up on OpenTelemetry can help with logging code — and so can Sonar. Keeping the quirks of C# logging in mind, you can ensure the clean code you write leads to the kind of high-quality software your organization needs.