Skip to content

Commit 7323a0c

Browse files
Improve logging
1 parent 8d5bb1e commit 7323a0c

3 files changed

Lines changed: 119 additions & 52 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Microsoft.Extensions.Logging;
2+
3+
namespace RemoteExec.Client;
4+
5+
internal class RemoteExecLoggerProvider : ILoggerProvider
6+
{
7+
private bool disposedValue;
8+
private readonly ILogger logger;
9+
10+
public RemoteExecLoggerProvider(ILogger logger)
11+
{
12+
this.logger = logger;
13+
}
14+
15+
public ILogger CreateLogger(string categoryName)
16+
{
17+
return logger;
18+
}
19+
20+
protected virtual void Dispose(bool disposing)
21+
{
22+
if (!disposedValue)
23+
{
24+
// No managed or unmanaged resources to dispose
25+
disposedValue = true;
26+
}
27+
}
28+
29+
public void Dispose()
30+
{
31+
Dispose(disposing: true);
32+
GC.SuppressFinalize(this);
33+
}
34+
}

RemoteExec.Client/RemoteExecutor.cs

Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Microsoft.AspNetCore.SignalR.Client;
22
using Microsoft.Extensions.Logging;
3+
using Microsoft.Extensions.Logging.Abstractions;
34

45
using RemoteExec.Shared;
56

@@ -19,18 +20,35 @@ public class RemoteExecutor : IDisposable
1920
private CancellationTokenSource distributorCts = new();
2021
private Task? distributorTask;
2122
private readonly LoadBalancingStrategy loadBalancingStrategy;
22-
23+
private readonly ILogger logger;
2324
private bool disposedValue;
2425

2526
public event EventHandler<ServerMetricsUpdatedEventArgs>? MetricsUpdated;
2627

27-
public RemoteExecutor(string url) : this([url], LoadBalancingStrategy.ResourceAware)
28+
public RemoteExecutor(string url) : this([url], LoadBalancingStrategy.ResourceAware, NullLogger.Instance)
2829
{
2930
}
3031

