🔷 📝 Logging Patterns - TuskLang for C# - "Logging Mastery"
📝 Logging Patterns - TuskLang for C# - "Logging Mastery"
Master logging patterns with TuskLang in your C# applications!
Logging is essential for debugging, monitoring, and auditing. This guide covers structured logging, log levels, log aggregation, correlation IDs, and real-world logging scenarios for TuskLang in C# environments.
📋 Logging Philosophy
"We Don't Bow to Any King"
- Log everything - Every action, every decision - Structured data - JSON logs for easy parsing - Context matters - Include relevant metadata - Performance first - Async logging, no blocking - Searchable logs - Make logs easy to find and analyze🏗️ Structured Logging
Example: Structured Logging Service
// StructuredLoggingService.cs
using Serilog;
using Serilog.Events;public class StructuredLoggingService
{
private readonly ILogger _logger;
private readonly TuskLang _parser;
private readonly Dictionary<string, object> _globalProperties;
public StructuredLoggingService(ILogger logger)
{
_logger = logger;
_parser = new TuskLang();
_globalProperties = new Dictionary<string, object>();
LoadLoggingConfiguration();
}
private void LoadLoggingConfiguration()
{
var config = _parser.ParseFile("config/logging.tsk");
// Set global properties
_globalProperties["application"] = config["application_name"].ToString();
_globalProperties["version"] = config["version"].ToString();
_globalProperties["environment"] = config["environment"].ToString();
// Configure Serilog
var logConfig = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.Enrich.WithProperty("Application", _globalProperties["application"])
.Enrich.WithProperty("Version", _globalProperties["version"])
.Enrich.WithProperty("Environment", _globalProperties["environment"]);
// Add sinks based on configuration
var sinks = config["sinks"] as Dictionary<string, object>;
foreach (var sink in sinks ?? new Dictionary<string, object>())
{
ConfigureSink(logConfig, sink.Key, sink.Value as Dictionary<string, object>);
}
Log.Logger = logConfig.CreateLogger();
}
private void ConfigureSink(LoggerConfiguration logConfig, string sinkName, Dictionary<string, object>? sinkConfig)
{
switch (sinkName.ToLower())
{
case "console":
logConfig.WriteTo.Console(
outputTemplate: sinkConfig?["output_template"]?.ToString() ??
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
);
break;
case "file":
logConfig.WriteTo.File(
path: sinkConfig?["path"]?.ToString() ?? "logs/app-.txt",
rollingInterval: RollingInterval.Day,
outputTemplate: sinkConfig?["output_template"]?.ToString() ??
"{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
);
break;
case "elasticsearch":
logConfig.WriteTo.Elasticsearch(
new Uri(sinkConfig?["url"]?.ToString() ?? "http://localhost:9200"),
indexFormat: sinkConfig?["index_format"]?.ToString() ?? "logs-{0:yyyy.MM.dd}"
);
break;
}
}
public void LogInformation(string message, Dictionary<string, object>? properties = null)
{
var enrichedProperties = MergeProperties(_globalProperties, properties);
_logger.Information(message, enrichedProperties);
}
public void LogWarning(string message, Dictionary<string, object>? properties = null)
{
var enrichedProperties = MergeProperties(_globalProperties, properties);
_logger.Warning(message, enrichedProperties);
}
public void LogError(string message, Exception? exception = null, Dictionary<string, object>? properties = null)
{
var enrichedProperties = MergeProperties(_globalProperties, properties);
if (exception != null)
{
_logger.Error(exception, message, enrichedProperties);
}
else
{
_logger.Error(message, enrichedProperties);
}
}
public void LogDebug(string message, Dictionary<string, object>? properties = null)
{
var enrichedProperties = MergeProperties(_globalProperties, properties);
_logger.Debug(message, enrichedProperties);
}
private Dictionary<string, object> MergeProperties(Dictionary<string, object> global, Dictionary<string, object>? local)
{
var merged = new Dictionary<string, object>(global);
if (local != null)
{
foreach (var kvp in local)
{
merged[kvp.Key] = kvp.Value;
}
}
return merged;
}
}
🔗 Correlation ID Pattern
Example: Correlation ID Service
// CorrelationIdService.cs
using System.Diagnostics;public class CorrelationIdService
{
private readonly AsyncLocal<string> _correlationId = new AsyncLocal<string>();
private readonly ILogger<CorrelationIdService> _logger;
public CorrelationIdService(ILogger<CorrelationIdService> logger)
{
_logger = logger;
}
public string GetCorrelationId()
{
if (string.IsNullOrEmpty(_correlationId.Value))
{
_correlationId.Value = GenerateCorrelationId();
_logger.LogDebug("Generated new correlation ID: {CorrelationId}", _correlationId.Value);
}
return _correlationId.Value;
}
public void SetCorrelationId(string correlationId)
{
_correlationId.Value = correlationId;
_logger.LogDebug("Set correlation ID: {CorrelationId}", correlationId);
}
public IDisposable CreateScope(string operationName)
{
var correlationId = GetCorrelationId();
return new CorrelationScope(correlationId, operationName, _logger);
}
private string GenerateCorrelationId()
{
return $"{DateTime.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}";
}
}
public class CorrelationScope : IDisposable
{
private readonly string _correlationId;
private readonly string _operationName;
private readonly ILogger _logger;
private readonly Stopwatch _stopwatch;
public CorrelationScope(string correlationId, string operationName, ILogger logger)
{
_correlationId = correlationId;
_operationName = operationName;
_logger = logger;
_stopwatch = Stopwatch.StartNew();
_logger.LogInformation("Starting operation {OperationName} with correlation ID {CorrelationId}",
operationName, correlationId);
}
public void Dispose()
{
_stopwatch.Stop();
_logger.LogInformation("Completed operation {OperationName} with correlation ID {CorrelationId} in {ElapsedMs}ms",
_operationName, _correlationId, _stopwatch.ElapsedMilliseconds);
}
}
📊 Log Aggregation
Example: Log Aggregation Service
// LogAggregationService.cs
public class LogAggregationService
{
private readonly StructuredLoggingService _loggingService;
private readonly CorrelationIdService _correlationService;
private readonly ILogger<LogAggregationService> _logger;
private readonly Dictionary<string, LogMetrics> _metrics;
public LogAggregationService(
StructuredLoggingService loggingService,
CorrelationIdService correlationService,
ILogger<LogAggregationService> logger)
{
_loggingService = loggingService;
_correlationService = correlationService;
_logger = logger;
_metrics = new Dictionary<string, LogMetrics>();
}
public void LogApiRequest(string method, string path, int statusCode, long durationMs)
{
var correlationId = _correlationService.GetCorrelationId();
var properties = new Dictionary<string, object>
{
["correlation_id"] = correlationId,
["http_method"] = method,
["http_path"] = path,
["http_status_code"] = statusCode,
["duration_ms"] = durationMs,
["log_type"] = "api_request"
};
var level = statusCode >= 400 ? "Warning" : "Information";
var message = $"{method} {path} returned {statusCode} in {durationMs}ms";
switch (level)
{
case "Warning":
_loggingService.LogWarning(message, properties);
break;
default:
_loggingService.LogInformation(message, properties);
break;
}
UpdateMetrics("api_requests", properties);
}
public void LogDatabaseOperation(string operation, string table, long durationMs, bool success)
{
var correlationId = _correlationService.GetCorrelationId();
var properties = new Dictionary<string, object>
{
["correlation_id"] = correlationId,
["db_operation"] = operation,
["db_table"] = table,
["duration_ms"] = durationMs,
["success"] = success,
["log_type"] = "database_operation"
};
var level = success ? "Information" : "Error";
var message = $"Database {operation} on {table} {(success ? "succeeded" : "failed")} in {durationMs}ms";
switch (level)
{
case "Error":
_loggingService.LogError(message, properties: properties);
break;
default:
_loggingService.LogInformation(message, properties);
break;
}
UpdateMetrics("database_operations", properties);
}
public void LogBusinessEvent(string eventType, string entityType, string entityId, Dictionary<string, object>? additionalData = null)
{
var correlationId = _correlationService.GetCorrelationId();
var properties = new Dictionary<string, object>
{
["correlation_id"] = correlationId,
["event_type"] = eventType,
["entity_type"] = entityType,
["entity_id"] = entityId,
["log_type"] = "business_event"
};
if (additionalData != null)
{
foreach (var kvp in additionalData)
{
properties[kvp.Key] = kvp.Value;
}
}
var message = $"Business event {eventType} for {entityType} {entityId}";
_loggingService.LogInformation(message, properties);
UpdateMetrics("business_events", properties);
}
private void UpdateMetrics(string metricType, Dictionary<string, object> properties)
{
if (!_metrics.ContainsKey(metricType))
{
_metrics[metricType] = new LogMetrics();
}
var metrics = _metrics[metricType];
metrics.Count++;
metrics.LastOccurrence = DateTime.UtcNow;
if (properties.ContainsKey("duration_ms"))
{
var duration = Convert.ToInt64(properties["duration_ms"]);
metrics.TotalDuration += duration;
metrics.AverageDuration = metrics.TotalDuration / metrics.Count;
}
}
public async Task<Dictionary<string, object>> GetLogMetricsAsync()
{
var metrics = new Dictionary<string, object>();
foreach (var kvp in _metrics)
{
metrics[kvp.Key] = new Dictionary<string, object>
{
["count"] = kvp.Value.Count,
["total_duration_ms"] = kvp.Value.TotalDuration,
["average_duration_ms"] = kvp.Value.AverageDuration,
["last_occurrence"] = kvp.Value.LastOccurrence.ToString("yyyy-MM-dd HH:mm:ss")
};
}
return metrics;
}
}public class LogMetrics
{
public int Count { get; set; }
public long TotalDuration { get; set; }
public double AverageDuration { get; set; }
public DateTime LastOccurrence { get; set; }
}
🔍 Log Analysis
Example: Log Analysis Service
// LogAnalysisService.cs
public class LogAnalysisService
{
private readonly StructuredLoggingService _loggingService;
private readonly ILogger<LogAnalysisService> _logger;
private readonly List<LogEntry> _logBuffer;
private readonly int _bufferSize;
public LogAnalysisService(
StructuredLoggingService loggingService,
ILogger<LogAnalysisService> logger)
{
_loggingService = loggingService;
_logger = logger;
_logBuffer = new List<LogEntry>();
_bufferSize = 1000;
}
public void AddLogEntry(LogEntry entry)
{
_logBuffer.Add(entry);
if (_logBuffer.Count >= _bufferSize)
{
AnalyzeLogs();
_logBuffer.Clear();
}
}
private void AnalyzeLogs()
{
var analysis = new Dictionary<string, object>();
// Error rate analysis
var errorCount = _logBuffer.Count(l => l.Level == "Error");
var errorRate = (double)errorCount / _logBuffer.Count;
analysis["error_rate"] = errorRate;
// Performance analysis
var apiRequests = _logBuffer.Where(l => l.Properties.ContainsKey("log_type") &&
l.Properties["log_type"].ToString() == "api_request").ToList();
if (apiRequests.Any())
{
var avgResponseTime = apiRequests.Average(r => Convert.ToInt64(r.Properties["duration_ms"]));
analysis["avg_api_response_time_ms"] = avgResponseTime;
}
// Database performance analysis
var dbOperations = _logBuffer.Where(l => l.Properties.ContainsKey("log_type") &&
l.Properties["log_type"].ToString() == "database_operation").ToList();
if (dbOperations.Any())
{
var avgDbTime = dbOperations.Average(r => Convert.ToInt64(r.Properties["duration_ms"]));
analysis["avg_db_operation_time_ms"] = avgDbTime;
}
// Log analysis results
_loggingService.LogInformation("Log analysis completed", analysis);
// Alert on high error rates
if (errorRate > 0.1) // 10% error rate threshold
{
_loggingService.LogWarning("High error rate detected: {ErrorRate:P}",
new Dictionary<string, object> { ["error_rate"] = errorRate });
}
}
}public class LogEntry
{
public DateTime Timestamp { get; set; }
public string Level { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty;
public Dictionary<string, object> Properties { get; set; } = new Dictionary<string, object>();
public Exception? Exception { get; set; }
}
🛠️ Real-World Logging Scenarios
- API monitoring: Log all API requests and responses - Database operations: Log database queries and performance - Business events: Log important business operations - Error tracking: Log and analyze error patterns🧩 Best Practices
- Use structured logging with JSON format - Include correlation IDs for request tracing - Log at appropriate levels - Don't log sensitive data - Use async logging for performance🏁 You're Ready!
You can now: - Implement structured logging - Use correlation IDs for tracing - Aggregate and analyze logs - Monitor application performance
Next: Performance Monitoring
---
"We don't bow to any king" - Your logging mastery, your observability excellence, your debugging power.
Log everything. Debug anything. 📝