feat(logging): build-config debug logging + task traceability #8

Open
claude wants to merge 8 commits from feature/debug-logging-traceability into main
3 changed files with 34 additions and 32 deletions
Showing only changes of commit 50c10b6e75 - Show all commits

View File

@@ -106,7 +106,9 @@ sealed class Program
// Services
sc.AddSingleton<GitService>();
sc.AddSingleton(sp => new WorkerClient(sp.GetRequiredService<AppSettings>().SignalRUrl));
sc.AddSingleton(sp => new WorkerClient(
sp.GetRequiredService<AppSettings>().SignalRUrl,
sp.GetRequiredService<ILogger<WorkerClient>>()));
sc.AddSingleton<IWorkerClient>(sp => sp.GetRequiredService<WorkerClient>());
// Release check + installer update

View File

@@ -11,7 +11,9 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.1" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.11" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Serilog" Version="4.1.0" />
</ItemGroup>
<ItemGroup>

View File

@@ -6,6 +6,8 @@ using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog.Context;
namespace ClaudeDo.Ui.Services;
@@ -30,6 +32,7 @@ sealed class IndefiniteRetryPolicy : IRetryPolicy
public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerClient
{
private readonly HubConnection _hub;
private readonly ILogger<WorkerClient> _logger;
private CancellationTokenSource? _startCts;
private Task _retryLoopTask = Task.CompletedTask;
private readonly object _startLock = new();
@@ -65,8 +68,9 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
public string? LastMergeAllTarget { get; private set; }
public WorkerClient(string signalRUrl)
public WorkerClient(string signalRUrl, ILogger<WorkerClient> logger)
{
_logger = logger;
_hub = new HubConnectionBuilder()
.WithUrl(signalRUrl)
.WithAutomaticReconnect(new IndefiniteRetryPolicy())
@@ -240,20 +244,24 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
catch { return default; }
}
public async Task RunNowAsync(string taskId)
/// <summary>Invoke a task-targeted hub method under a TaskId log scope, emitting a debug trace line.</summary>
private async Task InvokeForTaskAsync(string taskId, string method, params object?[] args)
{
await _hub.InvokeAsync("RunNow", taskId);
using (LogContext.PushProperty("TaskId", taskId))
{
_logger.LogDebug("UI invoking {Method} for task {TaskId}", method, taskId);
await _hub.InvokeCoreAsync(method, args);
}
}
public async Task ContinueTaskAsync(string taskId, string followUpPrompt)
{
await _hub.InvokeAsync("ContinueTask", taskId, followUpPrompt);
}
public Task RunNowAsync(string taskId)
=> InvokeForTaskAsync(taskId, "RunNow", taskId);
public async Task ResetTaskAsync(string taskId)
{
await _hub.InvokeAsync("ResetTask", taskId);
}
public Task ContinueTaskAsync(string taskId, string followUpPrompt)
=> InvokeForTaskAsync(taskId, "ContinueTask", taskId, followUpPrompt);
public Task ResetTaskAsync(string taskId)
=> InvokeForTaskAsync(taskId, "ResetTask", taskId);
public async Task<MergeResultDto> MergeTaskAsync(string taskId, string targetBranch, bool removeWorktree, string commitMessage)
{
@@ -264,10 +272,8 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
public Task<MergeTargetsDto?> GetMergeTargetsAsync(string taskId)
=> TryInvokeAsync<MergeTargetsDto>("GetMergeTargets", taskId);
public async Task CancelTaskAsync(string taskId)
{
await _hub.InvokeAsync("CancelTask", taskId);
}
public Task CancelTaskAsync(string taskId)
=> InvokeForTaskAsync(taskId, "CancelTask", taskId);
public async Task WakeQueueAsync()
{
@@ -386,25 +392,17 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC
await _hub.InvokeAsync("SetTaskStatus", taskId, status.ToString());
}
public async Task ApproveReviewAsync(string taskId)
{
await _hub.InvokeAsync("ApproveReview", taskId);
}
public Task ApproveReviewAsync(string taskId)
=> InvokeForTaskAsync(taskId, "ApproveReview", taskId);
public async Task RejectReviewToQueueAsync(string taskId, string feedback)
{
await _hub.InvokeAsync("RejectReviewToQueue", taskId, feedback);
}
public Task RejectReviewToQueueAsync(string taskId, string feedback)
=> InvokeForTaskAsync(taskId, "RejectReviewToQueue", taskId, feedback);
public async Task RejectReviewToIdleAsync(string taskId)
{
await _hub.InvokeAsync("RejectReviewToIdle", taskId);
}
public Task RejectReviewToIdleAsync(string taskId)
=> InvokeForTaskAsync(taskId, "RejectReviewToIdle", taskId);
public async Task CancelReviewAsync(string taskId)
{
await _hub.InvokeAsync("CancelReview", taskId);
}
public Task CancelReviewAsync(string taskId)
=> InvokeForTaskAsync(taskId, "CancelReview", taskId);
public Task<WorktreeCleanupDto?> CleanupFinishedWorktreesAsync(string? listId = null)
=> TryInvokeAsync<WorktreeCleanupDto>("CleanupFinishedWorktrees", listId);