🔷 Logging Patterns in C# with TuskLang

C# Documentation

Logging Patterns in C# with TuskLang

Overview

Effective logging is crucial for production applications. This guide covers comprehensive logging patterns for C# applications integrated with TuskLang, including structured logging, performance monitoring, and centralized log management.

Table of Contents

1. Structured Logging 2. Log Levels and Categories 3. Performance Logging 4. Error Logging 5. Audit Logging 6. Distributed Tracing 7. Log Aggregation 8. TuskLang Integration 9. Best Practices

Structured Logging

Basic Structured Logging

using Microsoft.Extensions.Logging;
using System.Collections.Generic;

public class UserService { private readonly ILogger<UserService> _logger;

public UserService(ILogger<UserService> logger) { _logger = logger; }

public async Task<User> CreateUserAsync(UserCreateRequest request) { using var scope = _logger.BeginScope(new Dictionary<string, object> { ["Operation"] = "CreateUser", ["RequestId"] = Guid.NewGuid(), ["Email"] = request.Email });

_logger.LogInformation("Starting user creation for email {Email}", request.Email);

try { var user = await _userRepository.CreateAsync(request); _logger.LogInformation("User created successfully. UserId: {UserId}, Email: {Email}", user.Id, user.Email);

return user; } catch (Exception ex) { _logger.LogError(ex, "Failed to create user for email {Email}. Error: {ErrorMessage}", request.Email, ex.Message); throw; } } }

Advanced Structured Logging with TuskLang

public class TuskLangLogger
{
    private readonly ILogger _logger;
    private readonly TuskLangConfig _config;

public TuskLangLogger(ILogger logger, TuskLangConfig config) { _logger = logger; _config = config; }

public void LogTuskOperation(string operation, object data, LogLevel level = LogLevel.Information) { var logData = new Dictionary<string, object> { ["TuskOperation"] = operation, ["TuskConfig"] = _config.Environment, ["Timestamp"] = DateTime.UtcNow, ["Data"] = data };

_logger.Log(level, "TuskLang operation: {Operation} with data: {@Data}", operation, logData); }

public IDisposable BeginTuskScope(string operation, Dictionary<string, object> properties) { var scopeData = new Dictionary<string, object>(properties) { ["TuskOperation"] = operation, ["TuskConfig"] = _config.Environment };

return _logger.BeginScope(scopeData); } }

Logging Configuration