31-
public RemoteExecutor(string[] urls, LoadBalancingStrategy loadBalancingStrategy = LoadBalancingStrategy.ResourceAware)
32+
public RemoteExecutor(string url, ILogger logger) : this([url], LoadBalancingStrategy.ResourceAware, logger)
33+
{
34+
}
35+
36+
public RemoteExecutor(string url, LoadBalancingStrategy loadBalancingStrategy) : this([url], loadBalancingStrategy, NullLogger.Instance)
37+
{
38+
}
39+
40+
public RemoteExecutor(string[] urls) : this(urls, LoadBalancingStrategy.ResourceAware, NullLogger.Instance)
41+
{
42+
}
43+
44+
public RemoteExecutor(string[] urls, LoadBalancingStrategy loadBalancingStrategy) : this(urls, loadBalancingStrategy, NullLogger.Instance)
45+
{
46+
}
47+
48+
public RemoteExecutor(string[] urls, LoadBalancingStrategy loadBalancingStrategy, ILogger logger)
3249
{
3350
this.loadBalancingStrategy = loadBalancingStrategy;
51+
this.logger = logger;
3452

3553
foreach (string url in urls)
3654
{
@@ -42,7 +60,7 @@ public RemoteExecutor(string[] urls, LoadBalancingStrategy loadBalancingStrategy
4260
.WithAutomaticReconnect()
4361
.ConfigureLogging(logging =>
4462
{
45-
_ = logging.AddProvider(new RemoteExecLoggerProvider());
63+
_ = logging.AddProvider(new RemoteExecLoggerProvider(logger));
4664
})
4765
.Build();
4866

@@ -106,20 +124,24 @@ public async Task StartAsync(CancellationToken cancellationToken = default)
106124

107125
public async Task StopAsync(CancellationToken cancellationToken = default)
108126
{
127+
logger.LogInformation("Stopping RemoteExecutor...");
128+
109129
await distributorCts.CancelAsync();
110130

111131
if (distributorTask != null)
112132
{
113133
try
114134
{
115135
await distributorTask;
136+
logger.LogDebug("Distributor task completed successfully");
116137
}
117-
catch (OperationCanceledException)
138+
catch (OperationCanceledException ex)
118139
{
119-
// Expected
140+
logger.LogError(ex, "Distributor task was canceled");
120141
}
121142
}
122143

144+
logger.LogDebug("Completing task channels for {ServerCount} servers", servers.Count);
123145
foreach (ServerConnection server in servers)
124146
{
125147
server.TaskChannel.Writer.Complete();
@@ -133,6 +155,7 @@ public async Task StopAsync(CancellationToken cancellationToken = default)
133155
}
134156

135157
await Task.WhenAll(stopTasks);
158+
logger.LogInformation("RemoteExecutor stopped successfully");
136159
}
137160

138161
public Dictionary<string, ServerMetrics> GetCurrentServerMetrics()
@@ -315,34 +338,3 @@ public void Dispose()
315338
GC.SuppressFinalize(this);
316339
}
317340
}
318-
319-
internal class RemoteExecLoggerProvider : ILoggerProvider
320-
{
321-
public ILogger CreateLogger(string categoryName)
322-
{
323-
return new RemoteExecLogger();
324-
}
325-
326-
public void Dispose()
327-
{
328-
throw new NotImplementedException();
329-
}
330-
}
331-
332-
internal class RemoteExecLogger : ILogger
333-
{
334-
public IDisposable? BeginScope<TState>(TState state) where TState : notnull
335-
{
336-
return null;
337-
}
338-
339-
public bool IsEnabled(LogLevel logLevel)
340-
{
341-
return true;
342-
}
343-
344-
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
345-
{
346-
Console.WriteLine($"[{logLevel}] {formatter(state, exception)}");
347-
}
348-
}

RemoteExec.Server/Hubs/RemoteExecutionHub.cs

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class RemoteExecutionHub(ILogger<RemoteExecutionHub> logger) : Hub
1515

1616
private static readonly ConcurrentDictionary<Guid, TaskCompletionSource<byte[]>> pendingAssemblyRequests = new();
1717

18+
// Track pending assembly requests per connection to avoid duplicate requests
19+
private static readonly ConcurrentDictionary<string, ConcurrentDictionary<string, Task<byte[]>>> pendingAssemblyRequestsByConnection = new();
20+
1821
private static ServerMetrics? lastMetrics;
1922
private static DateTime lastMetricsTimestamp;
2023
private static TimeSpan lastTotalProcessorTime;
@@ -28,6 +31,7 @@ public override Task OnConnectedAsync()
2831
RemoteJobAssemblyLoadContext assemblyLoadContext = new RemoteJobAssemblyLoadContext($"RemoteJob_{Guid.NewGuid()}");
2932

3033
_ = connections.TryAdd(Context.ConnectionId, assemblyLoadContext);
34+
_ = pendingAssemblyRequestsByConnection.TryAdd(Context.ConnectionId, new ConcurrentDictionary<string, Task<byte[]>>());
3135

3236
logger.LogInformation("Connection {ConnectionId} established", Context.ConnectionId);
3337

@@ -42,6 +46,8 @@ public override Task OnDisconnectedAsync(Exception? exception)
4246
logger.LogInformation("Connection {ConnectionId} disconnected", Context.ConnectionId);
4347
}
4448

49+
_ = pendingAssemblyRequestsByConnection.TryRemove(Context.ConnectionId, out _);
50+
4551
return base.OnDisconnectedAsync(exception);
4652
}
4753

