Logging in C#
Logging in C# is a critical practice for monitoring application behavior, diagnosing issues, and gaining operational insights. Whether building desktop applications, APIs, or cloud-native services, effective logging allows developers and operators to understand how the system behaves in real time and after the fact.
In structured logging within C#, it's common to log endpoint URLs that consist of a base address and a path. To ensure safe and consistent logging, developers should use a centralized utility method to combine these values without risking exceptions or malformed URLs. This Snipp outlines two recommended approaches for implementing this functionality in a logging-safe way.
When production issues happen, logs should clearly show what started, what happened in between, and how it ended. A simple and consistent pattern using _logger with Application Insights can make troubleshooting much easier.
Here is a practical example for an operation like retrieving role assignments.
Start – Log inputs clearly
Log the action and key parameters at the beginning:
const string action = "Retrieve role assignments for scope";
var stopwatch = Stopwatch.StartNew();
_logger.LogInformation("Start {Action}. Scope={Scope}, RoleId={RoleId}",
action, scope, roleId);
This creates structured properties (Action, Scope, RoleId) that are searchable in Application Insights.
Flow – Log important checkpoints
Log meaningful steps inside the method:
_logger.LogInformation("Flow {Action} – calling external service. Scope={Scope}",
action, scope);
Only log major steps. Avoid too many details.
End – Log result summary and duration
When successful:
stopwatch.Stop();
_logger.LogInformation("End {Action}. Count={Count}, ElapsedMs={ElapsedMs}",
action, result.Count, stopwatch.ElapsedMilliseconds);
Include result summaries (like counts) and execution time. This helps detect performance issues.
Fail – Log exception with full context
On failure:
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError("Fail {Action}. Scope={Scope}, RoleId={RoleId}, ElapsedMs={ElapsedMs}, Exception={Exception}",
action, scope, roleId, stopwatch.ElapsedMilliseconds, ex);
throw;
}
Always pass the exception to LogError. Application Insights will capture the stack trace automatically.
Why This Works
- Messages are easy to read
- Properties are structured and searchable
- Start, Flow, End, and Fail follow the same pattern
- Operations teams can trace full execution paths
With this consistent approach, logs become a reliable story of what your system did — and why.
Comments