.NET Core logging and tracing
Logging and tracing are really two names for the same technique. The simple technique has been used since the early days of computers. It simply involves instrumenting an application to write output to be consumed later.
Reasons to use logging and tracing
This simple technique is surprisingly powerful. It can be used in situations where a debugger fails:
- Issues occurring over long periods of time, can be difficult to debug with a traditional debugger. Logs allow for detailed post-mortem review spanning long periods of time. In contrast, debuggers are constrained to real-time analysis.
- Multi-threaded applications and distributed applications are often difficult to debug. Attaching a debugger tends to modify behaviors. Detailed logs can be analyzed as needed to understand complex systems.
- Issues in distributed applications may arise from a complex interaction between many components and it may not be reasonable to connect a debugger to every part of the system.
- Many services shouldn't be stalled. Attaching a debugger often causes timeout failures.
- Issues aren't always foreseen. Logging and tracing are designed for low overhead so that programs can always be recording in case an issue occurs.
.NET Core APIs
The choice of which print-style API to use is up to you. The key differences are:
- Always enabled and always writes to the console.
- Useful for information that your customer may need to see in the release.
- Because it's the simplest approach, it's often used for ad-hoc temporary debugging. This debug code is often never checked in to source control.
- Only enabled when
DEBUGis defined by adding
#define DEBUGto your source or specifying the option
- Writes to an attached debugger.
*nixwrites to stderr if
- Use this API when creating logs that will be enabled only in debug builds.
- Only enabled when
The following APIs are more event oriented. Rather than logging simple strings they log event objects.
- EventSource is the primary root .NET Core tracing API.
- Available in all .NET Standard versions.
- Only allows tracing serializable objects.
- Can be consumed in-process via any EventListener instances configured to consume the EventSource.
- Can be consumed out-of-process via:
- Windows only.
- Writes messages to the Windows Event Log.
- System administrators expect fatal application error messages to appear in the Windows Event Log.
Distributed Tracing is a diagnostic technique that helps engineers localize failures and performance issues within applications, especially those that may be distributed across multiple machines or processes. This technique tracks requests through an application correlating together work done by different application components and separating it from other work the application may be doing for concurrent requests.
ILogger and logging frameworks
The low-level APIs may not be the right choice for your logging needs. You may want to consider a logging framework.
The ILogger interface has been used to create a common logging interface where the loggers can be inserted through dependency injection.
For instance, to allow you to make the best choice for your application .NET offers support for a selection of built-in and third-party frameworks:
Logging in .NET provides an overview of the logging techniques it supports.
C# string interpolation can simplify writing logging code.
The Exception.Message property is useful for logging exceptions.
The System.Diagnostics.StackTrace class can be useful to provide stack info in your logs.
String formatting can take noticeable CPU processing time.
In performance-critical applications, it's recommended that you:
- Avoid lots of logging when no one is listening. Avoid constructing costly logging messages by checking if logging is enabled first.
- Only log what's useful.
- Defer fancy formatting to the analysis stage.