@@ -99,8 +105,6 @@ private async Task<RemoteExecutionResult> ExecuteTask(RemoteExecutionRequest req
99105
{
100106
_ = Interlocked.Increment(ref activeTasks);
101107

102-
logger.LogInformation(req.MethodName);
103-
104108
try
105109
{
106110
if (!connections.TryGetValue(Context.ConnectionId, out RemoteJobAssemblyLoadContext? assemblyLoadContext))
@@ -282,17 +286,36 @@ private async Task<Assembly> RequestAssemblyAsync(string assemblyName)
282286
{
283287
try
284288
{
285-
Guid guid = Guid.NewGuid();
286-
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
289+
if (!pendingAssemblyRequestsByConnection.TryGetValue(Context.ConnectionId, out ConcurrentDictionary<string, Task<byte[]>>? connectionPendingRequests))
290+
{
291+
throw new InvalidOperationException("Connection not found");
292+
}
287293

288-
_ = pendingAssemblyRequests.TryAdd(guid, tcs);
294+
Task<byte[]> assemblyBytesTask = connectionPendingRequests.GetOrAdd(assemblyName, key =>
295+
{
296+
return Task.Run(async () =>
297+
{
298+
try
299+
{
300+
Guid guid = Guid.NewGuid();
301+
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
289302

290-
await Clients.Caller.SendAsync("RequestAssembly", assemblyName, guid);
303+
_ = pendingAssemblyRequests.TryAdd(guid, tcs);
304+
305+
await Clients.Caller.SendAsync("RequestAssembly", key, guid);
306+
307+
// Wait for the assembly with a timeout
308+
return await tcs.Task.WaitAsync(TimeSpan.FromSeconds(30));
309+
}
310+
finally
311+
{
312+
_ = connectionPendingRequests.TryRemove(key, out _);
313+
}
314+
});
315+
});
291316

292-
// Wait for the assembly with a timeout
293-
byte[] assemblyBytes = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(30));
317+
byte[] assemblyBytes = await assemblyBytesTask;
294318

295-
// Return a temporary assembly just for metadata inspection
296319
using MemoryStream ms = new MemoryStream(assemblyBytes);
297320
return Assembly.Load(assemblyBytes);
298321
}
@@ -307,16 +330,34 @@ private async Task<byte[]> GetAssemblyBytesAsync(Assembly assembly)
307330
{
308331
string assemblyName = assembly.GetName().FullName!;
309332

310-
Guid guid = Guid.NewGuid();
311-
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
333+
if (!pendingAssemblyRequestsByConnection.TryGetValue(Context.ConnectionId, out ConcurrentDictionary<string, Task<byte[]>>? connectionPendingRequests))
334+
{
335+
throw new InvalidOperationException("Connection not found");
336+
}
312337

313-
_ = pendingAssemblyRequests.TryAdd(guid, tcs);
338+
Task<byte[]> assemblyBytesTask = connectionPendingRequests.GetOrAdd(assemblyName, key =>
339+
{
340+
return Task.Run(async () =>
341+
{
342+
try
343+
{
344+
Guid guid = Guid.NewGuid();
345+
TaskCompletionSource<byte[]> tcs = new TaskCompletionSource<byte[]>();
346+
347+
_ = pendingAssemblyRequests.TryAdd(guid, tcs);
314348

315-
await Clients.Caller.SendAsync("RequestAssembly", assemblyName, guid);
349+
await Clients.Caller.SendAsync("RequestAssembly", key, guid);
316350

317-
byte[] assemblyBytes = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(30));
351+
return await tcs.Task.WaitAsync(TimeSpan.FromSeconds(30));
352+
}
353+
finally
354+
{
355+
_ = connectionPendingRequests.TryRemove(key, out _);
356+
}
357+
});
358+
});
318359

319-
return assemblyBytes;
360+
return await assemblyBytesTask;
320361
}
321362

322363
private async Task PreLoadReferencedAssembliesAsync(RemoteJobAssemblyLoadContext assemblyLoadContext, Assembly assembly)

0 commit comments

Comments
 (0)