Decorating a service
When using Avalanche.Service as dependency injection, any handler can participate in the build process of ServiceDescriptors by exposing method Handle<R, T>(IQuery<R, IServiceList<ServiceType>> query) where R : IServiceProducerRequest<ServiceType>.
/// <summary>Handler that decorates loggers.</summary>
public class LoggerDecorator : IHandlerBase
{
/// <summary>Decorate loggers in every producer request</summary>
[Order(9_000_000_000_000_000_000L)] // Participation order
public void Handle<R, T>(IQuery<R, IServiceList<ILogger<T>>> query) where R : IServiceProducerRequest<ILogger<T>>
{
// No loggers
if (query.Response.Value() is not IServiceList<ILogger<T>> list || list.Count == 0) return;
// Decorate each logger
for (int i = 0; i < list.Count; i++)
{
// Get logger
ILogger<T> logger = list.Elements[i];
// Decorate logger
ILogger<T> loggerDecoration = new LoggerDecoration<T>(logger);
// Assign decoration
list.Elements[i] = loggerDecoration;
}
}
}
/// <summary><see cref="ILogger"/> that is decorated.</summary>
public class LoggerDecoration<T> : ILogger<T>
{
/// <summary></summary>
ILogger<T> source;
/// <summary></summary>
public LoggerDecoration(ILogger<T> source)
{
this.source = source;
}
/// <summary></summary>
public IDisposable BeginScope<TState>(TState state) => source.BeginScope<TState>(state);
/// <summary></summary>
public bool IsEnabled(LogLevel logLevel) => source.IsEnabled(logLevel);
/// <summary></summary>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
// Print
string print = formatter(state, exception);
// Debug print
Console.WriteLine($"LoggerDecoration Here: {print}");
// Call source logger
source.Log<TState>(logLevel, eventId, state, exception, formatter);
}
}
The following example uses modified ILogger<T> instances.
// Create service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddLogging()
.AddHandler(new LoggerDecorator())
.AddHandlers(ServiceHandlers.Instance)
.AddTransient<MyRecord>();
// Create service
using var service = serviceCollection.BuildAvalancheServiceProvider(cachePolicy: CachePolicies.Default);
// Get myclass
MyRecord myClass = service.GetRequiredService<MyRecord>();
// Log with decorated logger
myClass.Logger.LogInformation("Hello world");
// "LoggerDecoration Here: Hello world"
/// <summary>Dependency injectible class</summary>
public record MyRecord(ILogger<MyRecord> Logger);
Same example on Asp.Net.
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Host.UseServiceProviderFactory(Avalanche.Service.ServiceProviderFactory.Instance);
builder.Host.ConfigureContainer((HostBuilderContext _, IServiceBuilder sb) =>
{
// Add handlers
sb.AddHandler(new LoggerDecorator());
});
WebApplication app = builder.Build();
app.MapGet("/", ([FromServices] Avalanche.Service.IService service) => Results.Ok("Hello world"));
// LoggerDecoration Here: Executing endpoint 'HTTP: GET /'
// LoggerDecoration Here: Executed endpoint 'HTTP: GET /'
Full Example
Full example
using System;
using System.Threading.Tasks;
using Avalanche.Service;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using static System.Console;
public class di_loggerdecorator
{
public static void Run()
{
{
// <11>
// Create service collection
IServiceCollection serviceCollection = new ServiceCollection()
.AddLogging()
.AddHandler(new LoggerDecorator())
.AddHandlers(ServiceHandlers.Instance)
.AddTransient<MyRecord>();
// Create service
using var service = serviceCollection.BuildAvalancheServiceProvider(cachePolicy: CachePolicies.Default);
// Get myclass
MyRecord myClass = service.GetRequiredService<MyRecord>();
// Log with decorated logger
myClass.Logger.LogInformation("Hello world");
// "LoggerDecoration Here: Hello world"
// </11>
WriteLine(myClass.Logger.GetType());
WriteLine(service);
}
{
// <21>
WebApplicationBuilder builder = WebApplication.CreateBuilder();
builder.Host.UseServiceProviderFactory(Avalanche.Service.ServiceProviderFactory.Instance);
builder.Host.ConfigureContainer((HostBuilderContext _, IServiceBuilder sb) =>
{
// Add handlers
sb.AddHandler(new LoggerDecorator());
});
WebApplication app = builder.Build();
app.MapGet("/", ([FromServices] Avalanche.Service.IService service) => Results.Ok("Hello world"));
// LoggerDecoration Here: Executing endpoint 'HTTP: GET /'
// LoggerDecoration Here: Executed endpoint 'HTTP: GET /'
// </21>
// Shutdown in 5 seconds ...
Task.Run(async () =>
{
await Task.Delay(5000);
app.Services.GetService<IHostApplicationLifetime>()!.StopApplication();
});
app.Run();
}
}
// <99>
/// <summary>Handler that decorates loggers.</summary>
public class LoggerDecorator : IHandlerBase
{
/// <summary>Decorate loggers in every producer request</summary>
[Order(9_000_000_000_000_000_000L)] // Participation order
public void Handle<R, T>(IQuery<R, IServiceList<ILogger<T>>> query) where R : IServiceProducerRequest<ILogger<T>>
{
// No loggers
if (query.Response.Value() is not IServiceList<ILogger<T>> list || list.Count == 0) return;
// Decorate each logger
for (int i = 0; i < list.Count; i++)
{
// Get logger
ILogger<T> logger = list.Elements[i];
// Decorate logger
ILogger<T> loggerDecoration = new LoggerDecoration<T>(logger);
// Assign decoration
list.Elements[i] = loggerDecoration;
}
}
}
/// <summary><see cref="ILogger"/> that is decorated.</summary>
public class LoggerDecoration<T> : ILogger<T>
{
/// <summary></summary>
ILogger<T> source;
/// <summary></summary>
public LoggerDecoration(ILogger<T> source)
{
this.source = source;
}
/// <summary></summary>
public IDisposable BeginScope<TState>(TState state) => source.BeginScope<TState>(state);
/// <summary></summary>
public bool IsEnabled(LogLevel logLevel) => source.IsEnabled(logLevel);
/// <summary></summary>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
// Print
string print = formatter(state, exception);
// Debug print
Console.WriteLine($"LoggerDecoration Here: {print}");
// Call source logger
source.Log<TState>(logLevel, eventId, state, exception, formatter);
}
}
// </99>
// <19>
/// <summary>Dependency injectible class</summary>
public record MyRecord(ILogger<MyRecord> Logger);
// </19>
}