Enhance error handling and logging in BotController, LiveTradingBotGrain, and BotService
- Added specific handling for ServiceUnavailableException in BotController to return user-friendly messages. - Improved logging for Orleans exceptions in both BotController and BotService to provide clearer context during errors. - Implemented verification of position closure status in LiveTradingBotGrain, including timeout handling for closing positions. - Enhanced logging for critical and non-critical operations during bot stop processes to ensure better traceability.
This commit is contained in:
119
src/Managing.Core/Exceptions/OrleansExceptionHelper.cs
Normal file
119
src/Managing.Core/Exceptions/OrleansExceptionHelper.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Managing.Core.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class to detect and handle Orleans-specific exceptions
|
||||
/// </summary>
|
||||
public static class OrleansExceptionHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if an exception is an Orleans-specific exception
|
||||
/// </summary>
|
||||
public static bool IsOrleansException(Exception ex)
|
||||
{
|
||||
if (ex == null) return false;
|
||||
|
||||
var exceptionTypeName = ex.GetType().Name;
|
||||
var exceptionMessage = ex.Message ?? string.Empty;
|
||||
|
||||
// Check for Orleans exception type names
|
||||
if (exceptionTypeName.Contains("Orleans", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for Orleans-specific exception messages
|
||||
if (exceptionMessage.Contains("Orleans", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("grain", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("silo", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("activation", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("forwarding failed", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("Unable to create local activation", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("Request timeout", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("Deadlock", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check inner exception recursively
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
return IsOrleansException(ex.InnerException);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an Orleans exception to a user-friendly message
|
||||
/// </summary>
|
||||
public static string GetUserFriendlyMessage(Exception ex, string operation = "operation")
|
||||
{
|
||||
if (!IsOrleansException(ex))
|
||||
{
|
||||
return ex.Message;
|
||||
}
|
||||
|
||||
var exceptionTypeName = ex.GetType().Name;
|
||||
var exceptionMessage = ex.Message ?? string.Empty;
|
||||
|
||||
// Handle timeout exceptions
|
||||
if (ex is TimeoutException || ex is TaskCanceledException ||
|
||||
exceptionMessage.Contains("timeout", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("timed out", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return $"The {operation} timed out. This may occur when closing positions takes longer than expected. Please try again in a moment.";
|
||||
}
|
||||
|
||||
// Handle deadlock exceptions
|
||||
if (exceptionMessage.Contains("deadlock", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return $"The {operation} could not complete due to a system conflict. Please try again in a moment.";
|
||||
}
|
||||
|
||||
// Handle activation/grain errors
|
||||
if (exceptionMessage.Contains("activation", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("grain", StringComparison.OrdinalIgnoreCase) ||
|
||||
exceptionMessage.Contains("forwarding failed", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return $"The {operation} encountered a temporary system issue. Please try again in a moment.";
|
||||
}
|
||||
|
||||
// Generic Orleans error
|
||||
return $"The {operation} encountered a temporary system issue. Please try again in a moment. If the problem persists, contact support.";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps an async operation with timeout and Orleans exception handling
|
||||
/// </summary>
|
||||
public static async Task<T> ExecuteWithTimeoutAndOrleansHandling<T>(
|
||||
Func<Task<T>> operation,
|
||||
TimeSpan timeout,
|
||||
string operationName,
|
||||
ILogger logger = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeout);
|
||||
var task = operation();
|
||||
var timeoutTask = Task.Delay(timeout, cts.Token);
|
||||
|
||||
var completedTask = await Task.WhenAny(task, timeoutTask);
|
||||
if (completedTask == timeoutTask)
|
||||
{
|
||||
throw new TimeoutException($"The {operationName} timed out after {timeout.TotalSeconds} seconds.");
|
||||
}
|
||||
|
||||
return await task;
|
||||
}
|
||||
catch (Exception ex) when (IsOrleansException(ex) || ex is TimeoutException)
|
||||
{
|
||||
logger?.LogError(ex, "Orleans exception or timeout during {OperationName}: {ExceptionType} - {Message}",
|
||||
operationName, ex.GetType().Name, ex.Message);
|
||||
var userMessage = GetUserFriendlyMessage(ex, operationName);
|
||||
throw new ServiceUnavailableException(userMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user