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:
2025-11-23 21:49:23 +07:00
parent d10ce5e3ba
commit c7c89c903f

View 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);
}
}
}