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:
@@ -132,22 +132,18 @@ builder.Services.AddDbContext<ManagingDbContext>((serviceProvider, options) =>
|
|||||||
// Enable service provider caching for better performance
|
// Enable service provider caching for better performance
|
||||||
options.EnableServiceProviderCaching();
|
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 logger = serviceProvider.GetRequiredService<ILogger<ManagingDbContext>>();
|
||||||
var sentryMonitoringService = serviceProvider.GetRequiredService<SentrySqlMonitoringService>();
|
var sentryMonitoringService = serviceProvider.GetRequiredService<SentrySqlMonitoringService>();
|
||||||
|
|
||||||
options.LogTo(msg =>
|
options.LogTo(msg =>
|
||||||
{
|
{
|
||||||
// Log SQL queries with enhanced formatting
|
// Log only warnings and errors to reduce log volume
|
||||||
if (msg.Contains("Executed DbCommand") || msg.Contains("Executing DbCommand"))
|
if (msg.Contains("Warning") || msg.Contains("Error"))
|
||||||
{
|
|
||||||
Console.WriteLine($"[EF-SQL] {msg}");
|
|
||||||
}
|
|
||||||
else if (msg.Contains("Warning") || msg.Contains("Error"))
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[EF-WARNING] {msg}");
|
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
|
}, ServiceLifetime.Scoped); // Explicitly specify scoped lifetime for proper request isolation
|
||||||
|
|
||||||
// Add specific health checks for databases and other services
|
// Add specific health checks for databases and other services
|
||||||
|
|||||||
@@ -37,7 +37,9 @@
|
|||||||
"LoggingEnabled": false,
|
"LoggingEnabled": false,
|
||||||
"SentryEnabled": false,
|
"SentryEnabled": false,
|
||||||
"LoopDetectionEnabled": true,
|
"LoopDetectionEnabled": true,
|
||||||
"LogErrorsOnly": true
|
"LogErrorsOnly": true,
|
||||||
|
"MaxQueryExecutionsPerWindow": 500,
|
||||||
|
"MaxMethodExecutionsPerWindow": 250
|
||||||
},
|
},
|
||||||
"Cors": {
|
"Cors": {
|
||||||
"AllowedOrigins": [
|
"AllowedOrigins": [
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"LoggingEnabled": true,
|
"LoggingEnabled": true,
|
||||||
"SentryEnabled": false,
|
"SentryEnabled": false,
|
||||||
"LoopDetectionEnabled": true,
|
"LoopDetectionEnabled": true,
|
||||||
"LogSlowQueriesOnly": false
|
"LogSlowQueriesOnly": true
|
||||||
},
|
},
|
||||||
"RunOrleansGrains": true,
|
"RunOrleansGrains": true,
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
|||||||
@@ -96,12 +96,12 @@
|
|||||||
"LoopDetectionEnabled": true,
|
"LoopDetectionEnabled": true,
|
||||||
"PerformanceMonitoringEnabled": true,
|
"PerformanceMonitoringEnabled": true,
|
||||||
"LoopDetectionWindowSeconds": 60,
|
"LoopDetectionWindowSeconds": 60,
|
||||||
"MaxQueryExecutionsPesrWindow": 100,
|
"MaxQueryExecutionsPerWindow": 500,
|
||||||
"MaxMethodExecutionsPerWindow": 50,
|
"MaxMethodExecutionsPerWindow": 250,
|
||||||
"LongRunningQueryThresholdMs": 1000,
|
"LongRunningQueryThresholdMs": 1000,
|
||||||
"SentryAlertThreshold": 5,
|
"SentryAlertThreshold": 5,
|
||||||
"SlowQueryThresholdMs": 1500,
|
"SlowQueryThresholdMs": 1500,
|
||||||
"LogSlowQueriesOnly": false,
|
"LogSlowQueriesOnly": true,
|
||||||
"LogErrorsOnly": false,
|
"LogErrorsOnly": false,
|
||||||
"DataRetentionMinutes": 300
|
"DataRetentionMinutes": 300
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ public abstract class BaseRepositoryWithLogging
|
|||||||
var result = await operation();
|
var result = await operation();
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
||||||
// Only log if slow query (>2000ms) and logging is enabled
|
// Only log if it should be logged based on monitoring settings (respects LogSlowQueriesOnly and thresholds)
|
||||||
if (stopwatch.Elapsed.TotalMilliseconds > 2000 && _sentryMonitoringService.IsLoggingEnabled())
|
if (_sentryMonitoringService.ShouldLogQuery(stopwatch.Elapsed))
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",
|
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",
|
||||||
@@ -121,8 +121,8 @@ public abstract class BaseRepositoryWithLogging
|
|||||||
await operation();
|
await operation();
|
||||||
stopwatch.Stop();
|
stopwatch.Stop();
|
||||||
|
|
||||||
// Only log if slow query (>2000ms) and logging is enabled
|
// Only log if it should be logged based on monitoring settings (respects LogSlowQueriesOnly and thresholds)
|
||||||
if (stopwatch.Elapsed.TotalMilliseconds > 2000 && _sentryMonitoringService.IsLoggingEnabled())
|
if (_sentryMonitoringService.ShouldLogQuery(stopwatch.Elapsed))
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",
|
"[SLOW-SQL] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms",
|
||||||
|
|||||||
@@ -768,12 +768,14 @@ public class ManagingDbContext : DbContext
|
|||||||
// Check for potential loops with Sentry integration
|
// Check for potential loops with Sentry integration
|
||||||
var isLoopDetected = _sentryMonitoringService.TrackQueryExecution(repositoryName, methodName, queryPattern, executionTime);
|
var isLoopDetected = _sentryMonitoringService.TrackQueryExecution(repositoryName, methodName, queryPattern, executionTime);
|
||||||
|
|
||||||
// Log query execution details
|
// Only log query execution details if it should be logged based on monitoring settings
|
||||||
var logLevel = executionTime.TotalMilliseconds > 1000 ? LogLevel.Warning : LogLevel.Debug;
|
if (_sentryMonitoringService.ShouldLogQuery(executionTime))
|
||||||
_logger.Log(logLevel,
|
{
|
||||||
"[SQL-QUERY-TRACKED] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms | Count: {Count}",
|
_logger.LogWarning(
|
||||||
repositoryName, methodName, queryPattern, executionTime.TotalMilliseconds,
|
"[SQL-QUERY-TRACKED] {Repository}.{Method} | Pattern: {Pattern} | Time: {Time}ms | Count: {Count}",
|
||||||
_queryExecutionCounts[queryPattern]);
|
repositoryName, methodName, queryPattern, executionTime.TotalMilliseconds,
|
||||||
|
_queryExecutionCounts[queryPattern]);
|
||||||
|
}
|
||||||
|
|
||||||
// Alert on potential loops
|
// Alert on potential loops
|
||||||
if (isLoopDetected)
|
if (isLoopDetected)
|
||||||
|
|||||||
@@ -71,13 +71,13 @@ public class SentrySqlMonitoringService
|
|||||||
var sentryTags = new Dictionary<string, string>();
|
var sentryTags = new Dictionary<string, string>();
|
||||||
var sentryExtras = new Dictionary<string, object>();
|
var sentryExtras = new Dictionary<string, object>();
|
||||||
|
|
||||||
// Check execution frequency
|
// Check execution frequency (increased threshold to reduce false positives)
|
||||||
if (executionsPerMinute > 20)
|
if (executionsPerMinute > 100)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
reasons.Add($"High frequency: {executionsPerMinute:F1} executions/minute");
|
reasons.Add($"High frequency: {executionsPerMinute:F1} executions/minute");
|
||||||
|
|
||||||
if (executionsPerMinute > 50) // Critical frequency threshold
|
if (executionsPerMinute > 200) // Critical frequency threshold (increased)
|
||||||
{
|
{
|
||||||
isCriticalAlert = true;
|
isCriticalAlert = true;
|
||||||
sentryTags["alert_level"] = "critical";
|
sentryTags["alert_level"] = "critical";
|
||||||
@@ -99,8 +99,8 @@ public class SentrySqlMonitoringService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for rapid successive executions
|
// Check for rapid successive executions (increased threshold to reduce false positives)
|
||||||
if (tracker.ExecutionCount > 5 && timeSinceFirst.TotalSeconds < 10)
|
if (tracker.ExecutionCount > 20 && timeSinceFirst.TotalSeconds < 10)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
isCriticalAlert = true;
|
isCriticalAlert = true;
|
||||||
@@ -109,8 +109,8 @@ public class SentrySqlMonitoringService
|
|||||||
sentryTags["issue_type"] = "rapid_execution";
|
sentryTags["issue_type"] = "rapid_execution";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for consistently slow queries
|
// Check for consistently slow queries (increased threshold to reduce false positives)
|
||||||
if (tracker.ExecutionCount > 3 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
|
if (tracker.ExecutionCount > 10 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");
|
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");
|
||||||
|
|||||||
@@ -73,8 +73,8 @@ public class SqlLoopDetectionService
|
|||||||
var isLoopDetected = false;
|
var isLoopDetected = false;
|
||||||
var reasons = new List<string>();
|
var reasons = new List<string>();
|
||||||
|
|
||||||
// Check execution frequency
|
// Check execution frequency (increased threshold to reduce false positives)
|
||||||
if (executionsPerMinute > 20)
|
if (executionsPerMinute > 100)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
reasons.Add($"High frequency: {executionsPerMinute:F1} executions/minute");
|
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");
|
reasons.Add($"High count: {tracker.ExecutionCount} executions in {timeSinceFirst.TotalMinutes:F1} minutes");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for rapid successive executions
|
// Check for rapid successive executions (increased threshold to reduce false positives)
|
||||||
if (tracker.ExecutionCount > 5 && timeSinceFirst.TotalSeconds < 10)
|
if (tracker.ExecutionCount > 20 && timeSinceFirst.TotalSeconds < 10)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
reasons.Add(
|
reasons.Add(
|
||||||
$"Rapid execution: {tracker.ExecutionCount} executions in {timeSinceFirst.TotalSeconds:F1} seconds");
|
$"Rapid execution: {tracker.ExecutionCount} executions in {timeSinceFirst.TotalSeconds:F1} seconds");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for consistently slow queries
|
// Check for consistently slow queries (increased threshold to reduce false positives)
|
||||||
if (tracker.ExecutionCount > 3 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
|
if (tracker.ExecutionCount > 10 && tracker.AverageExecutionTime.TotalMilliseconds > 1000)
|
||||||
{
|
{
|
||||||
isLoopDetected = true;
|
isLoopDetected = true;
|
||||||
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");
|
reasons.Add($"Consistently slow: {tracker.AverageExecutionTime.TotalMilliseconds:F0}ms average");
|
||||||
|
|||||||
@@ -36,14 +36,14 @@ public class SqlMonitoringSettings
|
|||||||
public int LoopDetectionWindowSeconds { get; set; } = 60;
|
public int LoopDetectionWindowSeconds { get; set; } = 60;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum query executions per window for loop detection (default: 100)
|
/// Maximum query executions per window for loop detection (default: 500)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxQueryExecutionsPerWindow { get; set; } = 100;
|
public int MaxQueryExecutionsPerWindow { get; set; } = 500;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum method executions per window for loop detection (default: 50)
|
/// Maximum method executions per window for loop detection (default: 250)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxMethodExecutionsPerWindow { get; set; } = 50;
|
public int MaxMethodExecutionsPerWindow { get; set; } = 250;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Threshold for long-running queries in milliseconds (default: 1000)
|
/// Threshold for long-running queries in milliseconds (default: 1000)
|
||||||
@@ -61,9 +61,9 @@ public class SqlMonitoringSettings
|
|||||||
public int SlowQueryThresholdMs { get; set; } = 2000;
|
public int SlowQueryThresholdMs { get; set; } = 2000;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to log only slow queries (reduces overhead) (default: false)
|
/// Whether to log only slow queries (reduces overhead) (default: true)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool LogSlowQueriesOnly { get; set; } = false;
|
public bool LogSlowQueriesOnly { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to log only errors (minimal overhead) (default: false)
|
/// Whether to log only errors (minimal overhead) (default: false)
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class SqlQueryLogger : IDisposable
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs the start of a database operation
|
/// Logs the start of a database operation
|
||||||
|
/// Only logs at Debug level to reduce log volume (not logged by default)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LogOperationStart(params (string name, object value)[] parameters)
|
public void LogOperationStart(params (string name, object value)[] parameters)
|
||||||
{
|
{
|
||||||
@@ -42,20 +43,20 @@ public class SqlQueryLogger : IDisposable
|
|||||||
_parameters[name] = value;
|
_parameters[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation(
|
_logger.LogDebug(
|
||||||
"[SQL-OP-START] {OperationId} | {Repository}.{Method} | Started at {StartTime}",
|
"[SQL-OP-START] {OperationId} | {Repository}.{Method} | Started at {StartTime}",
|
||||||
_operationId, _repositoryName, _methodName, _startTime.ToString("HH:mm:ss.fff"));
|
_operationId, _repositoryName, _methodName, _startTime.ToString("HH:mm:ss.fff"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a SQL query execution with timing and parameters
|
/// Logs a SQL query execution with timing and parameters
|
||||||
|
/// Only logs errors and slow queries (>1500ms) to reduce log volume
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LogQueryExecution(string query, TimeSpan executionTime, int? rowsAffected = null, Exception? exception = null)
|
public void LogQueryExecution(string query, TimeSpan executionTime, int? rowsAffected = null, Exception? exception = null)
|
||||||
{
|
{
|
||||||
_executedQueries.Add(query);
|
_executedQueries.Add(query);
|
||||||
|
|
||||||
var logLevel = exception != null ? LogLevel.Error :
|
const int slowQueryThresholdMs = 1500; // Only log queries slower than this threshold
|
||||||
executionTime.TotalMilliseconds > 1000 ? LogLevel.Warning : LogLevel.Information;
|
|
||||||
|
|
||||||
var logMessage = exception != null
|
var logMessage = exception != null
|
||||||
? "[SQL-QUERY-ERROR] {OperationId} | {Repository}.{Method} | Query failed after {ExecutionTime}ms | Error: {Error}"
|
? "[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
|
// Send SQL error to Sentry
|
||||||
SendSqlErrorToSentry(query, executionTime, exception, rowsAffected);
|
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
|
// Send slow query alert to Sentry
|
||||||
if (executionTime.TotalMilliseconds > 2000) // Critical slow query threshold
|
if (executionTime.TotalMilliseconds > 2000) // Critical slow query threshold
|
||||||
{
|
{
|
||||||
SendSlowQueryToSentry(query, executionTime, rowsAffected);
|
SendSlowQueryToSentry(query, executionTime, rowsAffected);
|
||||||
}
|
}
|
||||||
else if (executionTime.TotalMilliseconds > 1000) // Warning threshold
|
else
|
||||||
{
|
{
|
||||||
SendSlowQueryWarningToSentry(query, executionTime, rowsAffected);
|
SendSlowQueryWarningToSentry(query, executionTime, rowsAffected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log query details for slow queries or errors
|
// Log query details for slow queries or errors only
|
||||||
if (executionTime.TotalMilliseconds > 500 || exception != null)
|
if (executionTime.TotalMilliseconds > slowQueryThresholdMs || exception != null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"[SQL-QUERY-DETAILS] {OperationId} | Query: {Query} | Parameters: {Parameters}",
|
"[SQL-QUERY-DETAILS] {OperationId} | Query: {Query} | Parameters: {Parameters}",
|
||||||
@@ -127,13 +129,14 @@ public class SqlQueryLogger : IDisposable
|
|||||||
args[5] = exception.Message;
|
args[5] = exception.Message;
|
||||||
_logger.LogError(exception, logMessage, args);
|
_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);
|
_logger.Log(logLevel, logMessage, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log operation summary for long-running operations
|
// Log operation summary for long-running operations or many queries
|
||||||
if (totalTime.TotalMilliseconds > 1000 || _executedQueries.Count > 5)
|
if (totalTime.TotalMilliseconds > 1500 || _executedQueries.Count > 10)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"[SQL-OP-SUMMARY] {OperationId} | Parameters: {Parameters} | Query Count: {QueryCount} | Total Time: {TotalTime}ms",
|
"[SQL-OP-SUMMARY] {OperationId} | Parameters: {Parameters} | Query Count: {QueryCount} | Total Time: {TotalTime}ms",
|
||||||
|
|||||||
Reference in New Issue
Block a user