From 84b5b6ab734182439a3e638a787b44eff8168350 Mon Sep 17 00:00:00 2001 From: Eben Date: Thu, 21 Mar 2024 15:44:56 +0200 Subject: [PATCH] - initial commit --- .gitignore | 114 +++++++++++++ .media/logo.png | Bin 0 -> 2355 bytes LICENSE | 27 ++++ README.md | 25 +++ Shuttle.Recall.Logging.sln | 31 ++++ .../.package/AssemblyInfo.cs.template | 22 +++ .../Shuttle.NuGetPackager.MSBuild.dll | Bin 0 -> 18432 bytes .../.package/Shuttle.NuGetPackager.targets | 12 ++ .../.package/package.msbuild | 104 ++++++++++++ .../.package/package.nuspec | 27 ++++ .../.package/package.nuspec.template | 27 ++++ .../EventProcessingPipelineLogger.cs | 57 +++++++ .../EventProcessingPipelineObserver.cs | 151 ++++++++++++++++++ .../IRecallLoggingConfiguration.cs | 10 ++ Shuttle.Recall.Logging/PipelineObserver.cs | 83 ++++++++++ .../Properties/AssemblyInfo.cs | 22 +++ .../RecallLoggingBuilder.cs | 26 +++ .../RecallLoggingConfiguration.cs | 60 +++++++ .../RecallLoggingOptions.cs | 11 ++ .../RecallLoggingOptionsExtensions.cs | 48 ++++++ Shuttle.Recall.Logging/Resources.Designer.cs | 72 +++++++++ Shuttle.Recall.Logging/Resources.resx | 123 ++++++++++++++ .../ServiceCollectionExtensions.cs | 32 ++++ .../Shuttle.Recall.Logging.csproj | 37 +++++ Shuttle.Recall.Logging/ThreadingLogger.cs | 60 +++++++ Shuttle.Recall.Logging/ThreadingObserver.cs | 112 +++++++++++++ 26 files changed, 1293 insertions(+) create mode 100644 .gitignore create mode 100644 .media/logo.png create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Shuttle.Recall.Logging.sln create mode 100644 Shuttle.Recall.Logging/.package/AssemblyInfo.cs.template create mode 100644 Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.MSBuild.dll create mode 100644 Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.targets create mode 100644 Shuttle.Recall.Logging/.package/package.msbuild create mode 100644 Shuttle.Recall.Logging/.package/package.nuspec create mode 100644 Shuttle.Recall.Logging/.package/package.nuspec.template create mode 100644 Shuttle.Recall.Logging/EventProcessingPipelineLogger.cs create mode 100644 Shuttle.Recall.Logging/EventProcessingPipelineObserver.cs create mode 100644 Shuttle.Recall.Logging/IRecallLoggingConfiguration.cs create mode 100644 Shuttle.Recall.Logging/PipelineObserver.cs create mode 100644 Shuttle.Recall.Logging/Properties/AssemblyInfo.cs create mode 100644 Shuttle.Recall.Logging/RecallLoggingBuilder.cs create mode 100644 Shuttle.Recall.Logging/RecallLoggingConfiguration.cs create mode 100644 Shuttle.Recall.Logging/RecallLoggingOptions.cs create mode 100644 Shuttle.Recall.Logging/RecallLoggingOptionsExtensions.cs create mode 100644 Shuttle.Recall.Logging/Resources.Designer.cs create mode 100644 Shuttle.Recall.Logging/Resources.resx create mode 100644 Shuttle.Recall.Logging/ServiceCollectionExtensions.cs create mode 100644 Shuttle.Recall.Logging/Shuttle.Recall.Logging.csproj create mode 100644 Shuttle.Recall.Logging/ThreadingLogger.cs create mode 100644 Shuttle.Recall.Logging/ThreadingObserver.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9990a5e --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +.vs + +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ +[Dd]eployment + +# mstest test results +TestResults + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user +*.DotSettings + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +packages + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +[Bb]in +[Oo]bj +sql +TestResults +[Tt]est[Rr]esult* +*.Cache +ClientBin +[Ss]tyle[Cc]op.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML diff --git a/.media/logo.png b/.media/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3af388d9b701e0d71273ba6a33a04c949cf6d3ac GIT binary patch literal 2355 zcmV-33C#A1P)sKO~ zOp+kJs3k=p0uYD*1R?-|2tXhL5X20{He{me?nF?JzLHtQoEON~Hz6qk2oM)CCn^E- zW3G@UAU?R7xsZ7#vng{C^I8!=JTR15&OEu=-%6RYn72tEAlkqSo}A@lIC^c0)Bw># z|12-RI*?|d2q0SM!h9=_^boga*v<9@-nnJXHh`9*kz5?xDR#%rPQ> zaNuI{XGU)@vzrLOemv*X?@FjzARz*<7g*%!?xX##>KP&cOYv|4p7CguYJn&TpcC_w z0z9X4fu17*$P@Pz@LBhY0PF(h?E4n*d2k*%FA4=06@P#M%sP3bU&ma@{7L=4IkO|P zjpqF&>X{=&0Cv=3{WQ~l&3uZvgn1~#OT`lKlbYlG>y)h{(gXyH*35Pq>87b6;wKqi zK5&>E-@qQsw>9jc2{>Q+0Ko$lx-j1mF`e_HU$-&crcvs74yp5Vb`B!3+WGX{77T?Bl|I zPtw~lTuLF{I}m08)FJ%^ph?Vg^RV5Doatbl@l-z>-n zI+9030Ks6Z1$o#K<^l}60M=QMhxHpFt6T zON?S(5#pgLqKQ2(0&oZT@{geK;{oQ4B7ppW-8K^_IsqA9A-!EpA^?RaDCB$YGIV4| zi2(F~iIh7idKhBk(9lK!JV@?+^lb)40}qJ+vIEur0E#Zq8Eh^Bs2Mn$r!>L>s6*bI zV-!gN6i58)KML$WtZh@F+k*B!c9P&Zw2>SS-p%C1|HHn(ed;=vbnB}o2s3!tN{;o1 zA|LXCPA;OBd?5lzM=y`Z{eLkl$O#R*j2!1p-GEU=N{l!m1T&F{A>7czy!8|T{2yL< z_N-t|C1(NYcb7Kc=l7lBL~`N*_EGJ(7t7I?lM|F$`Tz>>>@i(KuTU=uK7x}BUVbJZ z+$;{C#v#-QIkVKz9zx4C5CNocHn~5Taf2FOp_-nn+Iv5-K-E?V7ypAx8}-Q&&mmg` z(86W=|0+4>1eOJm=h>>ZdC}$lEkyt+oRMexJ?g86H*Szy#4(D%%w=EI>C6YsL;&sz zW4)?^%g7#-GcQw}MqM-(0i>Y2R9d0h`qg9~zEvVvsG0sG83v}00&?Lqaw2$nQRO#L zwN3-o_rRnO_V{f#w7W~?aV9x|sInm;0!SfOjGbj3ynF)B-kjxnF2Ju~sv0il)9J#r?|4-cxb;oIK@$U1%~I2K5r9HXv3@Yj zTkg{4am)|MIW5dB&}7Up5CLkEVKrRaCIUzyQ=lbGy-qQ(`GQ7AOb1w`ddc8^)FQ8G z{^r;UV3VrutGD~Z4eY@m|2_}%cPE)GR*g8OKY&i}MiD>?+to|2UiI%au=ltRc{EwQ z+kcE8eW1`4cpv8<`ytgfiXSheZJCI68y&n4ulS z`}bo%9-Ksa8`{8cODZvt2p}EH)i`~vp`9*1=CP~c{pbgbAt$Vi6|l)8XbX9>7tqgT z)|Tp{sWPkuNNRx0Fm>jD+jT=9^M`W;>w54TlOh0rP~vl~RzjBV_m8^({QkPC46A<; zfX_HU=@Uv`0}D8nA^>}V^LuvqXn!YJvWh1Hs2Q7mw7n^6g%w2stO==Y5r9Wn;iK(U zA^`jG4f%7J6x#pQA^`gVU;V{C*j++)98)I((7;R|Y?ei_prR@O)BxK(w1=VPLJ>fC z0B4d}9@;C9V)Y+o0RZqR^x`}pO#M!YY9U2g0NASau?Kb*MX~oE2LS+2lQTW+7Fbvs z9pwUwx&YvNH{0NT1e~Z40fZaRxa(~0G>R7{Nl63{UEt7$OaUITTX%Te3L}mJK#ajblI(u zW-W>-zChHD9;yt+$F&gREC2vgy+v^@fJ8ExBtd*pONu}QAP@lvL;wO2fItKwh#CG9 ZU;xunZ%YIwMr{B9002ovPDHLkV1jGgB8mV2 literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..21736fc --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2024, Eben Roux +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + + Neither the name of the Shuttle organization nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4fd7765 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Recall Logging + +``` +PM> Install-Package Shuttle.Recall.Logging +``` + +## Configuration + +```c# +services.AddRecallLogging(); // all logging options enabled +``` + +Specific logging options may be specified: + +```c# +services.AddRecallLogging(builder => +{ + builder.Options.PipelineTypes = new List { "pieline-type-name" }; + builder.Options.PipelineEventTypes = new List { "pieline-event-type-name" }; + builder.Options.AddPipelineType(); + builder.Options.AddPipelineType(pipelineType); + builder.Options.AddPipelineEventType(); + builder.Options.AddPipelineEventType(pipelineEventType); +}); +``` \ No newline at end of file diff --git a/Shuttle.Recall.Logging.sln b/Shuttle.Recall.Logging.sln new file mode 100644 index 0000000..68f4e7a --- /dev/null +++ b/Shuttle.Recall.Logging.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shuttle.Recall.Logging", "Shuttle.Recall.Logging\Shuttle.Recall.Logging.csproj", "{F24CBCDF-F116-4005-9AD5-E58E50B62A27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3B2973EF-D99B-40BA-8534-36CB21D6E1DF}" + ProjectSection(SolutionItems) = preProject + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F24CBCDF-F116-4005-9AD5-E58E50B62A27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F24CBCDF-F116-4005-9AD5-E58E50B62A27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F24CBCDF-F116-4005-9AD5-E58E50B62A27}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F24CBCDF-F116-4005-9AD5-E58E50B62A27}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6DD7470E-DB82-45D5-A1F8-4C63FD8377CC} + EndGlobalSection +EndGlobal diff --git a/Shuttle.Recall.Logging/.package/AssemblyInfo.cs.template b/Shuttle.Recall.Logging/.package/AssemblyInfo.cs.template new file mode 100644 index 0000000..15b6466 --- /dev/null +++ b/Shuttle.Recall.Logging/.package/AssemblyInfo.cs.template @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +#if NETFRAMEWORK +[assembly: AssemblyTitle(".NET Framework")] +#endif + +#if NETCOREAPP +[assembly: AssemblyTitle(".NET Core")] +#endif + +#if NETSTANDARD +[assembly: AssemblyTitle(".NET Standard")] +#endif + +[assembly: AssemblyVersion("#{SemanticVersionCore}#.0")] +[assembly: AssemblyCopyright("Copyright (c) #{Year}#, Eben Roux")] +[assembly: AssemblyProduct("Shuttle.Recall.Logging")] +[assembly: AssemblyCompany("Eben Roux")] +[assembly: AssemblyConfiguration("Release")] +[assembly: AssemblyInformationalVersion("#{SemanticVersion}#")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.MSBuild.dll b/Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.MSBuild.dll new file mode 100644 index 0000000000000000000000000000000000000000..d746a0e61d42cc2d8400b23a40bef4c294fd86a8 GIT binary patch literal 18432 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 literal 0 HcmV?d00001 diff --git a/Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.targets b/Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.targets new file mode 100644 index 0000000..d714aa8 --- /dev/null +++ b/Shuttle.Recall.Logging/.package/Shuttle.NuGetPackager.targets @@ -0,0 +1,12 @@ + + + + Shuttle.NuGetPackager.MSBuild.dll + + + + + + + + diff --git a/Shuttle.Recall.Logging/.package/package.msbuild b/Shuttle.Recall.Logging/.package/package.msbuild new file mode 100644 index 0000000..4c7d4ac --- /dev/null +++ b/Shuttle.Recall.Logging/.package/package.msbuild @@ -0,0 +1,104 @@ + + + Shuttle.Recall.Logging + https://www.nuget.org + Release + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Shuttle.Recall.Logging/.package/package.nuspec b/Shuttle.Recall.Logging/.package/package.nuspec new file mode 100644 index 0000000..803b681 --- /dev/null +++ b/Shuttle.Recall.Logging/.package/package.nuspec @@ -0,0 +1,27 @@ + + + + + Shuttle.Recall.Logging + 17.0.0 + Eben Roux + Eben Roux + BSD-3-Clause + false + images\logo.png + docs\README.md + + https://github.com/Shuttle/Shuttle.Recall.Logging + Provides non-intrusive logging for Shuttle.Recall components. + Copyright (c) 2024, Eben Roux + + + + + + + + + + + diff --git a/Shuttle.Recall.Logging/.package/package.nuspec.template b/Shuttle.Recall.Logging/.package/package.nuspec.template new file mode 100644 index 0000000..fc801aa --- /dev/null +++ b/Shuttle.Recall.Logging/.package/package.nuspec.template @@ -0,0 +1,27 @@ + + + + + Shuttle.Recall.Logging + #{SemanticVersion}# + Eben Roux + Eben Roux + BSD-3-Clause + false + images\logo.png + docs\README.md + + https://github.com/Shuttle/Shuttle.Recall.Logging + Provides non-intrusive logging for Shuttle.Recall components. + Copyright (c) #{Year}#, Eben Roux + + +#{Dependencies}# + + + + + + + + diff --git a/Shuttle.Recall.Logging/EventProcessingPipelineLogger.cs b/Shuttle.Recall.Logging/EventProcessingPipelineLogger.cs new file mode 100644 index 0000000..7fe24e1 --- /dev/null +++ b/Shuttle.Recall.Logging/EventProcessingPipelineLogger.cs @@ -0,0 +1,57 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Shuttle.Core.Contract; +using Shuttle.Core.Pipelines; + +namespace Shuttle.Recall.Logging +{ + public class EventProcessingPipelineLogger : IHostedService + { + private readonly Type _pipelineType = typeof(EventProcessingPipeline); + private readonly ILogger _logger; + private readonly IPipelineFactory _pipelineFactory; + private readonly IRecallLoggingConfiguration _recallLoggingConfiguration; + + public EventProcessingPipelineLogger(ILogger logger, IRecallLoggingConfiguration recallLoggingConfiguration, IPipelineFactory pipelineFactory) + { + _logger = Guard.AgainstNull(logger, nameof(logger)); + _recallLoggingConfiguration = Guard.AgainstNull(recallLoggingConfiguration, nameof(recallLoggingConfiguration)); + _pipelineFactory = Guard.AgainstNull(pipelineFactory, nameof(pipelineFactory)); + + if (_recallLoggingConfiguration.ShouldLogPipelineType(_pipelineType)) + { + _pipelineFactory.PipelineCreated += OnPipelineCreated; + + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await Task.CompletedTask; + } + + private void OnPipelineCreated(object sender, PipelineEventArgs args) + { + if (args.Pipeline.GetType() != _pipelineType) + { + return; + } + + args.Pipeline.RegisterObserver(new EventProcessingPipelineObserver(_logger, _recallLoggingConfiguration)); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (_recallLoggingConfiguration.ShouldLogPipelineType(_pipelineType)) + { + _pipelineFactory.PipelineCreated -= OnPipelineCreated; + + } + + await Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/EventProcessingPipelineObserver.cs b/Shuttle.Recall.Logging/EventProcessingPipelineObserver.cs new file mode 100644 index 0000000..ec75a93 --- /dev/null +++ b/Shuttle.Recall.Logging/EventProcessingPipelineObserver.cs @@ -0,0 +1,151 @@ +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Shuttle.Core.Contract; +using Shuttle.Core.Pipelines; +using Shuttle.Core.PipelineTransaction; + +namespace Shuttle.Recall.Logging +{ + public class EventProcessingPipelineObserver : PipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver, + IPipelineObserver + { + public EventProcessingPipelineObserver(ILogger logger, IRecallLoggingConfiguration recallLoggingConfiguration) + : base(logger, recallLoggingConfiguration) + { + } + + public void Execute(OnStartTransactionScope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnStartTransactionScope pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAfterStartTransactionScope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterStartTransactionScope pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnGetProjectionEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnGetProjectionEvent pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAfterGetProjectionEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterGetProjectionEvent pipelineEvent) + { + Guard.AgainstNull(pipelineEvent, nameof(pipelineEvent)); + + await Trace(pipelineEvent, $"working = {pipelineEvent.Pipeline.State.GetWorking()} / has event = {pipelineEvent.Pipeline.State.GetProjectionEvent() != null}"); + } + + public void Execute(OnGetProjectionEventEnvelope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnGetProjectionEventEnvelope pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAfterGetProjectionEventEnvelope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterGetProjectionEventEnvelope pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnProcessEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnProcessEvent pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAfterProcessEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterProcessEvent pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAcknowledgeEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAcknowledgeEvent pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnAfterAcknowledgeEvent pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterAcknowledgeEvent pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnCompleteTransactionScope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnCompleteTransactionScope pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnDisposeTransactionScope pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnDisposeTransactionScope pipelineEvent) + { + await Trace(pipelineEvent); + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/IRecallLoggingConfiguration.cs b/Shuttle.Recall.Logging/IRecallLoggingConfiguration.cs new file mode 100644 index 0000000..826d0f4 --- /dev/null +++ b/Shuttle.Recall.Logging/IRecallLoggingConfiguration.cs @@ -0,0 +1,10 @@ +using System; + +namespace Shuttle.Recall.Logging +{ + public interface IRecallLoggingConfiguration + { + bool ShouldLogPipelineType(Type pipelineType); + bool ShouldLogPipelineEventType(Type pipelineEventType); + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/PipelineObserver.cs b/Shuttle.Recall.Logging/PipelineObserver.cs new file mode 100644 index 0000000..dd7a1a2 --- /dev/null +++ b/Shuttle.Recall.Logging/PipelineObserver.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Shuttle.Core.Contract; +using Shuttle.Core.Pipelines; +using Shuttle.Core.Reflection; + +namespace Shuttle.Recall.Logging +{ + public abstract class PipelineObserver : + IPipelineObserver, + IPipelineObserver, + IPipelineObserver + { + private readonly ILogger _logger; + private readonly IRecallLoggingConfiguration _recallLoggingConfiguration; + + private readonly Dictionary _eventCounts = new Dictionary(); + + protected PipelineObserver(ILogger logger, IRecallLoggingConfiguration recallLoggingConfiguration) + { + Guard.AgainstNull(logger, nameof(logger)); + Guard.AgainstNull(recallLoggingConfiguration, nameof(recallLoggingConfiguration)); + + _logger = logger; + _recallLoggingConfiguration = recallLoggingConfiguration; + } + + protected async Task Trace(IPipelineEvent pipelineEvent, string message = "") + { + Guard.AgainstNull(pipelineEvent, nameof(pipelineEvent)); + + var type = pipelineEvent.GetType(); + + if (!_recallLoggingConfiguration.ShouldLogPipelineEventType(type)) + { + return; + } + + if (!_eventCounts.ContainsKey(type)) + { + _eventCounts.Add(type, 0); + } + + _eventCounts[type] += 1; + + _logger.LogTrace($"[{type.Name} (thread {System.Threading.Thread.CurrentThread.ManagedThreadId}) / {_eventCounts[type]}] : pipeline = {pipelineEvent.Pipeline.GetType().FullName}{(string.IsNullOrEmpty(message) ? string.Empty : $" / {message}")}"); + + await Task.CompletedTask; + } + + public void Execute(OnAbortPipeline pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAbortPipeline pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnPipelineStarting pipelineEvent) + { + Trace(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnPipelineStarting pipelineEvent) + { + await Trace(pipelineEvent); + } + + public void Execute(OnPipelineException pipelineEvent) + { + ExecuteAsync(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnPipelineException pipelineEvent) + { + await Trace(pipelineEvent, $"exception = '{pipelineEvent.Pipeline.Exception?.AllMessages()}'"); + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/Properties/AssemblyInfo.cs b/Shuttle.Recall.Logging/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3db87db --- /dev/null +++ b/Shuttle.Recall.Logging/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +#if NETFRAMEWORK +[assembly: AssemblyTitle(".NET Framework")] +#endif + +#if NETCOREAPP +[assembly: AssemblyTitle(".NET Core")] +#endif + +#if NETSTANDARD +[assembly: AssemblyTitle(".NET Standard")] +#endif + +[assembly: AssemblyVersion("17.0.0.0")] +[assembly: AssemblyCopyright("Copyright (c) 2024, Eben Roux")] +[assembly: AssemblyProduct("Shuttle.Recall.Logging")] +[assembly: AssemblyCompany("Eben Roux")] +[assembly: AssemblyConfiguration("Release")] +[assembly: AssemblyInformationalVersion("17.0.0")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Shuttle.Recall.Logging/RecallLoggingBuilder.cs b/Shuttle.Recall.Logging/RecallLoggingBuilder.cs new file mode 100644 index 0000000..bfee332 --- /dev/null +++ b/Shuttle.Recall.Logging/RecallLoggingBuilder.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Shuttle.Core.Contract; + +namespace Shuttle.Recall.Logging +{ + public class RecallLoggingBuilder + { + private RecallLoggingOptions _recallLoggingOptions = new RecallLoggingOptions(); + + public RecallLoggingOptions Options + { + get => _recallLoggingOptions; + set => _recallLoggingOptions = value ?? throw new ArgumentNullException(nameof(value)); + } + + public IServiceCollection Services { get; } + + public RecallLoggingBuilder(IServiceCollection services) + { + Guard.AgainstNull(services, nameof(services)); + + Services = services; + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/RecallLoggingConfiguration.cs b/Shuttle.Recall.Logging/RecallLoggingConfiguration.cs new file mode 100644 index 0000000..fc724b4 --- /dev/null +++ b/Shuttle.Recall.Logging/RecallLoggingConfiguration.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Shuttle.Core.Contract; + +namespace Shuttle.Recall.Logging +{ + public class RecallLoggingConfiguration : IRecallLoggingConfiguration + { + private readonly List _pipelineTypes = new List(); + private readonly List _pipelineEventTypes = new List(); + + public RecallLoggingConfiguration(IOptions recallLoggingOptions, ILogger logger) + { + Guard.AgainstNull(recallLoggingOptions, nameof(recallLoggingOptions)); + Guard.AgainstNull(recallLoggingOptions.Value, nameof(recallLoggingOptions.Value)); + Guard.AgainstNull(logger, nameof(logger)); + + foreach (var pipelineType in recallLoggingOptions.Value.PipelineTypes) + { + try + { + _pipelineTypes.Add(Type.GetType(pipelineType)); + } + catch (Exception ex) + { + logger.LogError(ex.Message); + } + } + + foreach (var pipelineEventType in recallLoggingOptions.Value.PipelineEventTypes) + { + try + { + _pipelineEventTypes.Add(Type.GetType(pipelineEventType)); + } + catch (Exception ex) + { + logger.LogError(ex.Message); + } + } + } + + public bool ShouldLogPipelineType(Type pipelineType) + { + Guard.AgainstNull(pipelineType, nameof(pipelineType)); + + return !_pipelineTypes.Any() || _pipelineTypes.Contains(pipelineType); + } + + public bool ShouldLogPipelineEventType(Type pipelineEventType) + { + Guard.AgainstNull(pipelineEventType, nameof(pipelineEventType)); + + return !_pipelineEventTypes.Any() || _pipelineEventTypes.Contains(pipelineEventType); + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/RecallLoggingOptions.cs b/Shuttle.Recall.Logging/RecallLoggingOptions.cs new file mode 100644 index 0000000..49c72ef --- /dev/null +++ b/Shuttle.Recall.Logging/RecallLoggingOptions.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Shuttle.Recall.Logging +{ + public class RecallLoggingOptions + { + public List PipelineEventTypes { get; set; } = new List(); + public List PipelineTypes { get; set; } = new List(); + public bool Threading { get; set; } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/RecallLoggingOptionsExtensions.cs b/Shuttle.Recall.Logging/RecallLoggingOptionsExtensions.cs new file mode 100644 index 0000000..1b1b12f --- /dev/null +++ b/Shuttle.Recall.Logging/RecallLoggingOptionsExtensions.cs @@ -0,0 +1,48 @@ +using System; +using Shuttle.Core.Contract; + +namespace Shuttle.Recall.Logging +{ + public static class RecallLoggingOptionsExtensions + { + public static RecallLoggingOptions AddPipelineType( + this RecallLoggingOptions recallLoggingOptions) + { + return recallLoggingOptions.AddPipelineType(typeof(T)); + } + + public static RecallLoggingOptions AddPipelineType(this RecallLoggingOptions recallLoggingOptions, Type type) + { + Guard.AgainstNull(type, nameof(type)); + + if (recallLoggingOptions.PipelineTypes == null) + { + throw new InvalidOperationException(Resources.PipelineTypesNullException); + } + + recallLoggingOptions.PipelineTypes.Add(type.AssemblyQualifiedName); + + return recallLoggingOptions; + } + + public static RecallLoggingOptions AddPipelineEventType(this RecallLoggingOptions recallLoggingOptions) + { + return recallLoggingOptions.AddPipelineEventType(typeof(T)); + } + + public static RecallLoggingOptions AddPipelineEventType(this RecallLoggingOptions recallLoggingOptions, Type type) + { + Guard.AgainstNull(type, nameof(type)); + + if (recallLoggingOptions.PipelineEventTypes == null) + { + throw new InvalidOperationException(Resources.PipelineTypesNullException); + } + + recallLoggingOptions.PipelineEventTypes.Add(type.AssemblyQualifiedName); + + return recallLoggingOptions; + } + + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/Resources.Designer.cs b/Shuttle.Recall.Logging/Resources.Designer.cs new file mode 100644 index 0000000..f032ea6 --- /dev/null +++ b/Shuttle.Recall.Logging/Resources.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Shuttle.Recall.Logging { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Shuttle.Recall.Logging.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to The logging options `PipelineTypes` list is `null`.. + /// + public static string PipelineTypesNullException { + get { + return ResourceManager.GetString("PipelineTypesNullException", resourceCulture); + } + } + } +} diff --git a/Shuttle.Recall.Logging/Resources.resx b/Shuttle.Recall.Logging/Resources.resx new file mode 100644 index 0000000..58fb8b3 --- /dev/null +++ b/Shuttle.Recall.Logging/Resources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The logging options `PipelineTypes` list is `null`. + + \ No newline at end of file diff --git a/Shuttle.Recall.Logging/ServiceCollectionExtensions.cs b/Shuttle.Recall.Logging/ServiceCollectionExtensions.cs new file mode 100644 index 0000000..1dd91d6 --- /dev/null +++ b/Shuttle.Recall.Logging/ServiceCollectionExtensions.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.Extensions.DependencyInjection; +using Shuttle.Core.Contract; + +namespace Shuttle.Recall.Logging +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddRecallLogging(this IServiceCollection services, Action? builder = null) + { + Guard.AgainstNull(services, nameof(services)); + + var recallLoggingBuilder = new RecallLoggingBuilder(services); + + builder?.Invoke(recallLoggingBuilder); + + services.AddOptions().Configure(options => + { + options.PipelineTypes = recallLoggingBuilder.Options.PipelineTypes; + options.PipelineEventTypes = recallLoggingBuilder.Options.PipelineEventTypes; + options.Threading = recallLoggingBuilder.Options.Threading; + }); + + services.AddHostedService(); + services.AddHostedService(); + + services.AddSingleton(); + + return services; + } + } +} diff --git a/Shuttle.Recall.Logging/Shuttle.Recall.Logging.csproj b/Shuttle.Recall.Logging/Shuttle.Recall.Logging.csproj new file mode 100644 index 0000000..218b78c --- /dev/null +++ b/Shuttle.Recall.Logging/Shuttle.Recall.Logging.csproj @@ -0,0 +1,37 @@ + + + + + netstandard2.1 + false + enable + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + PublicResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/Shuttle.Recall.Logging/ThreadingLogger.cs b/Shuttle.Recall.Logging/ThreadingLogger.cs new file mode 100644 index 0000000..4b5b0c4 --- /dev/null +++ b/Shuttle.Recall.Logging/ThreadingLogger.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Shuttle.Core.Contract; +using Shuttle.Core.Pipelines; + +namespace Shuttle.Recall.Logging +{ + public class ThreadingLogger : IHostedService + { + private readonly Type _pipelineType = typeof(EventProcessorStartupPipeline); + private readonly ILogger _logger; + private readonly IPipelineFactory _pipelineFactory; + private readonly RecallLoggingOptions _recallLoggingOptions; + + public ThreadingLogger(IOptions serviceBusLoggingOptions, ILogger logger, IPipelineFactory pipelineFactory) + { + Guard.AgainstNull(serviceBusLoggingOptions, nameof(serviceBusLoggingOptions)); + + _recallLoggingOptions = Guard.AgainstNull(serviceBusLoggingOptions.Value, nameof(serviceBusLoggingOptions.Value)); + _logger = Guard.AgainstNull(logger, nameof(logger)); + _pipelineFactory = Guard.AgainstNull(pipelineFactory, nameof(pipelineFactory)); + + if (_recallLoggingOptions.Threading) + { + _pipelineFactory.PipelineCreated += OnPipelineCreated; + } + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await Task.CompletedTask; + } + + private void OnPipelineCreated(object sender, PipelineEventArgs args) + { + if (args.Pipeline.GetType() != _pipelineType) + { + return; + } + + args.Pipeline.RegisterObserver(new ThreadingObserver(_logger)); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + if (_recallLoggingOptions.Threading) + { + _pipelineFactory.PipelineCreated -= OnPipelineCreated; + + + } + + await Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/Shuttle.Recall.Logging/ThreadingObserver.cs b/Shuttle.Recall.Logging/ThreadingObserver.cs new file mode 100644 index 0000000..b799515 --- /dev/null +++ b/Shuttle.Recall.Logging/ThreadingObserver.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Shuttle.Core.Contract; +using Shuttle.Core.Pipelines; +using Shuttle.Core.Threading; + +namespace Shuttle.Recall.Logging +{ + public class ThreadingObserver : + IPipelineObserver, + IDisposable + { + private readonly ILogger _logger; + private readonly List _wiredProcessorThreadPools = new List(); + private readonly List _wiredProcessorThreads = new List(); + + public ThreadingObserver(ILogger logger) + { + _logger = Guard.AgainstNull(logger, nameof(logger)); + } + + public void Dispose() + { + _wiredProcessorThreads.ForEach(item => + { + item.ProcessorException -= OnProcessorException; + item.ProcessorExecuting -= OnProcessorExecuting; + item.ProcessorThreadActive -= OnProcessorThreadActive; + item.ProcessorThreadStarting -= OnProcessorThreadStarting; + item.ProcessorThreadStopped -= OnProcessorThreadStopped; + item.ProcessorThreadStopping -= OnProcessorThreadStopping; + item.ProcessorThreadOperationCanceled -= OnProcessorThreadOperationCanceled; + }); + + _wiredProcessorThreadPools.ForEach(item => item.ProcessorThreadCreated -= OnProcessorThreadCreated); + } + + public void Execute(OnAfterConfigureThreadPools pipelineEvent) + { + ExecuteAsync(pipelineEvent).GetAwaiter().GetResult(); + } + + public async Task ExecuteAsync(OnAfterConfigureThreadPools pipelineEvent) + { + Wire(pipelineEvent.Pipeline.State.Get("EventProcessorThreadPool")); + + await Task.CompletedTask; + } + + private void OnProcessorException(object sender, ProcessorThreadExceptionEventArgs e) + { + _logger.LogTrace($@"[ProcessorException] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId} / exception = '{e.Exception}'"); + } + + private void OnProcessorExecuting(object sender, ProcessorThreadEventArgs e) + { + _logger.LogTrace($@"[ProcessorExecuting] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId}"); + } + + private void OnProcessorThreadActive(object sender, ProcessorThreadEventArgs e) + { + _logger.LogTrace($@"[ProcessorThreadActive] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId}"); + } + + private void OnProcessorThreadCreated(object sender, ProcessorThreadCreatedEventArgs e) + { + _wiredProcessorThreads.Add(e.ProcessorThread); + + e.ProcessorThread.ProcessorException += OnProcessorException; + e.ProcessorThread.ProcessorExecuting += OnProcessorExecuting; + e.ProcessorThread.ProcessorThreadActive += OnProcessorThreadActive; + e.ProcessorThread.ProcessorThreadStarting += OnProcessorThreadStarting; + e.ProcessorThread.ProcessorThreadStopped += OnProcessorThreadStopped; + e.ProcessorThread.ProcessorThreadStopping += OnProcessorThreadStopping; + e.ProcessorThread.ProcessorThreadOperationCanceled += OnProcessorThreadOperationCanceled; + } + + private void OnProcessorThreadOperationCanceled(object sender, ProcessorThreadEventArgs e) + { + _logger.LogTrace($@"[ProcessorThreadOperationCanceled] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId}"); + } + + private void OnProcessorThreadStarting(object sender, ProcessorThreadEventArgs e) + { + _logger.LogTrace($@"[ProcessorThreadStarting] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId}"); + } + + private void OnProcessorThreadStopped(object sender, ProcessorThreadStoppedEventArgs e) + { + _logger.LogTrace($@"[ProcessorThreadStopped] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId} / aborted = '{e.Aborted}'"); + } + + private void OnProcessorThreadStopping(object sender, ProcessorThreadEventArgs e) + { + _logger.LogTrace($@"[ProcessorThreadStopping] : name = '{e.Name}' / processor = {((ProcessorThread)sender).Processor.GetType().FullName} / managed thread id = {e.ManagedThreadId}"); + } + + private void Wire(IProcessorThreadPool? processorThreadPool) + { + if (processorThreadPool == null) + { + return; + } + + _wiredProcessorThreadPools.Add(processorThreadPool); + + processorThreadPool.ProcessorThreadCreated += OnProcessorThreadCreated; + } + } +} \ No newline at end of file