A Serilog enricher that adds call stack information to log events in an exception-like format. Displays call stacks as: Method:Line --> Method:Line --> Method:Line for intuitive debugging and tracing.
$ dotnet add package Serilog.Enrichers.CallStackA Serilog enricher that adds call stack information to log events in an exception-like format. This enricher helps with debugging and tracing by providing detailed context about where log events originated, displaying the call stack in an intuitive format similar to exception stack traces.
Last updated: August 2025
Method:Line --> Method:Line --> Method:LineCallStack property for cleaner logsdotnet add package Serilog.Enrichers.CallStack
using Serilog;
using Serilog.Enrichers.CallStack;
var logger = new LoggerConfiguration()
.Enrich.WithCallStack()
.WriteTo.Console()
.CreateLogger();
logger.Information("Hello, world!");
This will produce log output similar to:
[15:30:45 INF] Hello, world! {CallStack="Program.Main:12 --> Program.<Main>$:8"}
var logger = new LoggerConfiguration()
.Enrich.WithCallStack(config => config
.WithCallStackFormat(useExceptionLikeFormat: true, maxFrames: 3)
.WithMethodParameters(includeParameters: true)
.WithFullNames(fullTypeName: false)
.SkipNamespace("System")
.SkipNamespace("Microsoft"))
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} | {CallStack}{NewLine}{Exception}")
.CreateLogger();
var logger = new LoggerConfiguration()
.Enrich.WithCallStack(config => config
.WithCallStackFormat(useExceptionLikeFormat: false) // Use individual properties
.WithIncludes(methodName: true, typeName: true, fileName: true, lineNumber: true)
.WithFullNames(fullTypeName: true)
.WithMethodParameters(includeParameters: true))
.WriteTo.Console()
.CreateLogger();
Choose between the new exception-like format or legacy individual properties:
var config = new CallStackEnricherConfiguration()
.WithCallStackFormat(
useExceptionLikeFormat: true, // Default: true
maxFrames: 5, // Default: 5, -1 for unlimited
callStackPropertyName: "CallStack"); // Default: "CallStack"
Exception-like Format Output:
CallStack: "UserService.ProcessUser:45 --> UserController.CreateUser:23 --> Program.Main:12"
Legacy Format Output:
MethodName: "ProcessUser", TypeName: "UserService", FileName: "UserService.cs", LineNumber: 45
var config = new CallStackEnricherConfiguration()
.WithIncludes(
methodName: true, // Include method names
typeName: true, // Include type names
fileName: true, // Include file names
lineNumber: true, // Include line numbers
columnNumber: false, // Include column numbers
assemblyName: false); // Include assembly names
Customize the property names used in log events:
var config = new CallStackEnricherConfiguration()
.WithPropertyNames(
methodName: "Method",
typeName: "Class",
fileName: "File",
lineNumber: "Line",
columnNumber: "Column",
assemblyName: "Assembly");
Control whether to use full names (with namespaces/paths) or short names:
var config = new CallStackEnricherConfiguration()
.WithFullNames(
fullTypeName: true, // Use "MyApp.Services.UserService" vs "UserService"
fullFileName: true, // Use full path vs just filename
fullParameterTypes: true); // Use full type names in parameters
Include method parameter information in the method name:
var config = new CallStackEnricherConfiguration()
.WithMethodParameters(
includeParameters: true,
useFullParameterTypes: false);
// Results in: "ProcessUser(String name, Int32 id)" instead of just "ProcessUser"
Skip specific namespaces or types when walking the call stack:
var config = new CallStackEnricherConfiguration()
.SkipNamespace("System")
.SkipNamespace("Microsoft")
.SkipNamespace("Serilog")
.SkipType("MyApp.Infrastructure.LoggingWrapper");
Choose which frame in the call stack to capture:
var config = new CallStackEnricherConfiguration()
.WithFrameOffset(1); // Skip 1 frame up the call stack
Configure how exceptions during enrichment are handled:
var config = new CallStackEnricherConfiguration()
.WithExceptionHandling(
suppress: true, // Don't throw exceptions
onException: ex => Console.WriteLine($"Enricher error: {ex.Message}"));
For production environments where you want minimal overhead:
var config = new CallStackEnricherConfiguration()
.WithIncludes(
methodName: true,
typeName: true,
fileName: false, // Skip file names to reduce overhead
lineNumber: false, // Skip line numbers
columnNumber: false,
assemblyName: false)
.WithFullNames(fullTypeName: false); // Use short type names
For development environments where you want maximum detail:
var config = new CallStackEnricherConfiguration()
.WithIncludes(
methodName: true,
typeName: true,
fileName: true,
lineNumber: true,
columnNumber: true,
assemblyName: true)
.WithFullNames(
fullTypeName: true,
fullFileName: true,
fullParameterTypes: true)
.WithMethodParameters(includeParameters: true)
.WithExceptionHandling(suppress: false); // Throw exceptions for debugging
Skip common framework types and focus on application code:
var config = new CallStackEnricherConfiguration()
.SkipNamespace("System")
.SkipNamespace("Microsoft")
.SkipNamespace("Serilog")
.SkipNamespace("Newtonsoft")
.SkipType("MyApp.Infrastructure.LoggingService")
.WithFrameOffset(0);
var logger = new LoggerConfiguration()
.Enrich.WithCallStack()
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} | Call Stack: {CallStack}{NewLine}{Exception}")
.CreateLogger();
var logger = new LoggerConfiguration()
.Enrich.WithCallStack(config => config.WithCallStackFormat(useExceptionLikeFormat: false))
.WriteTo.Console(outputTemplate:
"[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj} " +
"({TypeName}.{MethodName} in {FileName}:{LineNumber}){NewLine}{Exception}")
.CreateLogger();
var logger = new LoggerConfiguration()
.Enrich.WithCallStack()
.WriteTo.File(new JsonFormatter(), "log.json")
.CreateLogger();
var logger = new LoggerConfiguration()
.Enrich.WithCallStack()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();
For file names and line numbers to work properly, ensure your application is built with debug symbols:
<PropertyGroup>
<DebugType>portable</DebugType>
<DebugSymbols>true</DebugSymbols>
</PropertyGroup>
With the new exception-like format, log events include a single CallStack property:
{
"@t": "2025-07-29T00:30:45.123Z",
"@l": "Information",
"@m": "Processing user request",
"CallStack": "UserService.ProcessRequest(String userId, UserRequest request):45 --> UserController.CreateUser:23 --> Program.Main:12"
}
When using legacy format (useExceptionLikeFormat: false), individual properties are included:
{
"@t": "2025-07-29T00:30:45.123Z",
"@l": "Information",
"@m": "Processing user request",
"MethodName": "ProcessRequest(String userId, UserRequest request)",
"TypeName": "MyApp.Services.UserService",
"FileName": "UserService.cs",
"LineNumber": 45,
"ColumnNumber": 12,
"AssemblyName": "MyApp.Services"
}
maxFrames to control overhead (default: 5 frames)maxFrames accordinglyThis project is licensed under the MIT License - see the LICENSE file for details.
We welcome contributions to improve Serilog.Enrichers.CallStack! Here's how you can help:
mainIf you find a bug or have a feature request, please open an issue with:
For questions or discussions about the enricher, please open a GitHub discussion or issue.