From a662eac727488d0fb7ec6a0289562856ead5d433 Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Mon, 16 Dec 2024 11:43:19 +0300 Subject: [PATCH] Add example --- README.md | 1 + readme/di-tracing-via-serilog.md | 147 ++++++++++++++++++ .../Advanced/DITracingViaSerilogScenario.cs | 116 ++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 readme/di-tracing-via-serilog.md create mode 100644 tests/Pure.DI.UsageTests/Advanced/DITracingViaSerilogScenario.cs diff --git a/README.md b/README.md index 922211b0d..829005625 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ dotnet run - [Exposed generic roots](readme/exposed-generic-roots.md) - [Exposed generic roots with args](readme/exposed-generic-roots-with-args.md) - [Serilog](readme/serilog.md) +- [DI tracing via serilog](readme/di-tracing-via-serilog.md) ### Applications - Console - [Schrödinger's cat](readme/Console.md) diff --git a/readme/di-tracing-via-serilog.md b/readme/di-tracing-via-serilog.md new file mode 100644 index 000000000..9f6fe57de --- /dev/null +++ b/readme/di-tracing-via-serilog.md @@ -0,0 +1,147 @@ +#### DI tracing via serilog + +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Advanced/DITracingViaSerilogScenario.cs) + + +```c# +interface IDependency; + +class Dependency : IDependency; + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +interface ILogger: Serilog.ILogger; + +class Logger(Serilog.ILogger logger) : ILogger +{ + private readonly Serilog.ILogger _logger = + logger.ForContext(typeof(T)); + + public void Write(LogEvent logEvent) => + _logger.Write(logEvent); +} + +partial class Composition: ILogEventSink +{ + private readonly Action? _eventsHandler; + private readonly ILogger? _logger; + + public Composition(Action eventsHandler) + : this() + { + _eventsHandler = eventsHandler; + _logger = new Logger( + new Serilog.LoggerConfiguration() + .WriteTo.Sink(this) + .CreateLogger().ForContext()); + } + + private void Setup() => + DI.Setup(nameof(Composition)) + + .Hint(Hint.OnNewInstance, "On") + .Hint(Hint.OnDependencyInjection, "On") + .Bind().To(_ => _logger!) + .Bind().As(Lifetime.Singleton).To>() + + .Bind().To() + .Bind().To() + .Root(nameof(Root)); + + public void Emit(LogEvent logEvent) => + _eventsHandler?.Invoke(logEvent); + + partial void OnNewInstance(ref T value, object? tag, Lifetime lifetime) => + _logger?.Information("Created [{Value}], tag [{Tag}] as {Lifetime}", value, tag, lifetime); + + private partial T OnDependencyInjection(in T value, object? tag, Lifetime lifetime) + { + _logger?.Information("Injected [{Value}], tag [{Tag}] as {Lifetime}", value, tag, lifetime); + return value; + } +} + +var events = new List(); +var composition = new Composition(logEvent => events.Add(logEvent)); +var service = composition.Root; +``` + +The following partial class will be generated: + +```c# +partial class Composition +{ + private readonly Composition _root; + + [OrdinalAttribute(256)] + public Composition() + { + _root = this; + } + + internal Composition(Composition parentScope) + { + _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + } + + public IService Root + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Dependency transientDependency1 = new Dependency(); + OnNewInstance(ref transientDependency1, null, Lifetime.Transient); + Service transientService0 = new Service(OnDependencyInjection(transientDependency1, null, Lifetime.Transient)); + OnNewInstance(ref transientService0, null, Lifetime.Transient); + return OnDependencyInjection(transientService0, null, Lifetime.Transient); + } + } + + + partial void OnNewInstance(ref T value, object? tag, Lifetime lifetime); + + private partial T OnDependencyInjection(in T value, object? tag, Lifetime lifetime); +} +``` + +Class diagram: + +```mermaid +--- + config: + class: + hideEmptyMembersBox: true +--- +classDiagram + Service --|> IService + Dependency --|> IDependency + Composition ..> Service : IService Root + Service *-- Dependency : IDependency + namespace Pure.DI.UsageTests.Advanced.DITracingViaSerilogScenario { + class Composition { + <> + +IService Root + } + class Dependency { + +Dependency() + } + class IDependency { + <> + } + class IService { + <> + } + class Service { + +Service(IDependency dependency) + } + } +``` + diff --git a/tests/Pure.DI.UsageTests/Advanced/DITracingViaSerilogScenario.cs b/tests/Pure.DI.UsageTests/Advanced/DITracingViaSerilogScenario.cs new file mode 100644 index 000000000..1a57668b0 --- /dev/null +++ b/tests/Pure.DI.UsageTests/Advanced/DITracingViaSerilogScenario.cs @@ -0,0 +1,116 @@ +/* +$v=true +$p=302 +$d=DI tracing via serilog +*/ + +// ReSharper disable ClassNeverInstantiated.Local +// ReSharper disable CheckNamespace +// ReSharper disable UnusedType.Global +// ReSharper disable ArrangeTypeModifiers +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable UnusedVariable + +// ReSharper disable UnusedTypeParameter +// ReSharper disable UnusedMember.Local +// ReSharper disable UnusedMemberInSuper.Global +// ReSharper disable UnusedMember.Global +#pragma warning disable CS9113 // Parameter is unread. +#pragma warning disable CA1859 +namespace Pure.DI.UsageTests.Advanced.DITracingViaSerilogScenario; + +#pragma warning disable CA2263 +using Serilog.Core; +using Serilog.Events; +using Xunit; + +// { +interface IDependency; + +class Dependency : IDependency; + +interface IService +{ + IDependency Dependency { get; } +} + +class Service(IDependency dependency) : IService +{ + public IDependency Dependency { get; } = dependency; +} + +interface ILogger: Serilog.ILogger; + +class Logger(Serilog.ILogger logger) : ILogger +{ + private readonly Serilog.ILogger _logger = + logger.ForContext(typeof(T)); + + public void Write(LogEvent logEvent) => + _logger.Write(logEvent); +} + +partial class Composition: ILogEventSink +{ + private readonly Action? _eventsHandler; + private readonly ILogger? _logger; + + public Composition(Action eventsHandler) + : this() + { + _eventsHandler = eventsHandler; + _logger = new Logger( + new Serilog.LoggerConfiguration() + .WriteTo.Sink(this) + .CreateLogger().ForContext()); + } + + private void Setup() => + DI.Setup(nameof(Composition)) +// } + .Hint(Hint.Resolve, "Off") +// { + .Hint(Hint.OnNewInstance, "On") + .Hint(Hint.OnDependencyInjection, "On") + .Bind().To(_ => _logger!) + .Bind().As(Lifetime.Singleton).To>() + + .Bind().To() + .Bind().To() + .Root(nameof(Root)); + + public void Emit(LogEvent logEvent) => + _eventsHandler?.Invoke(logEvent); + + partial void OnNewInstance(ref T value, object? tag, Lifetime lifetime) => + _logger?.Information("Created [{Value}], tag [{Tag}] as {Lifetime}", value, tag, lifetime); + + private partial T OnDependencyInjection(in T value, object? tag, Lifetime lifetime) + { + _logger?.Information("Injected [{Value}], tag [{Tag}] as {Lifetime}", value, tag, lifetime); + return value; + } +} +// } + +class EventSink(ICollection events) + : ILogEventSink +{ + public void Emit(LogEvent logEvent) => + events.Add(logEvent); +} + +public class Scenario +{ + [Fact] + public void Run() + { +// { + var events = new List(); + var composition = new Composition(logEvent => events.Add(logEvent)); + var service = composition.Root; +// } + events.Count.ShouldBe(4); + composition.SaveClassDiagram(); + } +} \ No newline at end of file