/

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);

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" });

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" });

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%" });

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);

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);

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);

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);

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);

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;
        }
    }
}

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
});

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"
});

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);

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");

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
});

Next Steps