public class LoggingConfiguration
{
    public static IHostBuilder ConfigureLogging(IHostBuilder builder)
    {
        return builder.ConfigureLogging((context, logging) =>
        {
            logging.ClearProviders();
            
            // Console logging with structured format
            logging.AddConsole(options =>
            {
                options.FormatterName = "json";
            });

// File logging logging.AddFile("logs/app-{Date}.log", options => { options.FileSizeLimitBytes = 10 1024 1024; // 10MB options.RetainedFileCountLimit = 30; options.Append = true; });

// Application Insights logging.AddApplicationInsights();

// Custom TuskLang logging logging.AddProvider(new TuskLangLoggingProvider()); }); } }

Log Levels and Categories

Log Level Strategy

public class LogLevelStrategy
{
    private readonly ILogger _logger;

public LogLevelStrategy(ILogger logger) { _logger = logger; }

public void LogDebug(string message, params object[] args) { if (_logger.IsEnabled(LogLevel.Debug)) { _logger.LogDebug(message, args); } }

public void LogInformation(string message, params object[] args) { _logger.LogInformation(message, args); }

public void LogWarning(string message, Exception exception = null, params object[] args) { if (exception != null) { _logger.LogWarning(exception, message, args); } else { _logger.LogWarning(message, args); } }

public void LogError(string message, Exception exception = null, params object[] args) { if (exception != null) { _logger.LogError(exception, message, args); } else { _logger.LogError(message, args); } }

public void LogCritical(string message, Exception exception = null, params object[] args) { if (exception != null) { _logger.LogCritical(exception, message, args); } else { _logger.LogCritical(message, args); } } }

Category-Based Logging

public static class LogCategories
{
    public const string Database = "Database";
    public const string Api = "API";
    public const string Security = "Security";
    public const string Performance = "Performance";
    public const string TuskLang = "TuskLang";
}

public class CategoryLogger { private readonly ILoggerFactory _loggerFactory;

public CategoryLogger(ILoggerFactory loggerFactory) { _loggerFactory = loggerFactory; }

public ILogger GetLogger(string category) { return _loggerFactory.CreateLogger(category); }

public void LogDatabaseOperation(string operation, TimeSpan duration, bool success) { var logger = GetLogger(LogCategories.Database); if (success) { logger.LogInformation("Database operation {Operation} completed in {Duration}ms", operation, duration.TotalMilliseconds); } else { logger.LogError("Database operation {Operation} failed after {Duration}ms", operation, duration.TotalMilliseconds); } }

public void LogApiCall(string endpoint, HttpStatusCode statusCode, TimeSpan duration) { var logger = GetLogger(LogCategories.Api); var level = statusCode.IsSuccessStatusCode ? LogLevel.Information : LogLevel.Warning; logger.Log(level, "API call to {Endpoint} returned {StatusCode} in {Duration}ms", endpoint, statusCode, duration.TotalMilliseconds); } }

Performance Logging

Performance Monitoring

public class PerformanceLogger
{
    private readonly ILogger _logger;
    private readonly Dictionary<string, Stopwatch> _timers;

public PerformanceLogger(ILogger logger) { _logger = logger; _timers = new Dictionary<string, Stopwatch>(); }

public IDisposable MeasureOperation(string operationName) { return new OperationTimer(_logger, operationName); }

public async Task<T> MeasureAsync<T>(string operationName, Func<Task<T>> operation) { var stopwatch = Stopwatch.StartNew(); try { var result = await operation(); _logger.LogInformation("Operation {Operation} completed in {Duration}ms", operationName, stopwatch.ElapsedMilliseconds); return result; } catch (Exception ex) { _logger.LogError(ex, "Operation {Operation} failed after {Duration}ms", operationName, stopwatch.ElapsedMilliseconds); throw; } }

public T Measure<T>(string operationName, Func<T> operation) { var stopwatch = Stopwatch.StartNew(); try { var result = operation(); _logger.LogInformation("Operation {Operation} completed in {Duration}ms", operationName, stopwatch.ElapsedMilliseconds); return result; } catch (Exception ex) { _logger.LogError(ex, "Operation {Operation} failed after {Duration}ms", operationName, stopwatch.ElapsedMilliseconds); throw; } }

private class OperationTimer : IDisposable { private readonly ILogger _logger; private readonly string _operationName; private readonly Stopwatch _stopwatch;

public OperationTimer(ILogger logger, string operationName) { _logger = logger; _operationName = operationName; _stopwatch = Stopwatch.StartNew(); }

public void Dispose() { _stopwatch.Stop(); _logger.LogInformation("Operation {Operation} completed in {Duration}ms", _operationName, _stopwatch.ElapsedMilliseconds); } } }

Memory Usage Logging

public class MemoryLogger
{
    private readonly ILogger _logger;
    private readonly Timer _timer;

public MemoryLogger(ILogger logger) { _logger = logger; _timer = new Timer(LogMemoryUsage, null, TimeSpan.Zero, TimeSpan.FromMinutes(5)); }

private void LogMemoryUsage(object state) { var process = Process.GetCurrentProcess(); var memoryInfo = process.WorkingSet64; var memoryMB = memoryInfo / (1024 * 1024);

_logger.LogInformation("Current memory usage: {MemoryMB}MB", memoryMB);

if (memoryMB > 1000) // 1GB threshold { _logger.LogWarning("High memory usage detected: {MemoryMB}MB", memoryMB); } }

public void LogMemorySnapshot(string operation) { var process = Process.GetCurrentProcess(); var memoryInfo = process.WorkingSet64; var memoryMB = memoryInfo / (1024 * 1024);

_logger.LogInformation("Memory snapshot for {Operation}: {MemoryMB}MB", operation, memoryMB); } }

Error Logging

Exception Logging

public class ExceptionLogger
{
    private readonly ILogger _logger;

public ExceptionLogger(ILogger logger) { _logger = logger; }

public void LogException(Exception exception, string context = null) { var logData = new Dictionary<string, object> { ["ExceptionType"] = exception.GetType().Name, ["Message"] = exception.Message, ["StackTrace"] = exception.StackTrace, ["Context"] = context ?? "Unknown" };

if (exception.InnerException != null) { logData["InnerException"] = new Dictionary<string, object> { ["Type"] = exception.InnerException.GetType().Name, ["Message"] = exception.InnerException.Message }; }

_logger.LogError("Exception occurred: {@ExceptionData}", logData); }

public void LogAggregateException(AggregateException exception, string context = null) { _logger.LogError(exception, "Aggregate exception in {Context}. Inner exceptions: {Count}", context, exception.InnerExceptions.Count);

foreach (var innerException in exception.InnerExceptions) { LogException(innerException, $"{context}.Inner"); } } }

Error Recovery Logging

public class ErrorRecoveryLogger
{
    private readonly ILogger _logger;

public ErrorRecoveryLogger(ILogger logger) { _logger = logger; }

public void LogRetryAttempt(string operation, int attemptNumber, Exception exception) { _logger.LogWarning(exception, "Retry attempt {Attempt} for operation {Operation}", attemptNumber, operation); }

public void LogRecoverySuccess(string operation, int totalAttempts) { _logger.LogInformation("Operation {Operation} recovered after {Attempts} attempts", operation, totalAttempts); }

public void LogRecoveryFailure(string operation, int totalAttempts, Exception finalException) { _logger.LogError(finalException, "Operation {Operation} failed after {Attempts} attempts", operation, totalAttempts); } }

Audit Logging

Security Audit Logging

public class SecurityAuditLogger
{
    private readonly ILogger _logger;

public SecurityAuditLogger(ILogger logger) { _logger = logger; }

public void LogAuthentication(string userId, string method, bool success, string ipAddress) { var level = success ? LogLevel.Information : LogLevel.Warning; _logger.Log(level, "Authentication attempt - User: {UserId}, Method: {Method}, " + "Success: {Success}, IP: {IpAddress}", userId, method, success, ipAddress); }

public void LogAuthorization(string userId, string resource, string action, bool granted) { var level = granted ? LogLevel.Information : LogLevel.Warning; _logger.Log(level, "Authorization check - User: {UserId}, Resource: {Resource}, " + "Action: {Action}, Granted: {Granted}", userId, resource, action, granted); }

public void LogDataAccess(string userId, string dataType, string operation, bool success) { _logger.LogInformation("Data access - User: {UserId}, Type: {DataType}, " + "Operation: {Operation}, Success: {Success}", userId, dataType, operation, success); } }

Business Audit Logging

public class BusinessAuditLogger
{
    private readonly ILogger _logger;

public BusinessAuditLogger(ILogger logger) { _logger = logger; }

public void LogUserAction(string userId, string action, object data) { _logger.LogInformation("User action - User: {UserId}, Action: {Action}, Data: {@Data}", userId, action, data); }

public void LogConfigurationChange(string userId, string configKey, object oldValue, object newValue) { _logger.LogInformation("Configuration change - User: {UserId}, Key: {Key}, " + "Old: {OldValue}, New: {NewValue}", userId, configKey, oldValue, newValue); }

public void LogDataModification(string userId, string entityType, string entityId, Dictionary<string, object> changes) { _logger.LogInformation("Data modification - User: {UserId}, Entity: {EntityType}, " + "ID: {EntityId}, Changes: {@Changes}", userId, entityType, entityId, changes); } }

Distributed Tracing

Correlation ID Logging

public class CorrelationLogger
{
    private readonly ILogger _logger;
    private readonly IHttpContextAccessor _httpContextAccessor;

public CorrelationLogger(ILogger logger, IHttpContextAccessor httpContextAccessor) { _logger = logger; _httpContextAccessor = httpContextAccessor; }

public string GetCorrelationId() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext?.Request.Headers.ContainsKey("X-Correlation-ID") == true) { return httpContext.Request.Headers["X-Correlation-ID"].FirstOrDefault(); }

return Activity.Current?.Id ?? Guid.NewGuid().ToString(); }

public IDisposable BeginCorrelationScope(string operation) { var correlationId = GetCorrelationId(); return _logger.BeginScope(new Dictionary<string, object> { ["CorrelationId"] = correlationId, ["Operation"] = operation }); }

public void LogWithCorrelation(string message, params object[] args) { var correlationId = GetCorrelationId(); _logger.LogInformation("[{CorrelationId}] {Message}", correlationId, string.Format(message, args)); } }

Activity Logging

public class ActivityLogger
{
    private readonly ILogger _logger;

public ActivityLogger(ILogger logger) { _logger = logger; }

public Activity StartActivity(string operationName, Dictionary<string, object> tags = null) { var activity = new Activity(operationName); if (tags != null) { foreach (var tag in tags) { activity.SetTag(tag.Key, tag.Value); } }

activity.Start(); _logger.LogInformation("Activity started: {ActivityName}", operationName); return activity; }

public void StopActivity(Activity activity) { activity.Stop(); _logger.LogInformation("Activity completed: {ActivityName} in {Duration}ms", activity.OperationName, activity.Duration.TotalMilliseconds); } }

Log Aggregation

Centralized Logging

public class LogAggregator
{
    private readonly ILogger _logger;
    private readonly HttpClient _httpClient;
    private readonly string _logEndpoint;

public LogAggregator(ILogger logger, HttpClient httpClient, string logEndpoint) { _logger = logger; _httpClient = httpClient; _logEndpoint = logEndpoint; }

public async Task SendLogAsync(LogEntry entry) { try { var json = JsonSerializer.Serialize(entry); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_logEndpoint, content); if (!response.IsSuccessStatusCode) { _logger.LogWarning("Failed to send log to aggregator. Status: {StatusCode}", response.StatusCode); } } catch (Exception ex) { _logger.LogError(ex, "Error sending log to aggregator"); } }

public async Task SendBatchAsync(IEnumerable<LogEntry> entries) { var batch = new LogBatch { Entries = entries.ToList(), Timestamp = DateTime.UtcNow, Source = Environment.MachineName };

try { var json = JsonSerializer.Serialize(batch); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync($"{_logEndpoint}/batch", content); if (!response.IsSuccessStatusCode) { _logger.LogWarning("Failed to send log batch. Status: {StatusCode}", response.StatusCode); } } catch (Exception ex) { _logger.LogError(ex, "Error sending log batch to aggregator"); } } }

public class LogEntry { public string Level { get; set; } public string Message { get; set; } public string Category { get; set; } public DateTime Timestamp { get; set; } public Dictionary<string, object> Properties { get; set; } public string Exception { get; set; } }

public class LogBatch { public List<LogEntry> Entries { get; set; } public DateTime Timestamp { get; set; } public string Source { get; set; } }

TuskLang Integration

TuskLang Logging Provider

public class TuskLangLoggingProvider : ILoggerProvider
{
    private readonly TuskLangConfig _config;
    private readonly Dictionary<string, TuskLangLogger> _loggers;

public TuskLangLoggingProvider(TuskLangConfig config) { _config = config; _loggers = new Dictionary<string, TuskLangLogger>(); }

public ILogger CreateLogger(string categoryName) { if (!_loggers.ContainsKey(categoryName)) { _loggers[categoryName] = new TuskLangLogger(categoryName, _config); }

return _loggers[categoryName]; }

public void Dispose() { foreach (var logger in _loggers.Values) { logger.Dispose(); } _loggers.Clear(); } }

public class TuskLangLogger : ILogger { private readonly string _categoryName; private readonly TuskLangConfig _config; private readonly TuskLang _tuskLang;

public TuskLangLogger(string categoryName, TuskLangConfig config) { _categoryName = categoryName; _config = config; _tuskLang = new TuskLang(config); }

public IDisposable BeginScope<TState>(TState state) { return NullScope.Instance; }

public bool IsEnabled(LogLevel logLevel) { return logLevel >= _config.MinimumLogLevel; }

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) { if (!IsEnabled(logLevel)) return;

var message = formatter(state, exception); // Log to TuskLang configuration var logEntry = new Dictionary<string, object> { ["level"] = logLevel.ToString(), ["category"] = _categoryName, ["message"] = message, ["timestamp"] = DateTime.UtcNow, ["exception"] = exception?.ToString() };

_tuskLang.SetValue($"logs.{DateTime.UtcNow:yyyyMMdd}.{Guid.NewGuid()}", logEntry); }

public void Dispose() { _tuskLang?.Dispose(); }

private class NullScope : IDisposable { public static NullScope Instance { get; } = new NullScope(); private NullScope() { } public void Dispose() { } } }

TuskLang Log Configuration

public class TuskLangLogConfig
{
    public LogLevel MinimumLogLevel { get; set; } = LogLevel.Information;
    public bool EnableStructuredLogging { get; set; } = true;
    public bool EnablePerformanceLogging { get; set; } = true;
    public bool EnableAuditLogging { get; set; } = true;
    public string LogFilePath { get; set; } = "logs/tusklang.log";
    public int MaxLogFileSizeMB { get; set; } = 100;
    public int RetainedFileCount { get; set; } = 30;
}

public static class TuskLangLoggingExtensions { public static ILoggingBuilder AddTuskLang(this ILoggingBuilder builder, TuskLangLogConfig config) { builder.AddProvider(new TuskLangLoggingProvider(config)); return builder; }

public static ILoggingBuilder AddTuskLang(this ILoggingBuilder builder, Action<TuskLangLogConfig> configure) { var config = new TuskLangLogConfig(); configure(config); builder.AddProvider(new TuskLangLoggingProvider(config)); return builder; } }

Best Practices

Logging Guidelines

public class LoggingBestPractices
{
    private readonly ILogger _logger;

public LoggingBestPractices(ILogger logger) { _logger = logger; }

// ✅ Good: Use structured logging with parameters public void GoodLogging(string userId, string action) { _logger.LogInformation("User {UserId} performed {Action}", userId, action); }

// ❌ Bad: String concatenation in logging public void BadLogging(string userId, string action) { _logger.LogInformation("User " + userId + " performed " + action); }

// ✅ Good: Use appropriate log levels public void AppropriateLogLevels(string operation) { _logger.LogDebug("Starting operation {Operation}", operation); _logger.LogInformation("Operation {Operation} completed successfully", operation); _logger.LogWarning("Operation {Operation} took longer than expected", operation); _logger.LogError("Operation {Operation} failed", operation); }

// ✅ Good: Include context in error logs public void ErrorWithContext(Exception ex, string operation, object data) { _logger.LogError(ex, "Operation {Operation} failed with data {@Data}", operation, data); }

// ✅ Good: Use scopes for related operations public async Task ProcessUserAsync(string userId) { using var scope = _logger.BeginScope(new Dictionary<string, object> { ["UserId"] = userId, ["Operation"] = "ProcessUser" });

_logger.LogInformation("Starting user processing"); // ... processing logic ... _logger.LogInformation("User processing completed"); } }

Performance Considerations

public class LoggingPerformance
{
    private readonly ILogger _logger;

public LoggingPerformance(ILogger logger) { _logger = logger; }

// ✅ Good: Check log level before expensive operations public void EfficientLogging(string data) { if (_logger.IsEnabled(LogLevel.Debug)) { var expensiveData = ProcessData(data); _logger.LogDebug("Processed data: {@Data}", expensiveData); } }

// ✅ Good: Use lazy evaluation for expensive objects public void LazyLogging(Func<object> expensiveObjectFactory) { _logger.LogDebug("Expensive object: {@Object}", new Lazy<object>(expensiveObjectFactory)); }

// ✅ Good: Batch logging operations public async Task BatchLoggingAsync(IEnumerable<string> messages) { var logEntries = messages.Select(m => new LogEntry { Message = m, Timestamp = DateTime.UtcNow, Level = "Information" });

await SendBatchAsync(logEntries); } }

Security Considerations

public class SecureLogging
{
    private readonly ILogger _logger;

public SecureLogging(ILogger logger) { _logger = logger; }

// ✅ Good: Sanitize sensitive data public void SanitizedLogging(string email, string password) { var sanitizedEmail = SanitizeEmail(email); var sanitizedPassword = "*"; _logger.LogInformation("User login attempt: {Email}", sanitizedEmail); // Never log passwords or sensitive data }

// ✅ Good: Use secure logging for sensitive operations public void SecureOperationLogging(string userId, string operation) { _logger.LogInformation("Secure operation - User: {UserId}, Operation: {Operation}", userId, operation); }

private string SanitizeEmail(string email) { if (string.IsNullOrEmpty(email)) return email;

var parts = email.Split('@'); if (parts.Length != 2) return "*";

var username = parts[0]; var domain = parts[1];

if (username.Length <= 2) return $"{username}*@{domain}";

return $"{username.Substring(0, 2)}*@{domain}"; } }

Summary

This comprehensive logging patterns guide covers:

- Structured Logging: Using dictionaries and objects for rich log data - Log Levels: Appropriate use of Debug, Information, Warning, Error, and Critical - Performance Logging: Measuring operations and memory usage - Error Logging: Comprehensive exception handling and recovery - Audit Logging: Security and business audit trails - Distributed Tracing: Correlation IDs and activity tracking - Log Aggregation: Centralized logging with batching - TuskLang Integration: Custom logging provider and configuration - Best Practices: Performance, security, and maintainability guidelines

The patterns ensure comprehensive observability while maintaining performance and security in production C# applications integrated with TuskLang.