Tags & Context
Tags and context help you organize, categorize, and filter your logs effectively. Use tags to mark logs with specific categories and context to provide additional structured information.
Adding Context to Logs
Use the context parameter in logging methods to add structured information:
C#
// Basic context
Log.Info("User action completed", new { UserId = 123, Action = "Login" });
// Complex context
Log.Error("Payment processing failed", new
{
TransactionId = "tx_123",
Amount = 99.99,
Currency = "USD",
PaymentMethod = "CreditCard",
Timestamp = DateTime.UtcNow
}, ex);// Basic context
Log.Info("User action completed", new { UserId = 123, Action = "Login" });
// Complex context
Log.Error("Payment processing failed", new
{
TransactionId = "tx_123",
Amount = 99.99,
Currency = "USD",
PaymentMethod = "CreditCard",
Timestamp = DateTime.UtcNow
}, ex);Tagging Strategies
Functional Tags
Use tags to categorize logs by functionality:
C#
// Authentication logs
Log.Info("User login attempt", new { Tag = "Authentication", UserId = "123" });
Log.Warn("Failed login attempt", new { Tag = "Authentication", Reason = "InvalidPassword" });
// Payment logs
Log.Info("Payment initiated", new { Tag = "Payment", TransactionId = "tx_123" });
Log.Error("Payment failed", new { Tag = "Payment", ErrorCode = "INSUFFICIENT_FUNDS" }, ex);
// Order processing logs
Log.Info("Order created", new { Tag = "OrderProcessing", OrderId = "ord_123" });
Log.Debug("Inventory checked", new { Tag = "OrderProcessing", ProductId = "prod_456" });// Authentication logs
Log.Info("User login attempt", new { Tag = "Authentication", UserId = "123" });
Log.Warn("Failed login attempt", new { Tag = "Authentication", Reason = "InvalidPassword" });
// Payment logs
Log.Info("Payment initiated", new { Tag = "Payment", TransactionId = "tx_123" });
Log.Error("Payment failed", new { Tag = "Payment", ErrorCode = "INSUFFICIENT_FUNDS" }, ex);
// Order processing logs
Log.Info("Order created", new { Tag = "OrderProcessing", OrderId = "ord_123" });
Log.Debug("Inventory checked", new { Tag = "OrderProcessing", ProductId = "prod_456" });Component Tags
Tag logs by system component or service:
C#
// Database operations
Log.Debug("Query executed", new { Tag = "Database", Query = "SELECT * FROM Users", Duration = 45 });
Log.Error("Connection failed", new { Tag = "Database", ConnectionString = "***" }, ex);
// External API calls
Log.Info("API request started", new { Tag = "ExternalAPI", Endpoint = "/payments", Method = "POST" });
Log.Warn("API rate limited", new { Tag = "ExternalAPI", Service = "PaymentGateway" });
// Cache operations
Log.Debug("Cache hit", new { Tag = "Cache", Key = "user_123", TTL = 300 });
Log.Info("Cache miss", new { Tag = "Cache", Key = "product_456" });// Database operations
Log.Debug("Query executed", new { Tag = "Database", Query = "SELECT * FROM Users", Duration = 45 });
Log.Error("Connection failed", new { Tag = "Database", ConnectionString = "***" }, ex);
// External API calls
Log.Info("API request started", new { Tag = "ExternalAPI", Endpoint = "/payments", Method = "POST" });
Log.Warn("API rate limited", new { Tag = "ExternalAPI", Service = "PaymentGateway" });
// Cache operations
Log.Debug("Cache hit", new { Tag = "Cache", Key = "user_123", TTL = 300 });
Log.Info("Cache miss", new { Tag = "Cache", Key = "product_456" });Severity Tags
Combine tags with severity for better categorization:
C#
// Business critical operations
Log.Info("Critical business process started", new { Tag = "BusinessCritical", Process = "MonthlyBilling" });
Log.Error("Critical process failed", new { Tag = "BusinessCritical", Impact = "Revenue" }, ex);
// Security events
Log.Warn("Suspicious activity detected", new { Tag = "Security", Activity = "MultipleFailedLogins" });
Log.Critical("Security breach detected", new { Tag = "Security", Severity = "High" });
// Performance monitoring
Log.Debug("Performance threshold exceeded", new { Tag = "Performance", Metric = "ResponseTime", Value = 5000 });
Log.Warn("Memory usage high", new { Tag = "Performance", MemoryUsage = "85%" });// Business critical operations
Log.Info("Critical business process started", new { Tag = "BusinessCritical", Process = "MonthlyBilling" });
Log.Error("Critical process failed", new { Tag = "BusinessCritical", Impact = "Revenue" }, ex);
// Security events
Log.Warn("Suspicious activity detected", new { Tag = "Security", Activity = "MultipleFailedLogins" });
Log.Critical("Security breach detected", new { Tag = "Security", Severity = "High" });
// Performance monitoring
Log.Debug("Performance threshold exceeded", new { Tag = "Performance", Metric = "ResponseTime", Value = 5000 });
Log.Warn("Memory usage high", new { Tag = "Performance", MemoryUsage = "85%" });Contextual Information Patterns
Request Context
C#
public class RequestContext
{
public string RequestId { get; set; }
public string UserId { get; set; }
public string UserAgent { get; set; }
public string IPAddress { get; set; }
public DateTime RequestTime { get; set; }
}
// Usage
var context = new RequestContext
{
RequestId = HttpContext.TraceIdentifier,
UserId = User.Identity.Name,
UserAgent = Request.Headers["User-Agent"],
IPAddress = HttpContext.Connection.RemoteIpAddress?.ToString(),
RequestTime = DateTime.UtcNow
};
Log.Info("Request processed", context);public class RequestContext
{
public string RequestId { get; set; }
public string UserId { get; set; }
public string UserAgent { get; set; }
public string IPAddress { get; set; }
public DateTime RequestTime { get; set; }
}
// Usage
var context = new RequestContext
{
RequestId = HttpContext.TraceIdentifier,
UserId = User.Identity.Name,
UserAgent = Request.Headers["User-Agent"],
IPAddress = HttpContext.Connection.RemoteIpAddress?.ToString(),
RequestTime = DateTime.UtcNow
};
Log.Info("Request processed", context);Business Context
C#
public class BusinessContext
{
public string TenantId { get; set; }
public string BusinessUnit { get; set; }
public string WorkflowId { get; set; }
public string ProcessStep { get; set; }
}
// Usage
var businessContext = new BusinessContext
{
TenantId = "tenant_123",
BusinessUnit = "Sales",
WorkflowId = "workflow_456",
ProcessStep = "ApprovalPending"
};
Log.Info("Business process updated", businessContext);public class BusinessContext
{
public string TenantId { get; set; }
public string BusinessUnit { get; set; }
public string WorkflowId { get; set; }
public string ProcessStep { get; set; }
}
// Usage
var businessContext = new BusinessContext
{
TenantId = "tenant_123",
BusinessUnit = "Sales",
WorkflowId = "workflow_456",
ProcessStep = "ApprovalPending"
};
Log.Info("Business process updated", businessContext);Technical Context
C#
public class TechnicalContext
{
public string ServiceName { get; set; }
public string Version { get; set; }
public string Environment { get; set; }
public string InstanceId { get; set; }
public Dictionary<string, object> Metrics { get; set; }
}
// Usage
var techContext = new TechnicalContext
{
ServiceName = "OrderService",
Version = "2.1.0",
Environment = "Production",
InstanceId = Environment.MachineName,
Metrics = new Dictionary<string, object>
{
["ResponseTime"] = 150,
["MemoryUsage"] = "45%",
["ActiveConnections"] = 23
}
};
Log.Info("Service health check", techContext);public class TechnicalContext
{
public string ServiceName { get; set; }
public string Version { get; set; }
public string Environment { get; set; }
public string InstanceId { get; set; }
public Dictionary<string, object> Metrics { get; set; }
}
// Usage
var techContext = new TechnicalContext
{
ServiceName = "OrderService",
Version = "2.1.0",
Environment = "Production",
InstanceId = Environment.MachineName,
Metrics = new Dictionary<string, object>
{
["ResponseTime"] = 150,
["MemoryUsage"] = "45%",
["ActiveConnections"] = 23
}
};
Log.Info("Service health check", techContext);Structured Logging Patterns
Hierarchical Context
C#
var orderContext = new
{
Order = new
{
Id = "ord_123",
Status = "Processing",
Customer = new
{
Id = "cust_456",
Tier = "Premium"
},
Items = new[]
{
new { ProductId = "prod_789", Quantity = 2, Price = 29.99 },
new { ProductId = "prod_101", Quantity = 1, Price = 49.99 }
}
},
Processing = new
{
Stage = "PaymentValidation",
Attempts = 1,
StartTime = DateTime.UtcNow
}
};
Log.Info("Order processing stage completed", orderContext);var orderContext = new
{
Order = new
{
Id = "ord_123",
Status = "Processing",
Customer = new
{
Id = "cust_456",
Tier = "Premium"
},
Items = new[]
{
new { ProductId = "prod_789", Quantity = 2, Price = 29.99 },
new { ProductId = "prod_101", Quantity = 1, Price = 49.99 }
}
},
Processing = new
{
Stage = "PaymentValidation",
Attempts = 1,
StartTime = DateTime.UtcNow
}
};
Log.Info("Order processing stage completed", orderContext);Event Sourcing Context
C#
public class EventContext
{
public string EventType { get; set; }
public string AggregateId { get; set; }
public int Version { get; set; }
public DateTime Timestamp { get; set; }
public object EventData { get; set; }
}
// Usage
var eventContext = new EventContext
{
EventType = "OrderCreated",
AggregateId = "order_123",
Version = 1,
Timestamp = DateTime.UtcNow,
EventData = new { CustomerId = "cust_456", Amount = 99.99 }
};
Log.Info("Domain event processed", eventContext);public class EventContext
{
public string EventType { get; set; }
public string AggregateId { get; set; }
public int Version { get; set; }
public DateTime Timestamp { get; set; }
public object EventData { get; set; }
}
// Usage
var eventContext = new EventContext
{
EventType = "OrderCreated",
AggregateId = "order_123",
Version = 1,
Timestamp = DateTime.UtcNow,
EventData = new { CustomerId = "cust_456", Amount = 99.99 }
};
Log.Info("Domain event processed", eventContext);Service Layer Integration
Service Context Pattern
C#
public abstract class BaseService
{
protected void LogWithContext(string message, object additionalContext = null)
{
var serviceContext = new
{
Service = GetType().Name,
Timestamp = DateTime.UtcNow,
Context = additionalContext
};
Log.Info(message, serviceContext);
}
protected void LogErrorWithContext(string message, Exception exception, object additionalContext = null)
{
var serviceContext = new
{
Service = GetType().Name,
Timestamp = DateTime.UtcNow,
Context = additionalContext
};
Log.Error(message, serviceContext, exception);
}
}
public class OrderService : BaseService
{
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
LogWithContext("Order creation started", new { CustomerId = request.CustomerId });
try
{
var order = await ProcessOrderAsync(request);
LogWithContext("Order created successfully", new { OrderId = order.Id });
return order;
}
catch (Exception ex)
{
LogErrorWithContext("Order creation failed", ex, new { RequestData = request });
throw;
}
}
}public abstract class BaseService
{
protected void LogWithContext(string message, object additionalContext = null)
{
var serviceContext = new
{
Service = GetType().Name,
Timestamp = DateTime.UtcNow,
Context = additionalContext
};
Log.Info(message, serviceContext);
}
protected void LogErrorWithContext(string message, Exception exception, object additionalContext = null)
{
var serviceContext = new
{
Service = GetType().Name,
Timestamp = DateTime.UtcNow,
Context = additionalContext
};
Log.Error(message, serviceContext, exception);
}
}
public class OrderService : BaseService
{
public async Task<Order> CreateOrderAsync(CreateOrderRequest request)
{
LogWithContext("Order creation started", new { CustomerId = request.CustomerId });
try
{
var order = await ProcessOrderAsync(request);
LogWithContext("Order created successfully", new { OrderId = order.Id });
return order;
}
catch (Exception ex)
{
LogErrorWithContext("Order creation failed", ex, new { RequestData = request });
throw;
}
}
}Filtering and Querying
Searchable Context
Structure context for easy searching and filtering:
C#
// Good for filtering
Log.Info("User action", new
{
Category = "UserManagement",
Action = "PasswordReset",
UserId = "user_123",
Success = true,
Duration = 250
});
// Good for metrics
Log.Info("API response", new
{
Category = "API",
Endpoint = "/api/orders",
Method = "POST",
StatusCode = 201,
ResponseTime = 150,
RequestSize = 1024,
ResponseSize = 512
});// Good for filtering
Log.Info("User action", new
{
Category = "UserManagement",
Action = "PasswordReset",
UserId = "user_123",
Success = true,
Duration = 250
});
// Good for metrics
Log.Info("API response", new
{
Category = "API",
Endpoint = "/api/orders",
Method = "POST",
StatusCode = 201,
ResponseTime = 150,
RequestSize = 1024,
ResponseSize = 512
});Time-Series Context
C#
public static class MetricsLogger
{
public static void LogMetric(string metricName, double value, Dictionary<string, string> tags = null)
{
var context = new
{
MetricType = "Measurement",
MetricName = metricName,
Value = value,
Timestamp = DateTime.UtcNow,
Tags = tags ?? new Dictionary<string, string>()
};
Log.Info("Metric recorded", context);
}
}
// Usage
MetricsLogger.LogMetric("response_time", 150.5, new Dictionary<string, string>
{
["endpoint"] = "/api/orders",
["method"] = "GET",
["status"] = "success"
});public static class MetricsLogger
{
public static void LogMetric(string metricName, double value, Dictionary<string, string> tags = null)
{
var context = new
{
MetricType = "Measurement",
MetricName = metricName,
Value = value,
Timestamp = DateTime.UtcNow,
Tags = tags ?? new Dictionary<string, string>()
};
Log.Info("Metric recorded", context);
}
}
// Usage
MetricsLogger.LogMetric("response_time", 150.5, new Dictionary<string, string>
{
["endpoint"] = "/api/orders",
["method"] = "GET",
["status"] = "success"
});Advanced Context Patterns
Context Builders
C#
public class LogContextBuilder
{
private readonly Dictionary<string, object> _context = new();
public LogContextBuilder WithUser(string userId, string userName = null)
{
_context["UserId"] = userId;
if (userName != null) _context["UserName"] = userName;
return this;
}
public LogContextBuilder WithRequest(string requestId, string method = null, string path = null)
{
_context["RequestId"] = requestId;
if (method != null) _context["Method"] = method;
if (path != null) _context["Path"] = path;
return this;
}
public LogContextBuilder WithBusiness(string tenantId, string feature = null)
{
_context["TenantId"] = tenantId;
if (feature != null) _context["Feature"] = feature;
return this;
}
public LogContextBuilder WithMetric(string name, object value)
{
_context[$"Metric_{name}"] = value;
return this;
}
public object Build() => new Dictionary<string, object>(_context);
}
// Usage
var context = new LogContextBuilder()
.WithUser("user_123", "john.doe")
.WithRequest("req_456", "POST", "/api/orders")
.WithBusiness("tenant_789", "OrderProcessing")
.WithMetric("ProcessingTime", 250)
.Build();
Log.Info("Order processed", context);public class LogContextBuilder
{
private readonly Dictionary<string, object> _context = new();
public LogContextBuilder WithUser(string userId, string userName = null)
{
_context["UserId"] = userId;
if (userName != null) _context["UserName"] = userName;
return this;
}
public LogContextBuilder WithRequest(string requestId, string method = null, string path = null)
{
_context["RequestId"] = requestId;
if (method != null) _context["Method"] = method;
if (path != null) _context["Path"] = path;
return this;
}
public LogContextBuilder WithBusiness(string tenantId, string feature = null)
{
_context["TenantId"] = tenantId;
if (feature != null) _context["Feature"] = feature;
return this;
}
public LogContextBuilder WithMetric(string name, object value)
{
_context[$"Metric_{name}"] = value;
return this;
}
public object Build() => new Dictionary<string, object>(_context);
}
// Usage
var context = new LogContextBuilder()
.WithUser("user_123", "john.doe")
.WithRequest("req_456", "POST", "/api/orders")
.WithBusiness("tenant_789", "OrderProcessing")
.WithMetric("ProcessingTime", 250)
.Build();
Log.Info("Order processed", context);Context Inheritance
C#
public static class ContextManager
{
private static readonly AsyncLocal<Dictionary<string, object>> _context = new();
public static void SetContext(Dictionary<string, object> context)
{
_context.Value = new Dictionary<string, object>(context);
}
public static void AddToContext(string key, object value)
{
_context.Value ??= new Dictionary<string, object>();
_context.Value[key] = value;
}
public static Dictionary<string, object> GetCurrentContext()
{
return _context.Value ?? new Dictionary<string, object>();
}
public static void LogWithInheritedContext(string message, object additionalContext = null)
{
var context = GetCurrentContext();
if (additionalContext != null)
{
context["Additional"] = additionalContext;
}
Log.Info(message, context);
}
}
// Usage
ContextManager.SetContext(new Dictionary<string, object>
{
["RequestId"] = "req_123",
["UserId"] = "user_456"
});
ContextManager.AddToContext("ProcessingStage", "Validation");
ContextManager.LogWithInheritedContext("Validation completed");public static class ContextManager
{
private static readonly AsyncLocal<Dictionary<string, object>> _context = new();
public static void SetContext(Dictionary<string, object> context)
{
_context.Value = new Dictionary<string, object>(context);
}
public static void AddToContext(string key, object value)
{
_context.Value ??= new Dictionary<string, object>();
_context.Value[key] = value;
}
public static Dictionary<string, object> GetCurrentContext()
{
return _context.Value ?? new Dictionary<string, object>();
}
public static void LogWithInheritedContext(string message, object additionalContext = null)
{
var context = GetCurrentContext();
if (additionalContext != null)
{
context["Additional"] = additionalContext;
}
Log.Info(message, context);
}
}
// Usage
ContextManager.SetContext(new Dictionary<string, object>
{
["RequestId"] = "req_123",
["UserId"] = "user_456"
});
ContextManager.AddToContext("ProcessingStage", "Validation");
ContextManager.LogWithInheritedContext("Validation completed");Best Practices
Tags & Context Best Practices
- Use consistent naming: Establish standard tag and property names across your application
- Keep context relevant: Only include information that adds value for debugging or monitoring
- Structure for searchability: Design context objects that are easy to query and filter
- Avoid deep nesting: Keep context objects relatively flat for better readability
- Include identifiers: Always include relevant IDs (user, request, transaction, etc.)
Performance Considerations
Efficient Context Creation
C#
// ✅ Good - Lightweight context
Log.Info("Action completed", new { UserId = "123", Action = "Login" });
// ⚠️ Consider - Heavy context objects
Log.Info("Action completed", new {
UserId = "123",
CompleteUserObject = user, // Avoid large objects
AllRequestHeaders = headers // Avoid unnecessary data
});
// ✅ Good - Selective context
Log.Info("Action completed", new {
UserId = user.Id,
UserRole = user.Role,
RequestPath = Request.Path
});// ✅ Good - Lightweight context
Log.Info("Action completed", new { UserId = "123", Action = "Login" });
// ⚠️ Consider - Heavy context objects
Log.Info("Action completed", new {
UserId = "123",
CompleteUserObject = user, // Avoid large objects
AllRequestHeaders = headers // Avoid unnecessary data
});
// ✅ Good - Selective context
Log.Info("Action completed", new {
UserId = user.Id,
UserRole = user.Role,
RequestPath = Request.Path
});Next Steps
- Basic Logging - Learn fundamental logging methods
- Data Masking - Protect sensitive information in context
- Global Properties - Add consistent context to all logs