🔷 Performance Monitoring in C# TuskLang
Performance Monitoring in C# TuskLang
Overview
Performance monitoring is essential for maintaining optimal application performance. This guide covers metrics collection, profiling, dashboards, and optimization strategies for C# TuskLang applications.
📊 Metrics Collection
Performance Metrics Service
public class PerformanceMetricsService
{
private readonly ILogger<PerformanceMetricsService> _logger;
private readonly TSKConfig _config;
private readonly Dictionary<string, MetricCollector> _collectors;
private readonly Timer _reportingTimer;
private readonly IMetricsExporter _exporter;
public PerformanceMetricsService(ILogger<PerformanceMetricsService> logger, TSKConfig config, IMetricsExporter exporter)
{
_logger = logger;
_config = config;
_exporter = exporter;
_collectors = new Dictionary<string, MetricCollector>();
var reportingInterval = TimeSpan.FromSeconds(config.Get<int>("metrics.reporting_interval_seconds", 60));
_reportingTimer = new Timer(ReportMetrics, null, reportingInterval, reportingInterval);
}
public void RecordTiming(string operation, TimeSpan duration, Dictionary<string, object>? tags = null)
{
var collector = GetOrCreateCollector(operation);
collector.RecordTiming(duration, tags);
}
public void IncrementCounter(string name, int value = 1, Dictionary<string, object>? tags = null)
{
var collector = GetOrCreateCollector(name);
collector.IncrementCounter(value, tags);
}
public void RecordGauge(string name, double value, Dictionary<string, object>? tags = null)
{
var collector = GetOrCreateCollector(name);
collector.RecordGauge(value, tags);
}
public void RecordHistogram(string name, double value, Dictionary<string, object>? tags = null)
{
var collector = GetOrCreateCollector(name);
collector.RecordHistogram(value, tags);
}
private MetricCollector GetOrCreateCollector(string name)
{
lock (_collectors)
{
if (!_collectors.ContainsKey(name))
{
_collectors[name] = new MetricCollector(name);
}
return _collectors[name];
}
}
private async void ReportMetrics(object? state)
{
try
{
var metrics = CollectAllMetrics();
await _exporter.ExportAsync(metrics);
_logger.LogDebug("Exported {Count} metrics", metrics.Count);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to report metrics");
}
}
private List<Metric> CollectAllMetrics()
{
var metrics = new List<Metric>();
lock (_collectors)
{
foreach (var kvp in _collectors)
{
var collector = kvp.Value;
metrics.AddRange(collector.GetMetrics());
}
}
return metrics;
}
public void Dispose()
{
_reportingTimer?.Dispose();
}
}public class MetricCollector
{
private readonly string _name;
private readonly ConcurrentQueue<TimingMetric> _timings;
private readonly ConcurrentQueue<CounterMetric> _counters;
private readonly ConcurrentQueue<GaugeMetric> _gauges;
private readonly ConcurrentQueue<HistogramMetric> _histograms;
public MetricCollector(string name)
{
_name = name;
_timings = new ConcurrentQueue<TimingMetric>();
_counters = new ConcurrentQueue<CounterMetric>();
_gauges = new ConcurrentQueue<GaugeMetric>();
_histograms = new ConcurrentQueue<HistogramMetric>();
}
public void RecordTiming(TimeSpan duration, Dictionary<string, object>? tags = null)
{
_timings.Enqueue(new TimingMetric
{
Name = _name,
Duration = duration,
Tags = tags ?? new Dictionary<string, object>(),
Timestamp = DateTime.UtcNow
});
}
public void IncrementCounter(int value, Dictionary<string, object>? tags = null)
{
_counters.Enqueue(new CounterMetric
{
Name = _name,
Value = value,
Tags = tags ?? new Dictionary<string, object>(),
Timestamp = DateTime.UtcNow
});
}
public void RecordGauge(double value, Dictionary<string, object>? tags = null)
{
_gauges.Enqueue(new GaugeMetric
{
Name = _name,
Value = value,
Tags = tags ?? new Dictionary<string, object>(),
Timestamp = DateTime.UtcNow
});
}
public void RecordHistogram(double value, Dictionary<string, object>? tags = null)
{
_histograms.Enqueue(new HistogramMetric
{
Name = _name,
Value = value,
Tags = tags ?? new Dictionary<string, object>(),
Timestamp = DateTime.UtcNow
});
}
public List<Metric> GetMetrics()
{
var metrics = new List<Metric>();
// Collect timing metrics
while (_timings.TryDequeue(out var timing))
{
metrics.Add(timing);
}
// Collect counter metrics
while (_counters.TryDequeue(out var counter))
{
metrics.Add(counter);
}
// Collect gauge metrics
while (_gauges.TryDequeue(out var gauge))
{
metrics.Add(gauge);
}
// Collect histogram metrics
while (_histograms.TryDequeue(out var histogram))
{
metrics.Add(histogram);
}
return metrics;
}
}
public abstract class Metric
{
public string Name { get; set; } = string.Empty;
public Dictionary<string, object> Tags { get; set; } = new();
public DateTime Timestamp { get; set; }
public abstract string Type { get; }
}
public class TimingMetric : Metric
{
public TimeSpan Duration { get; set; }
public override string Type => "timing";
}
public class CounterMetric : Metric
{
public int Value { get; set; }
public override string Type => "counter";
}
public class GaugeMetric : Metric
{
public double Value { get; set; }
public override string Type => "gauge";
}
public class HistogramMetric : Metric
{
public double Value { get; set; }
public override string Type => "histogram";
}
Metrics Exporters
public interface IMetricsExporter
{
Task ExportAsync(List<Metric> metrics);
}public class PrometheusMetricsExporter : IMetricsExporter
{
private readonly ILogger<PrometheusMetricsExporter> _logger;
private readonly TSKConfig _config;
private readonly HttpClient _httpClient;
public PrometheusMetricsExporter(ILogger<PrometheusMetricsExporter> logger, TSKConfig config)
{
_logger = logger;
_config = config;
_httpClient = new HttpClient();
}
public async Task ExportAsync(List<Metric> metrics)
{
var prometheusUrl = _config.Get<string>("metrics.prometheus_url");
if (string.IsNullOrEmpty(prometheusUrl))
{
return;
}
var prometheusMetrics = ConvertToPrometheusFormat(metrics);
var content = new StringContent(prometheusMetrics, Encoding.UTF8, "text/plain");
var response = await _httpClient.PostAsync(prometheusUrl, content);
response.EnsureSuccessStatusCode();
}
private string ConvertToPrometheusFormat(List<Metric> metrics)
{
var lines = new List<string>();
foreach (var metric in metrics)
{
var tags = string.Join(",", metric.Tags.Select(kvp => $"{kvp.Key}=\"{kvp.Value}\""));
var metricName = $"tusklang_{metric.Name}";
switch (metric)
{
case TimingMetric timing:
lines.Add($"{metricName}_duration_ms{{{tags}}} {timing.Duration.TotalMilliseconds}");
break;
case CounterMetric counter:
lines.Add($"{metricName}_total{{{tags}}} {counter.Value}");
break;
case GaugeMetric gauge:
lines.Add($"{metricName}{{{tags}}} {gauge.Value}");
break;
case HistogramMetric histogram:
lines.Add($"{metricName}_bucket{{{tags}}} {histogram.Value}");
break;
}
}
return string.Join("\n", lines);
}
}
public class InfluxDBMetricsExporter : IMetricsExporter
{
private readonly ILogger<InfluxDBMetricsExporter> _logger;
private readonly TSKConfig _config;
private readonly HttpClient _httpClient;
public InfluxDBMetricsExporter(ILogger<InfluxDBMetricsExporter> logger, TSKConfig config)
{
_logger = logger;
_config = config;
_httpClient = new HttpClient();
}
public async Task ExportAsync(List<Metric> metrics)
{
var influxUrl = _config.Get<string>("metrics.influxdb_url");
var database = _config.Get<string>("metrics.influxdb_database", "tusklang_metrics");
if (string.IsNullOrEmpty(influxUrl))
{
return;
}
var influxMetrics = ConvertToInfluxFormat(metrics, database);
var content = new StringContent(influxMetrics, Encoding.UTF8, "application/octet-stream");
var response = await _httpClient.PostAsync($"{influxUrl}/write?db={database}", content);
response.EnsureSuccessStatusCode();
}
private string ConvertToInfluxFormat(List<Metric> metrics, string database)
{
var lines = new List<string>();
foreach (var metric in metrics)
{
var tags = string.Join(",", metric.Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"));
var measurement = $"tusklang_{metric.Name}";
switch (metric)
{
case TimingMetric timing:
lines.Add($"{measurement},{tags} duration_ms={timing.Duration.TotalMilliseconds} {GetTimestamp(metric.Timestamp)}");
break;
case CounterMetric counter:
lines.Add($"{measurement},{tags} value={counter.Value} {GetTimestamp(metric.Timestamp)}");
break;
case GaugeMetric gauge:
lines.Add($"{measurement},{tags} value={gauge.Value} {GetTimestamp(metric.Timestamp)}");
break;
case HistogramMetric histogram:
lines.Add($"{measurement},{tags} value={histogram.Value} {GetTimestamp(metric.Timestamp)}");
break;
}
}
return string.Join("\n", lines);
}
private long GetTimestamp(DateTime timestamp)
{
return new DateTimeOffset(timestamp).ToUnixTimeNanoseconds();
}
}
🔍 Performance Profiling
Profiling Middleware
public class PerformanceProfilingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<PerformanceProfilingMiddleware> _logger;
private readonly PerformanceMetricsService _metricsService;
public PerformanceProfilingMiddleware(
RequestDelegate next,
ILogger<PerformanceProfilingMiddleware> logger,
PerformanceMetricsService metricsService)
{
_next = next;
_logger = logger;
_metricsService = metricsService;
}
public async Task InvokeAsync(HttpContext context)
{
var stopwatch = Stopwatch.StartNew();
var path = context.Request.Path;
var method = context.Request.Method;
try
{
await _next(context);
}
finally
{
stopwatch.Stop();
var duration = stopwatch.Elapsed;
var statusCode = context.Response.StatusCode;
// Record metrics
_metricsService.RecordTiming("http_request_duration", duration, new Dictionary<string, object>
{
["path"] = path,
["method"] = method,
["status_code"] = statusCode
});
_metricsService.IncrementCounter("http_requests_total", 1, new Dictionary<string, object>
{
["path"] = path,
["method"] = method,
["status_code"] = statusCode
});
// Log slow requests
if (duration.TotalSeconds > 1)
{
_logger.LogWarning("Slow request detected: {Method} {Path} took {Duration}ms",
method, path, duration.TotalMilliseconds);
}
}
}
}public class DatabaseProfilingInterceptor : IDbInterceptor
{
private readonly PerformanceMetricsService _metricsService;
private readonly ILogger<DatabaseProfilingInterceptor> _logger;
public DatabaseProfilingInterceptor(PerformanceMetricsService metricsService, ILogger<DatabaseProfilingInterceptor> logger)
{
_metricsService = metricsService;
_logger = logger;
}
public async Task<T> InterceptAsync<T>(Func<Task<T>> operation, string operationName, string? sql = null)
{
var stopwatch = Stopwatch.StartNew();
try
{
var result = await operation();
stopwatch.Stop();
var duration = stopwatch.Elapsed;
// Record metrics
_metricsService.RecordTiming("database_operation_duration", duration, new Dictionary<string, object>
{
["operation"] = operationName,
["sql"] = sql ?? "unknown"
});
_metricsService.IncrementCounter("database_operations_total", 1, new Dictionary<string, object>
{
["operation"] = operationName
});
// Log slow queries
if (duration.TotalSeconds > 0.5)
{
_logger.LogWarning("Slow database operation: {Operation} took {Duration}ms",
operationName, duration.TotalMilliseconds);
}
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
_metricsService.IncrementCounter("database_operation_errors", 1, new Dictionary<string, object>
{
["operation"] = operationName,
["error_type"] = ex.GetType().Name
});
throw;
}
}
}
public interface IDbInterceptor
{
Task<T> InterceptAsync<T>(Func<Task<T>> operation, string operationName, string? sql = null);
}
Memory Profiling
public class MemoryProfiler
{
private readonly ILogger<MemoryProfiler> _logger;
private readonly PerformanceMetricsService _metricsService;
private readonly Timer _profilingTimer;
private readonly TSKConfig _config;
public MemoryProfiler(ILogger<MemoryProfiler> logger, PerformanceMetricsService metricsService, TSKConfig config)
{
_logger = logger;
_metricsService = metricsService;
_config = config;
var profilingInterval = TimeSpan.FromMinutes(config.Get<int>("profiling.memory_interval_minutes", 5));
_profilingTimer = new Timer(ProfileMemory, null, profilingInterval, profilingInterval);
}
private void ProfileMemory(object? state)
{
try
{
var process = Process.GetCurrentProcess();
// Record memory metrics
_metricsService.RecordGauge("memory_working_set_mb", process.WorkingSet64 / 1024.0 / 1024.0);
_metricsService.RecordGauge("memory_private_mb", process.PrivateMemorySize64 / 1024.0 / 1024.0);
_metricsService.RecordGauge("memory_virtual_mb", process.VirtualMemorySize64 / 1024.0 / 1024.0);
// Record GC metrics
for (int i = 0; i <= GC.MaxGeneration; i++)
{
var collectionCount = GC.CollectionCount(i);
_metricsService.RecordGauge($"gc_collection_count_gen_{i}", collectionCount);
}
var totalMemory = GC.GetTotalMemory(false);
_metricsService.RecordGauge("gc_total_memory_mb", totalMemory / 1024.0 / 1024.0);
// Log memory warnings
var workingSetMB = process.WorkingSet64 / 1024.0 / 1024.0;
var memoryThreshold = _config.Get<double>("profiling.memory_threshold_mb", 1000);
if (workingSetMB > memoryThreshold)
{
_logger.LogWarning("High memory usage detected: {WorkingSetMB}MB", workingSetMB);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to profile memory");
}
}
public void Dispose()
{
_profilingTimer?.Dispose();
}
}
📈 Performance Dashboards
Dashboard Data Service
public class PerformanceDashboardService
{
private readonly ILogger<PerformanceDashboardService> _logger;
private readonly TSKConfig _config;
private readonly IMetricsRepository _metricsRepository;
public PerformanceDashboardService(ILogger<PerformanceDashboardService> logger, TSKConfig config, IMetricsRepository metricsRepository)
{
_logger = logger;
_config = config;
_metricsRepository = metricsRepository;
}
public async Task<DashboardData> GetDashboardDataAsync(DateTime startTime, DateTime endTime)
{
var dashboardData = new DashboardData
{
TimeRange = new TimeRange { Start = startTime, End = endTime },
ServiceName = _config.Get<string>("app.name", "unknown"),
Environment = _config.Get<string>("app.environment", "unknown")
};
// Get HTTP request metrics
dashboardData.HttpMetrics = await GetHttpMetricsAsync(startTime, endTime);
// Get database metrics
dashboardData.DatabaseMetrics = await GetDatabaseMetricsAsync(startTime, endTime);
// Get memory metrics
dashboardData.MemoryMetrics = await GetMemoryMetricsAsync(startTime, endTime);
// Get error metrics
dashboardData.ErrorMetrics = await GetErrorMetricsAsync(startTime, endTime);
// Get performance trends
dashboardData.PerformanceTrends = await GetPerformanceTrendsAsync(startTime, endTime);
return dashboardData;
}
private async Task<HttpMetrics> GetHttpMetricsAsync(DateTime startTime, DateTime endTime)
{
var metrics = await _metricsRepository.GetMetricsAsync("http_request_duration", startTime, endTime);
return new HttpMetrics
{
TotalRequests = metrics.Count,
AverageResponseTime = metrics.Average(m => ((TimingMetric)m).Duration.TotalMilliseconds),
MaxResponseTime = metrics.Max(m => ((TimingMetric)m).Duration.TotalMilliseconds),
MinResponseTime = metrics.Min(m => ((TimingMetric)m).Duration.TotalMilliseconds),
RequestsPerSecond = metrics.Count / (endTime - startTime).TotalSeconds,
StatusCodeDistribution = metrics
.GroupBy(m => m.Tags.GetValueOrDefault("status_code", "unknown"))
.ToDictionary(g => g.Key.ToString(), g => g.Count())
};
}
private async Task<DatabaseMetrics> GetDatabaseMetricsAsync(DateTime startTime, DateTime endTime)
{
var metrics = await _metricsRepository.GetMetricsAsync("database_operation_duration", startTime, endTime);
return new DatabaseMetrics
{
TotalOperations = metrics.Count,
AverageOperationTime = metrics.Average(m => ((TimingMetric)m).Duration.TotalMilliseconds),
MaxOperationTime = metrics.Max(m => ((TimingMetric)m).Duration.TotalMilliseconds),
MinOperationTime = metrics.Min(m => ((TimingMetric)m).Duration.TotalMilliseconds),
OperationsPerSecond = metrics.Count / (endTime - startTime).TotalSeconds,
OperationDistribution = metrics
.GroupBy(m => m.Tags.GetValueOrDefault("operation", "unknown"))
.ToDictionary(g => g.Key.ToString(), g => g.Count())
};
}
private async Task<MemoryMetrics> GetMemoryMetricsAsync(DateTime startTime, DateTime endTime)
{
var workingSetMetrics = await _metricsRepository.GetMetricsAsync("memory_working_set_mb", startTime, endTime);
var gcMetrics = await _metricsRepository.GetMetricsAsync("gc_total_memory_mb", startTime, endTime);
return new MemoryMetrics
{
AverageWorkingSetMB = workingSetMetrics.Average(m => ((GaugeMetric)m).Value),
MaxWorkingSetMB = workingSetMetrics.Max(m => ((GaugeMetric)m).Value),
MinWorkingSetMB = workingSetMetrics.Min(m => ((GaugeMetric)m).Value),
AverageGCTotalMemoryMB = gcMetrics.Average(m => ((GaugeMetric)m).Value),
MaxGCTotalMemoryMB = gcMetrics.Max(m => ((GaugeMetric)m).Value)
};
}
private async Task<ErrorMetrics> GetErrorMetricsAsync(DateTime startTime, DateTime endTime)
{
var errorMetrics = await _metricsRepository.GetMetricsAsync("database_operation_errors", startTime, endTime);
return new ErrorMetrics
{
TotalErrors = errorMetrics.Sum(m => ((CounterMetric)m).Value),
ErrorsPerSecond = errorMetrics.Sum(m => ((CounterMetric)m).Value) / (endTime - startTime).TotalSeconds,
ErrorDistribution = errorMetrics
.GroupBy(m => m.Tags.GetValueOrDefault("error_type", "unknown"))
.ToDictionary(g => g.Key.ToString(), g => g.Sum(m => ((CounterMetric)m).Value))
};
}
private async Task<List<PerformanceTrend>> GetPerformanceTrendsAsync(DateTime startTime, DateTime endTime)
{
var trends = new List<PerformanceTrend>();
// Get hourly trends
var interval = TimeSpan.FromHours(1);
var currentTime = startTime;
while (currentTime < endTime)
{
var intervalEnd = currentTime.Add(interval);
var metrics = await _metricsRepository.GetMetricsAsync("http_request_duration", currentTime, intervalEnd);
if (metrics.Any())
{
trends.Add(new PerformanceTrend
{
Timestamp = currentTime,
AverageResponseTime = metrics.Average(m => ((TimingMetric)m).Duration.TotalMilliseconds),
RequestCount = metrics.Count,
ErrorCount = metrics.Count(m => m.Tags.GetValueOrDefault("status_code", 200).ToString() == "500")
});
}
currentTime = intervalEnd;
}
return trends;
}
}public class DashboardData
{
public TimeRange TimeRange { get; set; } = new();
public string ServiceName { get; set; } = string.Empty;
public string Environment { get; set; } = string.Empty;
public HttpMetrics HttpMetrics { get; set; } = new();
public DatabaseMetrics DatabaseMetrics { get; set; } = new();
public MemoryMetrics MemoryMetrics { get; set; } = new();
public ErrorMetrics ErrorMetrics { get; set; } = new();
public List<PerformanceTrend> PerformanceTrends { get; set; } = new();
}
public class TimeRange
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
}
public class HttpMetrics
{
public int TotalRequests { get; set; }
public double AverageResponseTime { get; set; }
public double MaxResponseTime { get; set; }
public double MinResponseTime { get; set; }
public double RequestsPerSecond { get; set; }
public Dictionary<string, int> StatusCodeDistribution { get; set; } = new();
}
public class DatabaseMetrics
{
public int TotalOperations { get; set; }
public double AverageOperationTime { get; set; }
public double MaxOperationTime { get; set; }
public double MinOperationTime { get; set; }
public double OperationsPerSecond { get; set; }
public Dictionary<string, int> OperationDistribution { get; set; } = new();
}
public class MemoryMetrics
{
public double AverageWorkingSetMB { get; set; }
public double MaxWorkingSetMB { get; set; }
public double MinWorkingSetMB { get; set; }
public double AverageGCTotalMemoryMB { get; set; }
public double MaxGCTotalMemoryMB { get; set; }
}
public class ErrorMetrics
{
public int TotalErrors { get; set; }
public double ErrorsPerSecond { get; set; }
public Dictionary<string, int> ErrorDistribution { get; set; } = new();
}
public class PerformanceTrend
{
public DateTime Timestamp { get; set; }
public double AverageResponseTime { get; set; }
public int RequestCount { get; set; }
public int ErrorCount { get; set; }
}
🚀 Performance Optimization
Caching Strategies
public class PerformanceOptimizer
{
private readonly ILogger<PerformanceOptimizer> _logger;
private readonly TSKConfig _config;
private readonly PerformanceMetricsService _metricsService;
private readonly IDistributedCache _cache;
public PerformanceOptimizer(
ILogger<PerformanceOptimizer> logger,
TSKConfig config,
PerformanceMetricsService metricsService,
IDistributedCache cache)
{
_logger = logger;
_config = config;
_metricsService = metricsService;
_cache = cache;
}
public async Task<T> GetOrSetCacheAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null)
{
var cacheKey = $"perf_opt_{key}";
var cacheExpiration = expiration ?? TimeSpan.FromMinutes(_config.Get<int>("cache.default_expiration_minutes", 30));
try
{
var cachedValue = await _cache.GetStringAsync(cacheKey);
if (!string.IsNullOrEmpty(cachedValue))
{
_metricsService.IncrementCounter("cache_hits", 1, new Dictionary<string, object> { ["key"] = key });
return JsonSerializer.Deserialize<T>(cachedValue)!;
}
_metricsService.IncrementCounter("cache_misses", 1, new Dictionary<string, object> { ["key"] = key });
var value = await factory();
var json = JsonSerializer.Serialize(value);
await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = cacheExpiration
});
return value;
}
catch (Exception ex)
{
_logger.LogError(ex, "Cache operation failed for key: {Key}", key);
return await factory();
}
}
public async Task<T> ExecuteWithTimeoutAsync<T>(Func<Task<T>> operation, string operationName, TimeSpan? timeout = null)
{
var operationTimeout = timeout ?? TimeSpan.FromSeconds(_config.Get<int>("performance.default_timeout_seconds", 30));
using var cts = new CancellationTokenSource(operationTimeout);
try
{
var stopwatch = Stopwatch.StartNew();
var result = await operation().WaitAsync(cts.Token);
stopwatch.Stop();
_metricsService.RecordTiming($"{operationName}_duration", stopwatch.Elapsed);
return result;
}
catch (OperationCanceledException)
{
_metricsService.IncrementCounter("timeout_errors", 1, new Dictionary<string, object> { ["operation"] = operationName });
throw new TimeoutException($"Operation {operationName} timed out after {operationTimeout.TotalSeconds} seconds");
}
}
public async Task<T> ExecuteWithRetryAsync<T>(Func<Task<T>> operation, string operationName, int maxRetries = 3)
{
var retryCount = 0;
var lastException = (Exception?)null;
while (retryCount <= maxRetries)
{
try
{
var stopwatch = Stopwatch.StartNew();
var result = await operation();
stopwatch.Stop();
_metricsService.RecordTiming($"{operationName}_duration", stopwatch.Elapsed);
if (retryCount > 0)
{
_metricsService.IncrementCounter("retry_success", 1, new Dictionary<string, object>
{
["operation"] = operationName,
["retry_count"] = retryCount
});
}
return result;
}
catch (Exception ex)
{
lastException = ex;
retryCount++;
_metricsService.IncrementCounter("retry_attempts", 1, new Dictionary<string, object>
{
["operation"] = operationName,
["retry_count"] = retryCount
});
if (retryCount <= maxRetries)
{
var delay = TimeSpan.FromSeconds(Math.Pow(2, retryCount - 1)); // Exponential backoff
await Task.Delay(delay);
}
}
}
_metricsService.IncrementCounter("retry_failures", 1, new Dictionary<string, object>
{
["operation"] = operationName,
["retry_count"] = retryCount
});
throw lastException!;
}
}
📝 Summary
This guide covered comprehensive performance monitoring strategies for C# TuskLang applications:
- Metrics Collection: Performance metrics service with timing, counters, gauges, and histograms - Metrics Exporters: Prometheus and InfluxDB exporters for metrics aggregation - Performance Profiling: HTTP and database profiling middleware with interceptors - Memory Profiling: Memory usage monitoring and garbage collection metrics - Performance Dashboards: Dashboard data service with comprehensive metrics visualization - Performance Optimization: Caching strategies, timeout handling, and retry mechanisms
These performance monitoring strategies ensure your C# TuskLang applications are optimized, monitored, and performant in production environments.