feat(logging): improve exception handling and add comprehensive logging

JobExceptionHandlingBehavior:
- Add ILogger for diagnostic output
- Change ResultText to include full exception details (ToString())
- Log JobException with warning level including ProfileId, JobName, ProcessName, BatchId
- Return default instead of re-throwing to allow graceful handling

ReCRequestExecutionBehavior:
- Convert to primary constructor pattern
- Add ILogger for request tracking
- Store RecActionResult in command for later use
- Log successful ReC requests with detailed metrics (TotalActionCount, ActionExceptionCount)
- Improve error handling and logging
This commit is contained in:
2026-08-04 16:33:53 +02:00
parent 2067ffdf2e
commit 111281ac08
2 changed files with 26 additions and 19 deletions

View File

@@ -2,6 +2,7 @@
using ECMJobRunner.Application.ProfileHistories.Commands; using ECMJobRunner.Application.ProfileHistories.Commands;
using ECMJobRunner.Domain.ValueObjects; using ECMJobRunner.Domain.ValueObjects;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -14,7 +15,8 @@ namespace ECMJobRunner.Application.Profiles.Commands.Behaviors;
/// <typeparam name="TRequest">The type of the MediatR request</typeparam> /// <typeparam name="TRequest">The type of the MediatR request</typeparam>
/// <typeparam name="TResponse">The type of the MediatR response</typeparam> /// <typeparam name="TResponse">The type of the MediatR response</typeparam>
/// <param name="Sender">MediatR sender used to dispatch the <see cref="ECMJobRunner.Application.ProfileHistories.Commands.CreateProfileHistoryCommand"/></param> /// <param name="Sender">MediatR sender used to dispatch the <see cref="ECMJobRunner.Application.ProfileHistories.Commands.CreateProfileHistoryCommand"/></param>
public class JobExceptionHandlingBehavior<TRequest, TResponse>(ISender Sender) : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull /// <param name="Logger">Logger for diagnostic output.</param>
public class JobExceptionHandlingBehavior<TRequest, TResponse>(ISender Sender, ILogger<JobExceptionHandlingBehavior<TRequest, TResponse>> Logger) : IPipelineBehavior<TRequest, TResponse> where TRequest : notnull
{ {
/// <summary> /// <summary>
/// Handles the pipeline behavior /// Handles the pipeline behavior
@@ -36,12 +38,14 @@ public class JobExceptionHandlingBehavior<TRequest, TResponse>(ISender Sender) :
{ {
ProfileId = ex.ProfileId, ProfileId = ex.ProfileId,
Result = ResultType.Ok, Result = ResultType.Ok,
ResultText = ex.Message, ResultText = ex.ToString(),
AddedWho = "ECMJobRunner" AddedWho = "ECMJobRunner"
}; };
await Sender.Send(cmd, cancellationToken); await Sender.Send(cmd, cancellationToken);
throw; Logger.LogWarning(ex, "JobException caught in JobExceptionHandlingBehavior for ProfileId {ProfileId}, JobName {JobName}, ProcessName {ProcessName}, BatchId {BatchId}", ex.ProfileId, ex.JobName, ex.ProcessName, ex.BatchId);
return default!;
} }
} }
} }

View File

@@ -3,6 +3,7 @@ using ECMJobRunner.Application.Common.Exceptions;
using ECMJobRunner.Application.Common.Options; using ECMJobRunner.Application.Common.Options;
using ECMJobRunner.Application.Profiles.Commands; using ECMJobRunner.Application.Profiles.Commands;
using MediatR; using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using ReC.Client; using ReC.Client;
using ReC.Client.Api; using ReC.Client.Api;
@@ -18,22 +19,15 @@ namespace ECMJobRunner.Application.Profiles.Commands.Behaviors
/// </summary> /// </summary>
/// <typeparam name="TRequest">The request type</typeparam> /// <typeparam name="TRequest">The request type</typeparam>
/// <typeparam name="TResponse">The response type</typeparam> /// <typeparam name="TResponse">The response type</typeparam>
public class ReCRequestExecutionBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> /// <remarks>
/// Initializes a new instance of ReCRequestExecutionBehavior
/// </remarks>
/// <param name="ReCClient">ReC client for HTTP requests</param>
/// <param name="options">DEX job configuration options</param>
public class ReCRequestExecutionBehavior<TRequest, TResponse>(ReCClient ReCClient, IOptions<DexJobOptions> options, ILogger<ReCRequestExecutionBehavior<TRequest, TResponse>> Logger) : IPipelineBehavior<TRequest, TResponse>
where TRequest : notnull where TRequest : notnull
{ {
private readonly ReCClient _reCClient; private readonly DexJobOptions Options = options.Value;
private readonly DexJobOptions _options;
/// <summary>
/// Initializes a new instance of ReCRequestExecutionBehavior
/// </summary>
/// <param name="reCClient">ReC client for HTTP requests</param>
/// <param name="options">DEX job configuration options</param>
public ReCRequestExecutionBehavior(ReCClient reCClient, IOptions<DexJobOptions> options)
{
_reCClient = reCClient;
_options = options.Value;
}
/// <summary> /// <summary>
/// Handles the pipeline behavior /// Handles the pipeline behavior
@@ -57,14 +51,23 @@ namespace ECMJobRunner.Application.Profiles.Commands.Behaviors
{ {
try try
{ {
await _reCClient.RecActions.InvokeAsync(command.Job.ProfileId, new InvokeReferences() command.RecActionResult = await ReCClient.RecActions.InvokeAsync(command.Job.ProfileId, new InvokeReferences()
{ {
BatchId = command.BatchId, BatchId = command.BatchId,
}, cancel); }, cancel);
Logger.LogInformation(
"ReC request completed successfully. Profile ID: {ProfileId} | Job name: {JobName} | Batch ID: {BatchId} | Total action count: {TotalActionCount} | Action exception count: {ActionExceptionCount}",
command.Job.ProfileId,
command.Job.Name,
command.BatchId,
command.RecActionResult?.TotalActionCount ?? 0,
command.RecActionResult?.ActionExceptionCount ?? 0);
} }
catch (Exception ex) catch (Exception ex)
{ {
if (_options.Error.ReCRequest.OnSending == ErrorAction.Stop) if (Options.Error.ReCRequest.OnSending == ErrorAction.Stop)
{ {
throw new JobHttpException( throw new JobHttpException(
profileId: command.Job.ProfileId, profileId: command.Job.ProfileId,