Update configuration settings and logging behavior for SQL monitoring

- Increased thresholds for maximum query and method executions per window to 500 and 250, respectively, to reduce false positives in loop detection.
- Enabled logging of slow queries only, improving performance by reducing log volume.
- Adjusted SQL query logging to capture only warnings and errors, further optimizing logging efficiency.
- Updated various settings across appsettings files to reflect these changes, ensuring consistency in configuration.
This commit is contained in:
2025-11-24 01:02:53 +07:00
parent 372d19f840
commit fef66f6d7b
10 changed files with 56 additions and 53 deletions

View File

@@ -132,22 +132,18 @@ builder.Services.AddDbContext<ManagingDbContext>((serviceProvider, options) =>
// Enable service provider caching for better performance
options.EnableServiceProviderCaching();
// Enable comprehensive SQL query logging for monitoring and debugging
// Enable SQL query logging for warnings and errors only (reduced logging)
var logger = serviceProvider.GetRequiredService<ILogger<ManagingDbContext>>();
var sentryMonitoringService = serviceProvider.GetRequiredService<SentrySqlMonitoringService>();
options.LogTo(msg =>
{
// Log SQL queries with enhanced formatting
if (msg.Contains("Executed DbCommand") || msg.Contains("Executing DbCommand"))
{
Console.WriteLine($"[EF-SQL] {msg}");
}
else if (msg.Contains("Warning") || msg.Contains("Error"))
// Log only warnings and errors to reduce log volume
if (msg.Contains("Warning") || msg.Contains("Error"))
{
Console.WriteLine($"[EF-WARNING] {msg}");
}
}, LogLevel.Information); // Log all SQL operations for monitoring
}, LogLevel.Warning); // Log only warnings and errors to reduce SQL log volume
}, ServiceLifetime.Scoped); // Explicitly specify scoped lifetime for proper request isolation
// Add specific health checks for databases and other services

View File

@@ -37,7 +37,9 @@
"LoggingEnabled": false,
"SentryEnabled": false,
"LoopDetectionEnabled": true,
"LogErrorsOnly": true
"LogErrorsOnly": true,
"MaxQueryExecutionsPerWindow": 500,
"MaxMethodExecutionsPerWindow": 250
},
"Cors": {
"AllowedOrigins": [

View File

@@ -34,7 +34,7 @@
"LoggingEnabled": true,
"SentryEnabled": false,
"LoopDetectionEnabled": true,
"LogSlowQueriesOnly": false
"LogSlowQueriesOnly": true
},
"RunOrleansGrains": true,
"AllowedHosts": "*",

View File

@@ -96,12 +96,12 @@
"LoopDetectionEnabled": true,
"PerformanceMonitoringEnabled": true,
"LoopDetectionWindowSeconds": 60,
"MaxQueryExecutionsPesrWindow": 100,
"MaxMethodExecutionsPerWindow": 50,
"MaxQueryExecutionsPerWindow": 500,
"MaxMethodExecutionsPerWindow": 250,
"LongRunningQueryThresholdMs": 1000,
"SentryAlertThreshold": 5,
"SlowQueryThresholdMs": 1500,
"LogSlowQueriesOnly": false,
"LogSlowQueriesOnly": true,
"LogErrorsOnly": false,
"DataRetentionMinutes": 300
},

View File

