Skip to content

Commit 96dd822

Browse files
committed
override BackgroundService.StartAsync to add logging
1 parent 6cbc64b commit 96dd822

File tree

2 files changed

+114
-15
lines changed

2 files changed

+114
-15
lines changed

src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ public class MuninNodeBackgroundService : BackgroundService {
1616
eventId: default, // TODO
1717
formatString: "Munin node '{HostName}' starting."
1818
);
19+
private static readonly Action<ILogger, string, Exception?> LogStarted = LoggerMessage.Define<string>(
20+
LogLevel.Information,
21+
eventId: default, // TODO
22+
formatString: "Munin node '{HostName}' started."
23+
);
1924
private static readonly Action<ILogger, string, Exception?> LogStopping = LoggerMessage.Define<string>(
2025
LogLevel.Information,
2126
eventId: default, // TODO
@@ -79,14 +84,27 @@ public override void Dispose()
7984
GC.SuppressFinalize(this);
8085
}
8186

82-
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
87+
public override async Task StartAsync(CancellationToken cancellationToken)
8388
{
8489
if (node is null)
8590
throw new ObjectDisposedException(GetType().FullName);
8691

92+
cancellationToken.ThrowIfCancellationRequested();
93+
8794
if (Logger is not null)
8895
LogStarting(Logger, node.HostName, null);
8996

97+
await base.StartAsync(cancellationToken).ConfigureAwait(false);
98+
99+
if (Logger is not null)
100+
LogStarted(Logger, node.HostName, null);
101+
}
102+
103+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
104+
{
105+
if (node is null)
106+
throw new ObjectDisposedException(GetType().FullName);
107+
90108
await node.RunAsync(stoppingToken).ConfigureAwait(false);
91109
}
92110

tests/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,99 @@ await reader.ReadLineAsync(cancellationToken),
132132
}
133133
}
134134

135+
private class EmptyPluginProvider : IPluginProvider {
136+
public IReadOnlyCollection<IPlugin> Plugins { get; } = [];
137+
public INodeSessionCallback? SessionCallback => null;
138+
}
139+
140+
private class EmptyNode : LocalNode {
141+
public override IPluginProvider PluginProvider { get; } = new EmptyPluginProvider();
142+
public override string HostName => "munin-node.localhost";
143+
144+
public EmptyNode()
145+
: base(
146+
listenerFactory: null,
147+
accessRule: null,
148+
logger: null
149+
)
150+
{
151+
}
152+
}
153+
154+
private class HookStartNode : EmptyNode {
155+
public EventHandler? Starting = null;
156+
public EventHandler? Started = null;
157+
158+
public HookStartNode()
159+
: base()
160+
{
161+
}
162+
163+
protected override ValueTask StartingAsync(CancellationToken cancellationToken)
164+
{
165+
Starting?.Invoke(this, EventArgs.Empty);
166+
167+
return default;
168+
}
169+
170+
protected override ValueTask StartedAsync(CancellationToken cancellationToken)
171+
{
172+
Started?.Invoke(this, EventArgs.Empty);
173+
174+
return default;
175+
}
176+
}
177+
178+
[Test]
179+
public void StartAsync_CancellationRequestedBeforeStarting()
180+
{
181+
var numberOfTimesStartingInvoked = 0;
182+
var numberOfTimesStartedInvoked = 0;
183+
184+
using var hookStartNode = new HookStartNode();
185+
186+
hookStartNode.Starting += (_, _) => numberOfTimesStartingInvoked++;
187+
hookStartNode.Started += (_, _) => numberOfTimesStartedInvoked++;
188+
189+
using var muninNodeService = new MuninNodeBackgroundService(hookStartNode);
190+
191+
// start service
192+
Assert.That(
193+
async () => await muninNodeService.StartAsync(cancellationToken: new(canceled: true)),
194+
Throws.InstanceOf<OperationCanceledException>()
195+
);
196+
197+
Assert.That(numberOfTimesStartingInvoked, Is.Zero);
198+
Assert.That(numberOfTimesStartedInvoked, Is.Zero);
199+
}
200+
201+
[Test]
202+
public void StartAsync_CancellationRequestedWhileStarting()
203+
{
204+
var numberOfTimesStartingInvoked = 0;
205+
var numberOfTimesStartedInvoked = 0;
206+
207+
using var hookStartNode = new HookStartNode();
208+
using var ctsStarting = new CancellationTokenSource();
209+
210+
hookStartNode.Starting += (_, _) => {
211+
numberOfTimesStartingInvoked++;
212+
ctsStarting.Cancel();
213+
};
214+
hookStartNode.Started += (_, _) => numberOfTimesStartedInvoked++;
215+
216+
using var muninNodeService = new MuninNodeBackgroundService(hookStartNode);
217+
218+
// start service
219+
Assert.That(
220+
async () => await muninNodeService.StartAsync(cancellationToken: ctsStarting.Token),
221+
Throws.InstanceOf<OperationCanceledException>()
222+
);
223+
224+
Assert.That(numberOfTimesStartingInvoked, Is.EqualTo(1));
225+
Assert.That(numberOfTimesStartedInvoked, Is.Zero);
226+
}
227+
135228
private class NullMuninNode : IMuninNode {
136229
public string HostName => "munin-node.localhost";
137230
public EndPoint EndPoint { get; } = new IPEndPoint(IPAddress.Any, 0);
@@ -160,24 +253,12 @@ public async Task StopAsync_NodeDoesNotSupportGracefulShutdown(CancellationToken
160253
);
161254
}
162255

163-
private class HookStopNode : LocalNode {
164-
private sealed class NullPluginProvider : IPluginProvider {
165-
public IReadOnlyCollection<IPlugin> Plugins { get; } = [];
166-
public INodeSessionCallback? SessionCallback => null;
167-
}
168-
169-
public override IPluginProvider PluginProvider { get; } = new NullPluginProvider();
170-
public override string HostName => "munin-node.localhost";
171-
256+
private class HookStopNode : EmptyNode {
172257
public EventHandler? Stopping = null;
173258
public EventHandler? Stopped = null;
174259

175260
public HookStopNode()
176-
: base(
177-
listenerFactory: null,
178-
accessRule: null,
179-
logger: null
180-
)
261+
: base()
181262
{
182263
}
183264

0 commit comments

Comments
 (0)