·
31 commits
to main
since this release
Released package
Release notes
The full release notes are available at gist.
Change log
Change log in this release:
- 2025-06-01 update assembly version
- 2025-05-31 add support for registering custom MuninNodeBackgroundService type
- 2025-05-31 mark IMuninNodeBuilder obsolete
- 2025-05-28 override BackgroundService.StartAsync to add logging
- 2025-05-28 expose ILogger
- 2025-05-28 improve logging using with LoggerMessage
- 2025-05-28 add support for graceful shutdown
- 2025-05-01 enable package validation
API changes
API changes in this release:
diff --git a/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-net8.0.apilist.cs b/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-net8.0.apilist.cs
index 387ee95..4d934a8 100644
--- a/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-net8.0.apilist.cs
+++ b/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-net8.0.apilist.cs
@@ -1,43 +1,50 @@
-// Smdn.Net.MuninNode.Hosting.dll (Smdn.Net.MuninNode.Hosting-3.0.0)
+// Smdn.Net.MuninNode.Hosting.dll (Smdn.Net.MuninNode.Hosting-3.1.0)
// Name: Smdn.Net.MuninNode.Hosting
-// AssemblyVersion: 3.0.0.0
-// InformationalVersion: 3.0.0+0830d2fdea4a5b05d99958b5116ff7b474590f2f
+// AssemblyVersion: 3.1.0.0
+// InformationalVersion: 3.1.0+d9b937573b3b1dd41eaf878498bcf5d285c10471
// TargetFramework: .NETCoreApp,Version=v8.0
// Configuration: Release
// Referenced assemblies:
// Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Hosting.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Smdn.Net.MuninNode, Version=2.2.0.0, Culture=neutral
+// Smdn.Net.MuninNode, Version=2.5.0.0, Culture=neutral
// System.Net.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
// System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
#nullable enable annotations
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Smdn.Net.MuninNode;
using Smdn.Net.MuninNode.DependencyInjection;
using Smdn.Net.MuninNode.Hosting;
namespace Smdn.Net.MuninNode.Hosting {
public static class IServiceCollectionExtensions {
public static IServiceCollection AddHostedMuninNodeService(this IServiceCollection services, Action<MuninNodeOptions> configureNode, Action<IMuninNodeBuilder> buildNode) {}
+ public static IServiceCollection AddHostedMuninNodeService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMuninNodeBackgroundService, TMuninNode, TMuninNodeOptions, TMuninNodeBuilder>(this IServiceCollection services, Action<TMuninNodeOptions> configureNode, Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder, Action<TMuninNodeBuilder> buildNode) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNode : class, IMuninNode where TMuninNodeOptions : MuninNodeOptions, new() where TMuninNodeBuilder : MuninNodeBuilder {}
+ public static IServiceCollection AddHostedMuninNodeService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMuninNodeBackgroundService, TMuninNodeBuilder>(this IServiceCollection services, Func<IMuninServiceBuilder, TMuninNodeBuilder> buildMunin) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNodeBuilder : MuninNodeBuilder {}
+ public static IServiceCollection AddHostedMuninNodeService<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMuninNodeBackgroundService, TMuninNodeService, TMuninNodeImplementation, TMuninNodeOptions, TMuninNodeBuilder>(this IServiceCollection services, Action<TMuninNodeOptions> configureNode, Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder, Action<TMuninNodeBuilder> buildNode) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNodeService : class, IMuninNode where TMuninNodeImplementation : class, TMuninNodeService where TMuninNodeOptions : MuninNodeOptions, new() where TMuninNodeBuilder : MuninNodeBuilder {}
}
public class MuninNodeBackgroundService : BackgroundService {
public MuninNodeBackgroundService(IMuninNode node) {}
public MuninNodeBackgroundService(IMuninNode node, ILogger<MuninNodeBackgroundService>? logger) {}
public EndPoint EndPoint { get; }
+ protected ILogger? Logger { get; }
public override void Dispose() {}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {}
+ public override async Task StartAsync(CancellationToken cancellationToken) {}
+ public override async Task StopAsync(CancellationToken cancellationToken) {}
}
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.5.0.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
diff --git a/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-netstandard2.1.apilist.cs b/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-netstandard2.1.apilist.cs
index 3162c84..a325383 100644
--- a/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-netstandard2.1.apilist.cs
+++ b/doc/api-list/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting-netstandard2.1.apilist.cs
@@ -1,42 +1,48 @@
-// Smdn.Net.MuninNode.Hosting.dll (Smdn.Net.MuninNode.Hosting-3.0.0)
+// Smdn.Net.MuninNode.Hosting.dll (Smdn.Net.MuninNode.Hosting-3.1.0)
// Name: Smdn.Net.MuninNode.Hosting
-// AssemblyVersion: 3.0.0.0
-// InformationalVersion: 3.0.0+0830d2fdea4a5b05d99958b5116ff7b474590f2f
+// AssemblyVersion: 3.1.0.0
+// InformationalVersion: 3.1.0+d9b937573b3b1dd41eaf878498bcf5d285c10471
// TargetFramework: .NETStandard,Version=v2.1
// Configuration: Release
// Referenced assemblies:
// Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Hosting.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
// Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
-// Smdn.Net.MuninNode, Version=2.2.0.0, Culture=neutral
+// Smdn.Net.MuninNode, Version=2.5.0.0, Culture=neutral
// netstandard, Version=2.1.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51
#nullable enable annotations
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Smdn.Net.MuninNode;
using Smdn.Net.MuninNode.DependencyInjection;
using Smdn.Net.MuninNode.Hosting;
namespace Smdn.Net.MuninNode.Hosting {
public static class IServiceCollectionExtensions {
public static IServiceCollection AddHostedMuninNodeService(this IServiceCollection services, Action<MuninNodeOptions> configureNode, Action<IMuninNodeBuilder> buildNode) {}
+ public static IServiceCollection AddHostedMuninNodeService<TMuninNodeBackgroundService, TMuninNode, TMuninNodeOptions, TMuninNodeBuilder>(this IServiceCollection services, Action<TMuninNodeOptions> configureNode, Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder, Action<TMuninNodeBuilder> buildNode) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNode : class, IMuninNode where TMuninNodeOptions : MuninNodeOptions, new() where TMuninNodeBuilder : MuninNodeBuilder {}
+ public static IServiceCollection AddHostedMuninNodeService<TMuninNodeBackgroundService, TMuninNodeBuilder>(this IServiceCollection services, Func<IMuninServiceBuilder, TMuninNodeBuilder> buildMunin) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNodeBuilder : MuninNodeBuilder {}
+ public static IServiceCollection AddHostedMuninNodeService<TMuninNodeBackgroundService, TMuninNodeService, TMuninNodeImplementation, TMuninNodeOptions, TMuninNodeBuilder>(this IServiceCollection services, Action<TMuninNodeOptions> configureNode, Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder, Action<TMuninNodeBuilder> buildNode) where TMuninNodeBackgroundService : MuninNodeBackgroundService where TMuninNodeService : class, IMuninNode where TMuninNodeImplementation : class, TMuninNodeService where TMuninNodeOptions : MuninNodeOptions, new() where TMuninNodeBuilder : MuninNodeBuilder {}
}
public class MuninNodeBackgroundService : BackgroundService {
public MuninNodeBackgroundService(IMuninNode node) {}
public MuninNodeBackgroundService(IMuninNode node, ILogger<MuninNodeBackgroundService>? logger) {}
public EndPoint EndPoint { get; }
+ protected ILogger? Logger { get; }
public override void Dispose() {}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {}
+ public override async Task StartAsync(CancellationToken cancellationToken) {}
+ public override async Task StopAsync(CancellationToken cancellationToken) {}
}
}
// API list generated by Smdn.Reflection.ReverseGenerating.ListApi.MSBuild.Tasks v1.5.0.0.
// Smdn.Reflection.ReverseGenerating.ListApi.Core v1.3.1.0 (https://github.com/smdn/Smdn.Reflection.ReverseGenerating)
Full changes
Full changes in this release:
diff --git a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting.csproj b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting.csproj
index f3af407..bf9dc3d 100644
--- a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting.csproj
+++ b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting.csproj
@@ -5,9 +5,9 @@ SPDX-License-Identifier: MIT
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.1;net8.0</TargetFrameworks>
- <VersionPrefix>3.0.0</VersionPrefix>
+ <VersionPrefix>3.1.0</VersionPrefix>
<VersionSuffix></VersionSuffix>
- <!--<PackageValidationBaselineVersion>3.0.0</PackageValidationBaselineVersion>-->
+ <PackageValidationBaselineVersion>3.0.0</PackageValidationBaselineVersion>
<RootNamespace/> <!-- empty the root namespace so that the namespace is determined only by the directory name, for code style rule IDE0030 -->
<Nullable>enable</Nullable>
<NoWarn>CS1591;$(NoWarn)</NoWarn> <!-- CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' -->
@@ -34,7 +34,7 @@ This library uses [Smdn.Net.MuninNode](https://www.nuget.org/packages/Smdn.Net.M
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
- <ProjectOrPackageReference ReferencePackageVersion="[2.2.0,4.0.0)" Include="..\Smdn.Net.MuninNode\Smdn.Net.MuninNode.csproj" />
+ <ProjectOrPackageReference ReferencePackageVersion="[2.5.0,4.0.0)" Include="..\Smdn.Net.MuninNode\Smdn.Net.MuninNode.csproj" />
</ItemGroup>
<Target Name="GenerateReadmeFileContent">
diff --git a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/IServiceCollectionExtensions.cs b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/IServiceCollectionExtensions.cs
index 4a3b85a..fc1893e 100644
--- a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/IServiceCollectionExtensions.cs
+++ b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/IServiceCollectionExtensions.cs
@@ -2,6 +2,9 @@
// SPDX-License-Identifier: MIT
using System;
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_DYNAMICALLYACCESSEDMEMBERSATTRIBUTE
+using System.Diagnostics.CodeAnalysis;
+#endif
using Microsoft.Extensions.DependencyInjection;
@@ -20,8 +23,8 @@ public static class IServiceCollectionExtensions {
/// configure the <c>Munin-Node</c> to be built.
/// </param>
/// <param name="buildNode">
- /// An <see cref="Action{IMuninServiceBuilder}"/> to build <c>Munin-Node</c> using with
- /// the <see cref="IMuninServiceBuilder"/>.
+ /// An <see cref="Action{IMuninNodeBuilder}"/> to build <c>Munin-Node</c> using with
+ /// the <see cref="IMuninNodeBuilder"/>.
/// </param>
/// <returns>The current <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
/// <exception cref="ArgumentNullException">
@@ -29,41 +32,264 @@ public static class IServiceCollectionExtensions {
/// <paramref name="configureNode"/> is <see langword="null"/>, or
/// <paramref name="buildNode"/> is <see langword="null"/>.
/// </exception>
+#pragma warning disable CS0618 // accept MuninNodeBuilder instead of IMuninNodeBuilder
public static IServiceCollection AddHostedMuninNodeService(
this IServiceCollection services,
Action<MuninNodeOptions> configureNode,
Action<IMuninNodeBuilder> buildNode
)
+#pragma warning restore CS0618
+ => AddHostedMuninNodeService<
+ MuninNodeBackgroundService,
+ IMuninNode,
+ IMuninNode,
+ MuninNodeOptions,
+ DefaultMuninNodeBuilder
+ >(
+ services: services ?? throw new ArgumentNullException(nameof(services)),
+ configureNode: configureNode ?? throw new ArgumentNullException(nameof(configureNode)),
+ createNodeBuilder: static (serviceBuilder, serviceKey) => new(serviceBuilder, serviceKey),
+ buildNode: builder => (buildNode ?? throw new ArgumentNullException(nameof(buildNode)))(builder)
+ );
+
+ private class DefaultMuninNodeBuilder(IMuninServiceBuilder serviceBuilder, string serviceKey)
+ : MuninNodeBuilder(serviceBuilder, serviceKey) {
+ }
+
+ /// <summary>
+ /// Add <typeparamref name="TMuninNodeBackgroundService"/>, which runs <typeparamref name="TMuninNode"/> as an
+ /// <see cref="Microsoft.Extensions.Hosting.IHostedService"/>, to <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TMuninNodeBackgroundService">
+ /// The type of <see cref="Microsoft.Extensions.Hosting.IHostedService"/> service to add to the <seealso cref="IServiceCollection"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNode">
+ /// The type of <see cref="IMuninNode"/> service to add to the <seealso cref="IServiceCollection"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeOptions">
+ /// The extended type of <see cref="MuninNodeOptions"/> to configure the <typeparamref name="TMuninNode"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeBuilder">
+ /// The extended type of <see cref="MuninNodeBuilder"/> to build the <typeparamref name="TMuninNode"/>.
+ /// </typeparam>
+ /// <param name="services">
+ /// An <see cref="IServiceCollection"/> that the built <typeparamref name="TMuninNodeBackgroundService"/> and
+ /// <typeparamref name="TMuninNode"/> will be added to.
+ /// </param>
+ /// <param name="configureNode">
+ /// An <see cref="Action{TMuninNodeOptions}"/> to setup <typeparamref name="TMuninNodeOptions"/> to
+ /// configure the <typeparamref name="TMuninNode"/> to be built.
+ /// </param>
+ /// <param name="createNodeBuilder">
+ /// An <see cref="Func{TMuninNodeBuilder}"/> to create <typeparamref name="TMuninNodeBuilder"/> to build
+ /// the <typeparamref name="TMuninNode"/>.
+ /// </param>
+ /// <param name="buildNode">
+ /// An <see cref="Action{TMuninNodeBuilder}"/> to build <typeparamref name="TMuninNode"/> using with
+ /// the <typeparamref name="TMuninNodeBuilder"/>.
+ /// </param>
+ /// <returns>The current <see cref="IMuninNodeBuilder"/> so that additional calls can be chained.</returns>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="services"/> is <see langword="null"/>, or
+ /// <paramref name="configureNode"/> is <see langword="null"/>, or
+ /// <paramref name="createNodeBuilder"/> is <see langword="null"/>, or
+ /// <paramref name="buildNode"/> is <see langword="null"/>.
+ /// </exception>
+#pragma warning disable IDE0055
+ public static
+ IServiceCollection AddHostedMuninNodeService<
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_DYNAMICALLYACCESSEDMEMBERSATTRIBUTE
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+#endif
+ TMuninNodeBackgroundService,
+ TMuninNode,
+ TMuninNodeOptions,
+ TMuninNodeBuilder
+ >(
+ this IServiceCollection services,
+ Action<TMuninNodeOptions> configureNode,
+ Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder,
+ Action<TMuninNodeBuilder> buildNode
+ )
+ where TMuninNodeBackgroundService : MuninNodeBackgroundService
+ where TMuninNode : class, IMuninNode
+ where TMuninNodeOptions : MuninNodeOptions, new()
+ where TMuninNodeBuilder : MuninNodeBuilder
+#pragma warning restore IDE0055
+ => AddHostedMuninNodeService<
+ TMuninNodeBackgroundService,
+ TMuninNode,
+ TMuninNode,
+ TMuninNodeOptions,
+ TMuninNodeBuilder
+ >(
+ services: services ?? throw new ArgumentNullException(nameof(services)),
+ configureNode: configureNode ?? throw new ArgumentNullException(nameof(configureNode)),
+ createNodeBuilder: createNodeBuilder ?? throw new ArgumentNullException(nameof(configureNode)),
+ buildNode: buildNode ?? throw new ArgumentNullException(nameof(buildNode))
+ );
+
+ /// <summary>
+ /// Add <typeparamref name="TMuninNodeBackgroundService"/>, which runs <typeparamref name="TMuninNodeImplementation"/> as an
+ /// <see cref="Microsoft.Extensions.Hosting.IHostedService"/>, to <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TMuninNodeBackgroundService">
+ /// The type of <see cref="Microsoft.Extensions.Hosting.IHostedService"/> service to add to the <seealso cref="IServiceCollection"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeService">
+ /// The type of <see cref="IMuninNode"/> service to add to the <seealso cref="IServiceCollection"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeImplementation">
+ /// The type of <typeparamref name="TMuninNodeService"/> implementation.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeOptions">
+ /// The extended type of <see cref="MuninNodeOptions"/> to configure the <typeparamref name="TMuninNodeImplementation"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeBuilder">
+ /// The extended type of <see cref="MuninNodeBuilder"/> to build the <typeparamref name="TMuninNodeImplementation"/>.
+ /// </typeparam>
+ /// <param name="services">
+ /// An <see cref="IServiceCollection"/> that the built <typeparamref name="TMuninNodeBackgroundService"/> and
+ /// <typeparamref name="TMuninNodeImplementation"/> will be added to.
+ /// </param>
+ /// <param name="configureNode">
+ /// An <see cref="Action{TMuninNodeOptions}"/> to setup <typeparamref name="TMuninNodeOptions"/> to
+ /// configure the <typeparamref name="TMuninNodeImplementation"/> to be built.
+ /// </param>
+ /// <param name="createNodeBuilder">
+ /// An <see cref="Func{TMuninNodeBuilder}"/> to create <typeparamref name="TMuninNodeBuilder"/> to build
+ /// the <typeparamref name="TMuninNodeImplementation"/>.
+ /// </param>
+ /// <param name="buildNode">
+ /// An <see cref="Action{TMuninNodeBuilder}"/> to build <typeparamref name="TMuninNodeImplementation"/> using with
+ /// the <typeparamref name="TMuninNodeBuilder"/>.
+ /// </param>
+ /// <returns>The current <see cref="IMuninNodeBuilder"/> so that additional calls can be chained.</returns>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="services"/> is <see langword="null"/>, or
+ /// <paramref name="configureNode"/> is <see langword="null"/>, or
+ /// <paramref name="createNodeBuilder"/> is <see langword="null"/>, or
+ /// <paramref name="buildNode"/> is <see langword="null"/>.
+ /// </exception>
+#pragma warning disable IDE0055
+ public static
+ IServiceCollection AddHostedMuninNodeService<
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_DYNAMICALLYACCESSEDMEMBERSATTRIBUTE
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+#endif
+ TMuninNodeBackgroundService,
+ TMuninNodeService,
+ TMuninNodeImplementation,
+ TMuninNodeOptions,
+ TMuninNodeBuilder
+ >(
+ this IServiceCollection services,
+ Action<TMuninNodeOptions> configureNode,
+ Func<IMuninServiceBuilder, string, TMuninNodeBuilder> createNodeBuilder,
+ Action<TMuninNodeBuilder> buildNode
+ )
+ where TMuninNodeBackgroundService : MuninNodeBackgroundService
+ where TMuninNodeService : class, IMuninNode
+ where TMuninNodeImplementation : class, TMuninNodeService
+ where TMuninNodeOptions : MuninNodeOptions, new()
+ where TMuninNodeBuilder : MuninNodeBuilder
+#pragma warning restore IDE0055
{
if (services is null)
throw new ArgumentNullException(nameof(services));
if (configureNode is null)
throw new ArgumentNullException(nameof(configureNode));
+ if (createNodeBuilder is null)
+ throw new ArgumentNullException(nameof(createNodeBuilder));
if (buildNode is null)
throw new ArgumentNullException(nameof(buildNode));
- return services.AddMunin(
- muninBuilder => {
- var muninNodeBuilder = muninBuilder.AddNode(configureNode);
+ return AddHostedMuninNodeService<TMuninNodeBackgroundService, TMuninNodeBuilder>(
+ services: services,
+ buildMunin: muninBuilder => {
+ var muninNodeBuilder = muninBuilder.AddNode<
+ TMuninNodeService,
+ TMuninNodeImplementation,
+ TMuninNodeOptions,
+ TMuninNodeBuilder
+ >(
+ configureNode,
+ createNodeBuilder
+ );
buildNode(muninNodeBuilder);
- muninNodeBuilder.Services.AddHostedService<MuninNodeBackgroundService>();
+ return muninNodeBuilder;
+ }
+ );
+ }
- // TODO: support keyed service
-#if false
- var muninNodeBuilder = muninBuilder.AddKeyedNode(configureNode);
+ /// <summary>
+ /// Add <typeparamref name="TMuninNodeBackgroundService"/>, which runs <c>Munin-Node</c> as an
+ /// <see cref="Microsoft.Extensions.Hosting.IHostedService"/>, to <see cref="IServiceCollection"/>.
+ /// </summary>
+ /// <typeparam name="TMuninNodeBackgroundService">
+ /// The type of <see cref="Microsoft.Extensions.Hosting.IHostedService"/> service to add to the <seealso cref="IServiceCollection"/>.
+ /// </typeparam>
+ /// <typeparam name="TMuninNodeBuilder">
+ /// The extended type of <see cref="MuninNodeBuilder"/> to build the <c>Munin-Node</c>.
+ /// </typeparam>
+ /// <param name="services">
+ /// An <see cref="IServiceCollection"/> that the built <typeparamref name="TMuninNodeBackgroundService"/> and
+ /// <c>Munin-Node</c> will be added to.
+ /// </param>
+ /// <param name="buildMunin">
+ /// A <see cref="Func{IMuninServiceBuilder, TMuninNodeBuilder}"/> that registers at least one <see cref="IMuninNode"/> to
+ /// <paramref name="services"/> and returns <typeparamref name="TMuninNodeBuilder"/>, which builds the <see cref="IMuninNode"/>
+ /// to be registered.
+ /// </param>
+ /// <returns>The current <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>
+ /// <exception cref="ArgumentNullException">
+ /// <paramref name="services"/> is <see langword="null"/>, or
+ /// <paramref name="buildMunin"/> is <see langword="null"/>.
+ /// </exception>
+ /// <remarks>
+ /// In future implementations, <typeparamref name="TMuninNodeBackgroundService"/> to be registered by
+ /// this method will use the same key as the <see cref="MuninNodeBuilder.ServiceKey"/> of the
+ /// <typeparamref name="TMuninNodeBuilder"/> returned by the <paramref name="buildMunin"/>.
+ /// </remarks>
+#pragma warning disable IDE0055
+ public static
+ IServiceCollection AddHostedMuninNodeService<
+#if SYSTEM_DIAGNOSTICS_CODEANALYSIS_DYNAMICALLYACCESSEDMEMBERSATTRIBUTE
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+#endif
+ TMuninNodeBackgroundService,
+ TMuninNodeBuilder
+ >(
+ this IServiceCollection services,
+ Func<IMuninServiceBuilder, TMuninNodeBuilder> buildMunin
+ )
+ where TMuninNodeBackgroundService : MuninNodeBackgroundService
+ where TMuninNodeBuilder : MuninNodeBuilder
+#pragma warning restore IDE0055
+ {
+ if (services is null)
+ throw new ArgumentNullException(nameof(services));
+ if (buildMunin is null)
+ throw new ArgumentNullException(nameof(buildMunin));
- buildNode(muninNodeBuilder);
+ return services.AddMunin(
+ muninBuilder => {
+ var muninNodeBuilder = buildMunin(muninBuilder);
+
+ muninNodeBuilder.Services.AddHostedService<TMuninNodeBackgroundService>();
+ // TODO: support keyed service
+#if false
// these code does not work currently
// https://github.com/dotnet/runtime/issues/99085
- muninNodeBuilder.Services.AddHostedService<MuninNodeBackgroundService>(
+ muninNodeBuilder.Services.AddHostedService<TMuninNodeBackgroundService>(
serviceKey: muninNodeBuilder.ServiceKey
);
muninNodeBuilder.Services.TryAddEnumerable(
- ServiceDescriptor.KeyedSingleton<IHostedService, MuninNodeBackgroundService>(
+ ServiceDescriptor.KeyedSingleton<IHostedService, TMuninNodeBackgroundService>(
serviceKey: muninNodeBuilder.ServiceKey
)
);
diff --git a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs
index 7243909..459e79f 100644
--- a/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs
+++ b/src/Smdn.Net.MuninNode.Hosting/Smdn.Net.MuninNode.Hosting/MuninNodeBackgroundService.cs
@@ -1,8 +1,5 @@
// SPDX-FileCopyrightText: 2025 smdn <[email protected]>
// SPDX-License-Identifier: MIT
-
-#pragma warning disable CA1848 // For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
-
using System;
using System.Net;
using System.Threading;
@@ -14,12 +11,34 @@ using Microsoft.Extensions.Logging;
namespace Smdn.Net.MuninNode.Hosting;
public class MuninNodeBackgroundService : BackgroundService {
+ private static readonly Action<ILogger, string, Exception?> LogStarting = LoggerMessage.Define<string>(
+ LogLevel.Information,
+ eventId: default, // TODO
+ formatString: "Munin node '{HostName}' starting."
+ );
+ private static readonly Action<ILogger, string, Exception?> LogStarted = LoggerMessage.Define<string>(
+ LogLevel.Information,
+ eventId: default, // TODO
+ formatString: "Munin node '{HostName}' started."
+ );
+ private static readonly Action<ILogger, string, Exception?> LogStopping = LoggerMessage.Define<string>(
+ LogLevel.Information,
+ eventId: default, // TODO
+ formatString: "Munin node '{HostName}' stopping."
+ );
+ private static readonly Action<ILogger, string, Exception?> LogStopped = LoggerMessage.Define<string>(
+ LogLevel.Information,
+ eventId: default, // TODO
+ formatString: "Munin node '{HostName}' stopped."
+ );
+
private IMuninNode node;
- private readonly ILogger<MuninNodeBackgroundService>? logger;
/// <inheritdoc cref="IMuninNode.EndPoint"/>
public EndPoint EndPoint => (node ?? throw new ObjectDisposedException(GetType().FullName)).EndPoint;
+ protected ILogger? Logger { get; }
+
#if false
// TODO: support ServiceKey
// this code does not work currently
@@ -30,7 +49,7 @@ public class MuninNodeBackgroundService : BackgroundService {
)
{
this.node = serviceProvider.GetRequiredKeyedService<IMuninNode>(serviceKey);
- this.logger = serviceProvider.GetService<ILoggerFactory>()?.CreateLogger<MuninNodeBackgroundService>();
+ Logger = serviceProvider.GetService<ILoggerFactory>()?.CreateLogger<MuninNodeBackgroundService>();
}
#endif
@@ -50,7 +69,7 @@ public class MuninNodeBackgroundService : BackgroundService {
)
{
this.node = node ?? throw new ArgumentNullException(nameof(node));
- this.logger = logger;
+ Logger = logger;
}
public override void Dispose()
@@ -65,16 +84,48 @@ public class MuninNodeBackgroundService : BackgroundService {
GC.SuppressFinalize(this);
}
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ public override async Task StartAsync(CancellationToken cancellationToken)
{
if (node is null)
throw new ObjectDisposedException(GetType().FullName);
- logger?.LogInformation("Munin node '{HostName}' starting.", node.HostName);
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (Logger is not null)
+ LogStarting(Logger, node.HostName, null);
+
+ await base.StartAsync(cancellationToken).ConfigureAwait(false);
+
+ if (Logger is not null)
+ LogStarted(Logger, node.HostName, null);
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ if (node is null)
+ throw new ObjectDisposedException(GetType().FullName);
await node.RunAsync(stoppingToken).ConfigureAwait(false);
+ }
+
+ public override async Task StopAsync(CancellationToken cancellationToken)
+ {
+ if (node is null)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ if (Logger is not null)
+ LogStopping(Logger, node.HostName, null);
+
+ await base.StopAsync(cancellationToken).ConfigureAwait(false);
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // attempt graceful shutdown if possible
+ if (node is NodeBase stoppableNode)
+ await stoppableNode.StopAsync(cancellationToken).ConfigureAwait(false);
- logger?.LogInformation("Munin node '{HostName}' stopped.", node.HostName);
+ if (Logger is not null)
+ LogStopped(Logger, node.HostName, null);
if (node is IDisposable disposableNode)
disposableNode.Dispose();
Notes
What's Changed
Notable changes
- Introduce protocol abstraction interfaces by @smdn in #22
- Implement 'dirtyconfig' protocol extension by @smdn in #23
- Implement 'multigraph' protocol extension by @smdn in #25
- Improve DI-related APIs to support custom types by @smdn in #27
Full Changelog: releases/Smdn.Net.MuninNode.Hosting-3.0.0...releases/Smdn.Net.MuninNode.Hosting-3.1.0