@@ -50,8 +50,8 @@ public abstract class BaseRepositoryWithLogging
var result = await operation();
stopwatch.Stop();
// Only log if slow query (>2000ms) and logging is enabled
if (stopwatch.Elapsed.TotalMilliseconds > 2000 && _sentryMonitoringService.IsLoggingEnabled())
// Only log if it should be logged based on monitoring settings (respects LogSlowQueriesOnly and thresholds)
if (_sentryMonitoringService.ShouldLogQuery(stopwatch.Elapsed))
{
_logger.LogWarning(
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",
@@ -121,8 +121,8 @@ public abstract class BaseRepositoryWithLogging
await operation();
stopwatch.Stop();
// Only log if slow query (>2000ms) and logging is enabled
if (stopwatch.Elapsed.TotalMilliseconds > 2000 && _sentryMonitoringService.IsLoggingEnabled())
// Only log if it should be logged based on monitoring settings (respects LogSlowQueriesOnly and thresholds)
if (_sentryMonitoringService.ShouldLogQuery(stopwatch.Elapsed))
{
_logger.LogWarning(
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",

View File

@@ -768,12 +768,14 @@ public class ManagingDbContext : DbContext
// Check for potential loops with Sentry integration
var isLoopDetected = _sentryMonitoringService.TrackQueryExecution(repositoryName, methodName, queryPattern, executionTime);
// Log query execution details
var logLevel = executionTime.TotalMilliseconds > 1000 ? LogLevel.Warning : LogLevel.Debug;
_logger.Log(logLevel,
"[SQL-QUERY-TRACKED] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms | Count: {Count}",
repositoryName, methodName, queryPattern, executionTime.TotalMilliseconds,
_queryExecutionCounts[queryPattern]);
// Only log query execution details if it should be logged based on monitoring settings
if (_sentryMonitoringService.ShouldLogQuery(executionTime))
{
_logger.LogWarning(
"[SQL-QUERY-TRACKED] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms | Count: {Count}",
repositoryName, methodName, queryPattern, executionTime.TotalMilliseconds,
_queryExecutionCounts[queryPattern]);
}
// Alert on potential loops
if (isLoopDetected)

View File

@@ -71,13 +71,13 @@ public class SentrySqlMonitoringService
var sentryTags = new Dictionary<string, string>();
var sentryExtras = new Dictionary<string, object>();
// Check execution frequency
if (executionsPerMinute > 20)
// Check execution frequency (increased threshold to reduce false positives)
if (executionsPerMinute > 100)
{
isLoopDetected = true;
reasons.Add($"High frequency: {executionsPerMinute:F1} executions/minute");
if (executionsPerMinute > 50) // Critical frequency threshold
if (executionsPerMinute > 200) // Critical frequency threshold (increased)
{
isCriticalAlert = true;
sentryTags["alert_level"] = "critical";
@@ -99,8 +99,8 @@ public class SentrySqlMonitoringService
}
}
// Check for rapid successive executions
if (tracker.ExecutionCount > 5 && timeSinceFirst.TotalSeconds < 10)
// Check for rapid successive executions (increased threshold to reduce false positives)
if (tracker.ExecutionCount > 20 && timeSinceFirst.TotalSeconds < 10)
{
isLoopDetected = true;
isCriticalAlert = true;
@@ -109,8 +109,8 @@ public class SentrySqlMonitoringService
sentryTags["issue_type"] = "rapid_execution";
}
// Check for consistently slow queries
if (tracker.ExecutionCount > 3 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
// Check for consistently slow queries (increased threshold to reduce false positives)
if (tracker.ExecutionCount > 10 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
{
isLoopDetected = true;
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");

View File

@@ -73,8 +73,8 @@ public class SqlLoopDetectionService
var isLoopDetected = false;
var reasons = new List<string>();
// Check execution frequency
if (executionsPerMinute > 20)
// Check execution frequency (increased threshold to reduce false positives)
if (executionsPerMinute > 100)
{
isLoopDetected = true;
reasons.Add($"High frequency: {executionsPerMinute:F1} executions/minute");
@@ -87,16 +87,16 @@ public class SqlLoopDetectionService
reasons.Add($"High count: {tracker.ExecutionCount} executions in {timeSinceFirst.TotalMinutes:F1} minutes");
}
// Check for rapid successive executions
if (tracker.ExecutionCount > 5 && timeSinceFirst.TotalSeconds < 10)
// Check for rapid successive executions (increased threshold to reduce false positives)
if (tracker.ExecutionCount > 20 && timeSinceFirst.TotalSeconds < 10)
{
isLoopDetected = true;
reasons.Add(
$"Rapid execution: {tracker.ExecutionCount} executions in {timeSinceFirst.TotalSeconds:F1} seconds");
}
// Check for consistently slow queries
if (tracker.ExecutionCount > 3 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
// Check for consistently slow queries (increased threshold to reduce false positives)
if (tracker.ExecutionCount > 10 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
{
isLoopDetected = true;
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");

View File

@@ -36,14 +36,14 @@ public class SqlMonitoringSettings
public int LoopDetectionWindowSeconds { get; set; } = 60;
/// <summary>
/// Maximum query executions per window for loop detection (default: 100)
/// Maximum query executions per window for loop detection (default: 500)
/// </summary>
public int MaxQueryExecutionsPerWindow { get; set; } = 100;
public int MaxQueryExecutionsPerWindow { get; set; } = 500;
/// <summary>
/// Maximum method executions per window for loop detection (default: 50)
/// Maximum method executions per window for loop detection (default: 250)
/// </summary>
public int MaxMethodExecutionsPerWindow { get; set; } = 50;
public int MaxMethodExecutionsPerWindow { get; set; } = 250;
/// <summary>
/// Threshold for long-running queries in milliseconds (default: 1000)
@@ -61,9 +61,9 @@ public class SqlMonitoringSettings
public int SlowQueryThresholdMs { get; set; } = 2000;
/// <summary>
/// Whether to log only slow queries (reduces overhead) (default: false)
/// Whether to log only slow queries (reduces overhead) (default: true)
/// </summary>
public bool LogSlowQueriesOnly { get; set; } = false;
public bool LogSlowQueriesOnly { get; set; } = true;
/// <summary>
/// Whether to log only errors (minimal overhead) (default: false)

View File

@@ -34,6 +34,7 @@ public class SqlQueryLogger : IDisposable
/// <summary>
/// Logs the start of a database operation
/// Only logs at Debug level to reduce log volume (not logged by default)
/// </summary>
public void LogOperationStart(params (string name, object value)[] parameters)
{
@@ -42,20 +43,20 @@ public class SqlQueryLogger : IDisposable
_parameters[name] = value;
}
_logger.LogInformation(
_logger.LogDebug(
"[SQL-OP-START] {OperationId} | {Repository}.{Method} | Started at {StartTime}",
_operationId, _repositoryName, _methodName, _startTime.ToString("HH:mm:ss.fff"));
}
/// <summary>
/// Logs a SQL query execution with timing and parameters
/// Only logs errors and slow queries (>1500ms) to reduce log volume
/// </summary>
public void LogQueryExecution(string query, TimeSpan executionTime, int? rowsAffected = null, Exception? exception = null)
{
_executedQueries.Add(query);
var logLevel = exception != null ? LogLevel.Error :
executionTime.TotalMilliseconds > 1000 ? LogLevel.Warning : LogLevel.Information;
const int slowQueryThresholdMs = 1500; // Only log queries slower than this threshold
var logMessage = exception != null
? "[SQL-QUERY-ERROR] {OperationId} | {Repository}.{Method} | Query failed after {ExecutionTime}ms | Error: {Error}"
@@ -75,23 +76,24 @@ public class SqlQueryLogger : IDisposable
// Send SQL error to Sentry
SendSqlErrorToSentry(query, executionTime, exception, rowsAffected);
}
else
else if (executionTime.TotalMilliseconds > slowQueryThresholdMs)
{
_logger.Log(logLevel, logMessage, args);
// Only log slow queries, not normal queries
_logger.LogWarning(logMessage, args);
// Send slow query alert to Sentry
if (executionTime.TotalMilliseconds > 2000) // Critical slow query threshold
{
SendSlowQueryToSentry(query, executionTime, rowsAffected);
}
else if (executionTime.TotalMilliseconds > 1000) // Warning threshold
else
{
SendSlowQueryWarningToSentry(query, executionTime, rowsAffected);
}
}
// Log query details for slow queries or errors
if (executionTime.TotalMilliseconds > 500 || exception != null)
// Log query details for slow queries or errors only
if (executionTime.TotalMilliseconds > slowQueryThresholdMs || exception != null)
{
_logger.LogWarning(
"[SQL-QUERY-DETAILS] {OperationId} | Query: {Query} | Parameters: {Parameters}",
@@ -127,13 +129,14 @@ public class SqlQueryLogger : IDisposable
args[5] = exception.Message;
_logger.LogError(exception, logMessage, args);
}
else
else if (totalTime.TotalMilliseconds > 1500 || _executedQueries.Count > 10)
{
// Only log slow operations or operations with many queries
_logger.Log(logLevel, logMessage, args);
}
// Log operation summary for long-running operations
if (totalTime.TotalMilliseconds > 1000 || _executedQueries.Count > 5)
// Log operation summary for long-running operations or many queries
if (totalTime.TotalMilliseconds > 1500 || _executedQueries.Count > 10)
{
_logger.LogWarning(
"[SQL-OP-SUMMARY] {OperationId} | Parameters: {Parameters} | Query Count: {QueryCount} | Total Time: {TotalTime}ms",