diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4558d44b..c36856df 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -92,3 +92,31 @@ jobs: with: name: test-results-linux path: build/output/junit-*.xml + + aot-validate: + runs-on: ${{ matrix.os.runner }} + strategy: + fail-fast: false + matrix: + os: + - runner: ubuntu-latest + folder: linux-x64 + binary: ecs-aot-smoketest + - runner: macos-latest + folder: osx-arm64 + binary: ecs-aot-smoketest + - runner: windows-latest + folder: win-x64 + binary: ecs-aot-smoketest.exe + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: ./global.json + + - name: Publish AOT + run: dotnet publish examples/ecs-aot-smoketest + + - name: Invoke AOT + run: ./examples/ecs-aot-smoketest/bin/Release/net9.0/${{ matrix.os.folder }}/publish/${{ matrix.os.binary }} diff --git a/dotnet-tools.json b/dotnet-tools.json index 9c1a3501..c20584c7 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -24,7 +24,7 @@ "rollForward": false }, "nupkg-validator": { - "version": "0.6.0", + "version": "0.7.0", "commands": [ "nupkg-validator" ], diff --git a/ecs-dotnet.sln b/ecs-dotnet.sln index 5e132dfc..56f1d65f 100644 --- a/ecs-dotnet.sln +++ b/ecs-dotnet.sln @@ -141,6 +141,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Serilog.Sinks.Tests EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Serilog.Enrichers.Web", "src\Elastic.Serilog.Enrichers.Web\Elastic.Serilog.Enrichers.Web.csproj", "{B6DCC4C4-1287-41BE-A19D-8F311C6E39F7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ecs-aot-smoketest", "examples\ecs-aot-smoketest\ecs-aot-smoketest.csproj", "{46706BAE-BBCD-4DD9-ADBD-AC099C770854}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -311,6 +313,10 @@ Global {B6DCC4C4-1287-41BE-A19D-8F311C6E39F7}.Debug|Any CPU.Build.0 = Debug|Any CPU {B6DCC4C4-1287-41BE-A19D-8F311C6E39F7}.Release|Any CPU.ActiveCfg = Release|Any CPU {B6DCC4C4-1287-41BE-A19D-8F311C6E39F7}.Release|Any CPU.Build.0 = Release|Any CPU + {46706BAE-BBCD-4DD9-ADBD-AC099C770854}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46706BAE-BBCD-4DD9-ADBD-AC099C770854}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46706BAE-BBCD-4DD9-ADBD-AC099C770854}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46706BAE-BBCD-4DD9-ADBD-AC099C770854}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -358,6 +364,7 @@ Global {5EDF109F-9DFF-4957-8864-BA2702FB78F6} = {7610B796-BB3E-4CB2-8296-79BBFF6D23FC} {933FD923-A2DC-49E3-B21E-8BA888DB5924} = {3582B07D-C2B0-49CC-B676-EAF806EB010E} {B6DCC4C4-1287-41BE-A19D-8F311C6E39F7} = {7610B796-BB3E-4CB2-8296-79BBFF6D23FC} + {46706BAE-BBCD-4DD9-ADBD-AC099C770854} = {05075402-8669-45BD-913A-BD40A29BBEAB} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F60C4BB-6216-4E50-B1E4-9C38EB484843} diff --git a/examples/Elastic.Extensions.Logging.Console.Example/Program.cs b/examples/Elastic.Extensions.Logging.Console.Example/Program.cs index 33f4c6eb..a973afcb 100644 --- a/examples/Elastic.Extensions.Logging.Console.Example/Program.cs +++ b/examples/Elastic.Extensions.Logging.Console.Example/Program.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Host = Microsoft.Extensions.Hosting.Host; await Host.CreateDefaultBuilder(args) @@ -24,4 +25,3 @@ await Host.CreateDefaultBuilder(args) }) .Build() .RunAsync(); - diff --git a/examples/Elastic.Serilog.Sinks.Example/Elastic.Serilog.Sinks.Example.csproj b/examples/Elastic.Serilog.Sinks.Example/Elastic.Serilog.Sinks.Example.csproj index 60cb07d0..1e7b673f 100644 --- a/examples/Elastic.Serilog.Sinks.Example/Elastic.Serilog.Sinks.Example.csproj +++ b/examples/Elastic.Serilog.Sinks.Example/Elastic.Serilog.Sinks.Example.csproj @@ -9,7 +9,7 @@ - + diff --git a/examples/aspnetcore-with-serilog/AspnetCoreExample.csproj b/examples/aspnetcore-with-serilog/AspnetCoreExample.csproj index d4baf31d..9a89478b 100644 --- a/examples/aspnetcore-with-serilog/AspnetCoreExample.csproj +++ b/examples/aspnetcore-with-serilog/AspnetCoreExample.csproj @@ -7,7 +7,7 @@ - + diff --git a/examples/ecs-aot-smoketest/ExtensionsLogger.cs b/examples/ecs-aot-smoketest/ExtensionsLogger.cs new file mode 100644 index 00000000..936bab11 --- /dev/null +++ b/examples/ecs-aot-smoketest/ExtensionsLogger.cs @@ -0,0 +1,50 @@ +using Elastic.Channels.Diagnostics; +using Elastic.Extensions.Logging; +using Elastic.Extensions.Logging.Options; +using Elastic.Transport; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +public class ExtensionsLogger(ITransport transport) +{ + public IDisposable CreateExtensionsLogger( + out ILogger logger, + out ElasticsearchLoggerProvider provider, + out string @namespace, + out WaitHandle waitHandle, + out IChannelDiagnosticsListener listener + ) + { + @namespace = Guid.NewGuid().ToString("N").ToLowerInvariant().Substring(0, 6); + var slim = new CountdownEvent(1); + waitHandle = slim.WaitHandle; + var s = @namespace; + var options = new ConfigureOptions(o => + { + o.Transport = transport; + o.DataStream = new DataStreamNameOptions { Type = "x", Namespace = s, DataSet = "dotnet" }; + var nodes = transport.Configuration.NodePool.Nodes.Select(n => n.Uri).ToArray(); + o.ShipTo = new ShipToOptions { NodeUris = nodes, NodePoolType = NodePoolType.Static }; + }); + + var channelSetup = new IChannelSetup[] + { + new ChannelSetup(c => + { + c.BufferOptions.WaitHandle = slim; + c.BufferOptions.OutboundBufferMaxSize = 1; + c.BufferOptions.OutboundBufferMaxLifetime = TimeSpan.FromSeconds(1); + c.BufferOptions.ExportMaxRetries = 0; + c.BufferOptions.ExportMaxConcurrency = 1; + }) + }; + + var optionsFactory = new OptionsFactory([options], []); + var optionsMonitor = new OptionsMonitor(optionsFactory, [], new OptionsCache()); + provider = new ElasticsearchLoggerProvider(optionsMonitor, channelSetup); + var loggerFactory = new LoggerFactory( [provider], new LoggerFilterOptions { MinLevel = LogLevel.Information }); + logger = loggerFactory.CreateLogger(); + listener = provider.DiagnosticsListener!; + return loggerFactory; + } +} diff --git a/examples/ecs-aot-smoketest/NLogExporter.cs b/examples/ecs-aot-smoketest/NLogExporter.cs new file mode 100644 index 00000000..4bb9da1e --- /dev/null +++ b/examples/ecs-aot-smoketest/NLogExporter.cs @@ -0,0 +1,41 @@ +using Elastic.Channels.Diagnostics; +using Elastic.Transport; +using NLog.Targets; + +public class NLogExporter(ITransport transport) +{ + public IDisposable CreateNLogLogger( + out NLog.Logger logger, + out NLog.LogFactory logFactory, + out string @namespace, + out WaitHandle waitHandle, + out IChannelDiagnosticsListener listener + ) + { + var slim = new CountdownEvent(1); + waitHandle = slim.WaitHandle; + @namespace = Guid.NewGuid().ToString("N").ToLowerInvariant().Substring(0, 6); + + logFactory = new NLog.LogFactory(); + var logConfig = new NLog.Config.LoggingConfiguration(logFactory); + var logTarget = new ElasticsearchTarget { Name = "elastic" }; + logTarget.RequestInvoker = transport.Configuration.RequestInvoker; + logTarget.DataStreamNamespace = @namespace; + logTarget.OutboundBufferMaxSize = 1; + logTarget.OutboundBufferMaxLifetimeSeconds = 1; + logTarget.ExportMaxRetries = 0; + logTarget.ExportMaxConcurrency = 1; + logTarget.ConfigureChannel = (cfg) => cfg.BufferOptions.WaitHandle = slim; + + logTarget.DataStreamType = "x"; + logTarget.DataStreamSet = "dotnet"; + var nodesUris = string.Join(",", transport.Configuration.NodePool.Nodes.Select(n => n.Uri.ToString()).ToArray()); + logTarget.NodeUris = nodesUris; + logTarget.NodePoolType = ElasticPoolType.Static; + logConfig.AddRuleForAllLevels(logTarget); + logFactory.Configuration = logConfig; + listener = logTarget.DiagnosticsListener!; + logger = logFactory.GetLogger("TestLogger"); + return logFactory; + } +} diff --git a/examples/ecs-aot-smoketest/Program.cs b/examples/ecs-aot-smoketest/Program.cs new file mode 100644 index 00000000..9fe24806 --- /dev/null +++ b/examples/ecs-aot-smoketest/Program.cs @@ -0,0 +1,85 @@ +// See https://aka.ms/new-console-template for more information + +using Elastic.CommonSchema; +using Elastic.Transport; +using Microsoft.Extensions.Logging; +using Log = Elastic.CommonSchema.Log; +using LogLevel = NLog.LogLevel; + +Console.WriteLine("Hello, World!"); + +var serialized = @$"{{}}"; +var deserialized = EcsDocument.Deserialize(serialized); +if (deserialized == null) throw new Exception("deserialized is null"); + +serialized = @$"{{ ""agent"": {{ ""unknown"": ""value"" }} }}"; +deserialized = EcsDocument.Deserialize(serialized); +if (deserialized == null) throw new Exception("deserialized is null"); +if (deserialized.Agent == null) throw new Exception("deserialized agent is null"); + +var d = new EcsDocument { Agent = new Agent { Name = "some-agent" }, Log = new Log { Level = "debug" } }; + +serialized = d.Serialize(); +if (string.IsNullOrEmpty(serialized)) throw new Exception("serialized is null"); +Console.WriteLine(serialized); + +var invoker = new InMemoryRequestInvoker(); +var pool = new StaticNodePool([new Node(new Uri("http://localhost:9200"))]); +var configuration = new TransportConfiguration(pool, invoker); +var transport = new DistributedTransport(configuration); + +var extension = new ExtensionsLogger(transport); +LogInMemoryExtensionsLogger(extension); + +var nlog = new NLogExporter(transport); +LogInMemoryNLog(nlog); + +/* +var serilog = new SerilogExporter(transport); +LogInMemorySerilog(serilog); + +void LogInMemorySerilog(SerilogExporter serilogExporter) +{ + using var logger = serilogExporter.CreateSerilogLogger(out var waitHandle, out var listener); + logger.Information("an error occurred {Status}", "failure"); + + if (!waitHandle.WaitOne(TimeSpan.FromSeconds(10))) + throw new Exception($"No flush occurred in 10 seconds: {listener}", listener.ObservedException); + + if (!listener.PublishSuccess) + throw new Exception("Serilog Logger did not export correctly"); + if (listener.ObservedException != null) + throw new Exception("Serilog Logger received exception", listener.ObservedException); + Console.WriteLine("Serilog Logger export success"); +} +*/ + +void LogInMemoryExtensionsLogger(ExtensionsLogger extensionsLogger) +{ + using var _ = extensionsLogger.CreateExtensionsLogger(out var logger, out var provider, out var @namespace, out var waitHandle, out var listener); + logger.LogError("an error occurred {Status}", "failure"); + + if (!waitHandle.WaitOne(TimeSpan.FromSeconds(10))) + throw new Exception($"No flush occurred in 10 seconds: {listener}", listener.ObservedException); + + if (!listener.PublishSuccess) + throw new Exception("Extensions Logger did not export correctly"); + if (listener.ObservedException != null) + throw new Exception("Extensions Logger received exception", listener.ObservedException); + Console.WriteLine("Extensions Logger export success"); +} + +void LogInMemoryNLog(NLogExporter serilogExporter) +{ + using var _ = serilogExporter.CreateNLogLogger(out var logger, out var _, out var _, out var waitHandle, out var listener); + logger.Log(LogLevel.Error, "an error occurred {Status}", "failure"); + + if (!waitHandle.WaitOne(TimeSpan.FromSeconds(10))) + throw new Exception($"No flush occurred in 10 seconds: {listener}", listener.ObservedException); + + if (!listener.PublishSuccess) + throw new Exception("NLog did not export correctly"); + if (listener.ObservedException != null) + throw new Exception("NLog export received exception", listener.ObservedException); + Console.WriteLine("NLog export success"); +} diff --git a/examples/ecs-aot-smoketest/SerilogExporter.cs b/examples/ecs-aot-smoketest/SerilogExporter.cs new file mode 100644 index 00000000..6731b8a1 --- /dev/null +++ b/examples/ecs-aot-smoketest/SerilogExporter.cs @@ -0,0 +1,39 @@ +using Elastic.Channels; +using Elastic.Channels.Diagnostics; +using Elastic.Ingest.Elasticsearch.DataStreams; +using Elastic.Serilog.Sinks; +using Elastic.Transport; +using Serilog; +using Serilog.Core; + +public class SerilogExporter(ITransport transport) +{ + public Logger CreateSerilogLogger(out WaitHandle waitHandle, out IChannelDiagnosticsListener listener) + { + var countdown = new CountdownEvent(1); + waitHandle = countdown.WaitHandle; + + IChannelDiagnosticsListener? listen = null; + var options = new ElasticsearchSinkOptions(transport) + { + DataStream = new DataStreamName("logs", "serilog", "tests"), + ConfigureChannel = c => + { + c.BufferOptions = new BufferOptions + { + WaitHandle = countdown, + OutboundBufferMaxSize = 1 + }; + }, + ChannelDiagnosticsCallback = l => listen = l + }; + listener = listen ?? throw new Exception("No listener"); + + var loggerConfig = new LoggerConfiguration() + .MinimumLevel.Information() + .WriteTo.Elasticsearch(options); + + var logger = loggerConfig.CreateLogger(); + return logger; + } +} diff --git a/examples/ecs-aot-smoketest/ecs-aot-smoketest.csproj b/examples/ecs-aot-smoketest/ecs-aot-smoketest.csproj new file mode 100644 index 00000000..07106cb8 --- /dev/null +++ b/examples/ecs-aot-smoketest/ecs-aot-smoketest.csproj @@ -0,0 +1,25 @@ + + + + Exe + net9.0 + ecs_aot_smoketest + enable + enable + true + false + true + + + + + + + + + + + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 7f8b648d..56b95fa7 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,7 +15,21 @@ true - + + + true + false + + + + true + + + + + + diff --git a/src/Elastic.Apm.NLog/ApmServiceNameLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmServiceNameLayoutRenderer.cs index fe562842..ef8c0107 100644 --- a/src/Elastic.Apm.NLog/ApmServiceNameLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmServiceNameLayoutRenderer.cs @@ -13,7 +13,7 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmServiceName as special logging variable to render the current Elastic APM Service Name /// [LayoutRenderer(Name)] -[ThreadSafe, ThreadAgnostic] +[ThreadAgnostic] public class ApmServiceNameLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/ApmServiceNodeNameLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmServiceNodeNameLayoutRenderer.cs index 95ca0669..de2c9a0a 100644 --- a/src/Elastic.Apm.NLog/ApmServiceNodeNameLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmServiceNodeNameLayoutRenderer.cs @@ -9,7 +9,7 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmServiceNodeName as special logging variable to render the current Elastic APM Service Node Name /// [LayoutRenderer(Name)] -[ThreadSafe, ThreadAgnostic] +[ThreadAgnostic] public class ApmServiceNodeNameLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/ApmServiceVersionLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmServiceVersionLayoutRenderer.cs index a2184b74..5f32ab69 100644 --- a/src/Elastic.Apm.NLog/ApmServiceVersionLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmServiceVersionLayoutRenderer.cs @@ -9,7 +9,7 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmServiceVersion as special logging variable to render the current Elastic APM Service Version /// [LayoutRenderer(Name)] -[ThreadSafe, ThreadAgnostic] +[ThreadAgnostic] public class ApmServiceVersionLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/ApmSpanIdLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmSpanIdLayoutRenderer.cs index 9126d2d0..e2277332 100644 --- a/src/Elastic.Apm.NLog/ApmSpanIdLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmSpanIdLayoutRenderer.cs @@ -9,7 +9,6 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmSpanId as special logging variable to render the current Elastic APM Span Id /// [LayoutRenderer(Name)] -[ThreadSafe] public class ApmSpanIdLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/ApmTraceIdLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmTraceIdLayoutRenderer.cs index db4e608d..ed70fffd 100644 --- a/src/Elastic.Apm.NLog/ApmTraceIdLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmTraceIdLayoutRenderer.cs @@ -13,7 +13,6 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmTraceId as special logging variable to render the current Elastic APM Trace Id /// [LayoutRenderer(Name)] -[ThreadSafe] public class ApmTraceIdLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/ApmTransactionIdLayoutRenderer.cs b/src/Elastic.Apm.NLog/ApmTransactionIdLayoutRenderer.cs index 35a3ffeb..80074a23 100644 --- a/src/Elastic.Apm.NLog/ApmTransactionIdLayoutRenderer.cs +++ b/src/Elastic.Apm.NLog/ApmTransactionIdLayoutRenderer.cs @@ -13,7 +13,6 @@ namespace Elastic.Apm.NLog; /// Provides ElasticApmTransactionId as special logging variable to render the current Elastic APM Transaction Id /// [LayoutRenderer(Name)] -[ThreadSafe] public class ApmTransactionIdLayoutRenderer : LayoutRenderer { /// diff --git a/src/Elastic.Apm.NLog/Elastic.Apm.NLog.csproj b/src/Elastic.Apm.NLog/Elastic.Apm.NLog.csproj index 4d581868..2a82c23a 100644 --- a/src/Elastic.Apm.NLog/Elastic.Apm.NLog.csproj +++ b/src/Elastic.Apm.NLog/Elastic.Apm.NLog.csproj @@ -13,7 +13,7 @@ Feel free to open an issue on our repos to discuss if you feel you need to target a lower NLog version --> - + diff --git a/src/Elastic.Apm.SerilogEnricher/Elastic.Apm.SerilogEnricher.csproj b/src/Elastic.Apm.SerilogEnricher/Elastic.Apm.SerilogEnricher.csproj index 7f45db7c..2f8b2074 100644 --- a/src/Elastic.Apm.SerilogEnricher/Elastic.Apm.SerilogEnricher.csproj +++ b/src/Elastic.Apm.SerilogEnricher/Elastic.Apm.SerilogEnricher.csproj @@ -6,7 +6,7 @@ True - + diff --git a/src/Elastic.CommonSchema.NLog/EcsLayout.cs b/src/Elastic.CommonSchema.NLog/EcsLayout.cs index 586f17d6..4713368f 100644 --- a/src/Elastic.CommonSchema.NLog/EcsLayout.cs +++ b/src/Elastic.CommonSchema.NLog/EcsLayout.cs @@ -18,7 +18,6 @@ namespace Elastic.CommonSchema.NLog { /// An NLOG layout implementation that renders logs as ECS json [Layout(Name)] - [ThreadSafe] [ThreadAgnostic] public partial class EcsLayout : Layout { @@ -50,7 +49,6 @@ public EcsLayout() ProcessThreadId = "${threadid}"; HostName = "${hostname}"; // NLog 4.6 - HostIp = "${local-ip:cachedSeconds=60}"; // NLog 4.6.8 ServerUser = "${environment-user}"; // NLog 4.6.4 @@ -94,9 +92,6 @@ protected override void InitializeLayout() UrlPort = "${aspnet-request-url:IncludeScheme=false:IncludeHost=false:IncludePath=false:IncludePort=true}"; UrlQuery = "${aspnet-request-url:IncludeScheme=false:IncludeHost=false:IncludePath=false:IncludeQueryString=true}"; UrlUserName = "${aspnet-user-identity}"; - - if (!NLogApmLoaded.Value) - ApmTraceId = "${scopeproperty:item=RequestId:whenEmpty=${aspnet-TraceIdentifier}}"; } base.InitializeLayout(); @@ -383,15 +378,14 @@ private MetadataDictionary GetMetadata(LogEventInfo e) if (IncludeScopeProperties) { - foreach (var key in MappedDiagnosticsLogicalContext.GetNames()) + foreach (var scopeProperty in ScopeContext.GetAllProperties()) { - if (string.IsNullOrEmpty(key) || ExcludeProperties.Contains(key)) + if (string.IsNullOrEmpty(scopeProperty.Key) || ExcludeProperties.Contains(scopeProperty.Key)) continue; - var propertyValue = MappedDiagnosticsLogicalContext.GetObject(key); - if (!TryPopulateWhenSafe(metadata, key, propertyValue)) + if (!TryPopulateWhenSafe(metadata, scopeProperty.Key, scopeProperty.Value)) { - Populate(metadata, key, propertyValue.ToString()); + Populate(metadata, scopeProperty.Key, scopeProperty.Value.ToString()); } } } diff --git a/src/Elastic.CommonSchema.NLog/Elastic.CommonSchema.NLog.csproj b/src/Elastic.CommonSchema.NLog/Elastic.CommonSchema.NLog.csproj index 05249fe6..70af4aae 100644 --- a/src/Elastic.CommonSchema.NLog/Elastic.CommonSchema.NLog.csproj +++ b/src/Elastic.CommonSchema.NLog/Elastic.CommonSchema.NLog.csproj @@ -11,6 +11,6 @@ - + diff --git a/src/Elastic.CommonSchema.Serilog/Elastic.CommonSchema.Serilog.csproj b/src/Elastic.CommonSchema.Serilog/Elastic.CommonSchema.Serilog.csproj index 121f1edd..bf594823 100644 --- a/src/Elastic.CommonSchema.Serilog/Elastic.CommonSchema.Serilog.csproj +++ b/src/Elastic.CommonSchema.Serilog/Elastic.CommonSchema.Serilog.csproj @@ -13,9 +13,6 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive - + - \ No newline at end of file + diff --git a/src/Elastic.CommonSchema/EcsDocument.DefaultService.cs b/src/Elastic.CommonSchema/EcsDocument.DefaultService.cs index 36dfaa2a..7a64d35c 100644 --- a/src/Elastic.CommonSchema/EcsDocument.DefaultService.cs +++ b/src/Elastic.CommonSchema/EcsDocument.DefaultService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -67,6 +68,8 @@ private static void UpdateServiceWithEnvironmentConfig(Service service, IDiction ? entryAssembly.GetCustomAttribute()?.InformationalVersion : null; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] private static Assembly? GetEntryAssembly() { var entryAssembly = Assembly.GetEntryAssembly(); diff --git a/src/Elastic.CommonSchema/EcsDocument.Serialization.cs b/src/Elastic.CommonSchema/EcsDocument.Serialization.cs index a61e4683..52b2bcb0 100644 --- a/src/Elastic.CommonSchema/EcsDocument.Serialization.cs +++ b/src/Elastic.CommonSchema/EcsDocument.Serialization.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using System; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text; using System.Text.Encodings.Web; @@ -68,10 +69,14 @@ protected internal virtual void WriteAdditionalProperties(Action EcsSerializerFactory.DeserializeAsync(stream, ctx); /// Serialize this instance to string + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public string Serialize() => JsonSerializer.Serialize(this, GetType(), SerializerOptions); // ReSharper disable once UnusedMember.Global /// Serialize this instance to utf8 bytes + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public byte[] SerializeToUtf8Bytes() => JsonSerializer.SerializeToUtf8Bytes(this, GetType(), SerializerOptions); private static readonly ReusableUtf8JsonWriter ReusableJsonWriter = new(); @@ -87,6 +92,8 @@ public StringBuilder Serialize(StringBuilder stringBuilder) // ReSharper disable once UnusedMember.Global /// Serialize this instance to a Stream + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public void Serialize(Stream stream) { using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions @@ -97,9 +104,13 @@ public void Serialize(Stream stream) JsonSerializer.Serialize(writer, this, SerializerOptions); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] internal void Serialize(Utf8JsonWriter writer) => JsonSerializer.Serialize(writer, this, SerializerOptions); /// Serialize this instance to a Stream asynchronously + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public Task SerializeAsync(Stream stream, CancellationToken ctx = default) => JsonSerializer.SerializeAsync(stream, this, GetType(), SerializerOptions, ctx); } diff --git a/src/Elastic.CommonSchema/EcsDocument.cs b/src/Elastic.CommonSchema/EcsDocument.cs index 74f2a2fe..a5c92774 100644 --- a/src/Elastic.CommonSchema/EcsDocument.cs +++ b/src/Elastic.CommonSchema/EcsDocument.cs @@ -226,7 +226,7 @@ Process ReturnFromCache() // see: https://github.com/elastic/apm-agent-dotnet/pull/847 // see: https://github.com/elastic/apm-agent-dotnet/issues/6 // Mentions observed exceptions however this extension method itself is wrapped in try/catch - var exString = exception.ToStringDemystified(); + var exString = exception.ToString(); return new Error { Message = exception.Message, Type = exception.GetType().FullName, StackTrace = exString }; } diff --git a/src/Elastic.CommonSchema/Elastic.CommonSchema.csproj b/src/Elastic.CommonSchema/Elastic.CommonSchema.csproj index ad476637..2baaf315 100644 --- a/src/Elastic.CommonSchema/Elastic.CommonSchema.csproj +++ b/src/Elastic.CommonSchema/Elastic.CommonSchema.csproj @@ -1,20 +1,19 @@ - netstandard2.0;netstandard2.1;net462;net8.0 + netstandard2.0;netstandard2.1;net462;net8.0;net9.0 Elastic Common Schema (ECS) Types Maps Elastic Common Schema (ECS) to .NET types including (de)serialization using System.Text.Json latest True enable - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + true + false + + + diff --git a/src/Elastic.CommonSchema/Serialization/EcsDocumentJsonConverterFactory.cs b/src/Elastic.CommonSchema/Serialization/EcsDocumentJsonConverterFactory.cs index 167c9271..ee1e24c3 100644 --- a/src/Elastic.CommonSchema/Serialization/EcsDocumentJsonConverterFactory.cs +++ b/src/Elastic.CommonSchema/Serialization/EcsDocumentJsonConverterFactory.cs @@ -1,4 +1,6 @@ using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; @@ -14,7 +16,10 @@ public class EcsDocumentJsonConverterFactory : JsonConverterFactory public override bool CanConvert(Type typeToConvert) => typeof(EcsDocument).IsAssignableFrom(typeToConvert); /// - public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL2092:DynamicallyAccessedMemberTypes", Justification = "More concrete then override")] + public override JsonConverter CreateConverter([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]Type typeToConvert, JsonSerializerOptions options) { if (typeToConvert == typeof(EcsDocument)) return EcsJsonConfiguration.DefaultEcsDocumentJsonConverter; diff --git a/src/Elastic.CommonSchema/Serialization/EcsJsonConverterBase.cs b/src/Elastic.CommonSchema/Serialization/EcsJsonConverterBase.cs index 68070d19..e9851e69 100644 --- a/src/Elastic.CommonSchema/Serialization/EcsJsonConverterBase.cs +++ b/src/Elastic.CommonSchema/Serialization/EcsJsonConverterBase.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -13,6 +14,8 @@ namespace Elastic.CommonSchema.Serialization public abstract class EcsJsonConverterBase : JsonConverter { /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] protected static JsonConverter GetDateTimeOffsetConverter(JsonSerializerOptions options) => options == EcsJsonConfiguration.SerializerOptions ? EcsJsonConfiguration.DateTimeOffsetConverter @@ -59,6 +62,8 @@ protected static void WritePropString(Utf8JsonWriter writer, string key, string? } /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] protected static void WriteProp(Utf8JsonWriter writer, string key, TValue value, JsonSerializerOptions options) { if (value == null) return; @@ -70,6 +75,8 @@ protected static void WriteProp(Utf8JsonWriter writer, string key, TValu } /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] protected static void WriteProp(Utf8JsonWriter writer, string key, TValue value, JsonTypeInfo typeInfo, JsonSerializerOptions options) { @@ -84,6 +91,8 @@ protected static void WriteProp(Utf8JsonWriter writer, string key, TValu else JsonSerializer.Serialize(writer, value, typeInfo); } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] internal static object? ReadPropDeserialize(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return null; @@ -122,6 +131,8 @@ protected static bool ReadPropString(ref Utf8JsonReader reader, string key, T b, /// // ReSharper disable once UnusedParameter.Local (key is used for readability) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] private static TValue? ReadProp(ref Utf8JsonReader reader, string key, JsonSerializerOptions options) where TValue : class { if (reader.TokenType == JsonTokenType.Null) return null; diff --git a/src/Elastic.CommonSchema/Serialization/EcsSerializerFactory.cs b/src/Elastic.CommonSchema/Serialization/EcsSerializerFactory.cs index 27ae68cb..46c15073 100644 --- a/src/Elastic.CommonSchema/Serialization/EcsSerializerFactory.cs +++ b/src/Elastic.CommonSchema/Serialization/EcsSerializerFactory.cs @@ -1,5 +1,6 @@ using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Text.Json; using System.Threading; @@ -21,18 +22,24 @@ namespace Elastic.CommonSchema.Serialization; /// /// Deserialize a instance from a Stream asynchronously. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public static ValueTask DeserializeAsync(Stream stream, CancellationToken ctx = default) => JsonSerializer.DeserializeAsync(stream, EcsJsonConfiguration.SerializerOptions, ctx); /// /// Deserialize a instance from a json string. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public static TEcsDocument? Deserialize(string json) => JsonSerializer.Deserialize(json, EcsJsonConfiguration.SerializerOptions); /// /// Deserialize a instance from a readonly span of bytes. /// + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public static TEcsDocument? Deserialize(ReadOnlySpan json) => JsonSerializer.Deserialize(json, EcsJsonConfiguration.SerializerOptions); diff --git a/src/Elastic.CommonSchema/Serialization/JsonConfiguration.cs b/src/Elastic.CommonSchema/Serialization/JsonConfiguration.cs index 549758aa..9c6f5db1 100644 --- a/src/Elastic.CommonSchema/Serialization/JsonConfiguration.cs +++ b/src/Elastic.CommonSchema/Serialization/JsonConfiguration.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using System; +using System.Diagnostics.CodeAnalysis; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; @@ -13,7 +14,16 @@ namespace Elastic.CommonSchema.Serialization /// Static class holding public static class EcsJsonConfiguration { + /// ahas + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] + static EcsJsonConfiguration() + { + } + /// Default used by ECS integrations + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public static JsonSerializerOptions SerializerOptions { get; } = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault, @@ -38,10 +48,14 @@ public static class EcsJsonConfiguration } }; + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] internal static readonly JsonConverter DateTimeOffsetConverter = (JsonConverter)SerializerOptions.GetConverter(typeof(DateTimeOffset)); /// Default for + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public static readonly EcsDocumentJsonConverter DefaultEcsDocumentJsonConverter = new(); private sealed class EcsJsonStringConverter : JsonConverter diff --git a/src/Elastic.CommonSchema/Serialization/MetadataDictionaryConverter.cs b/src/Elastic.CommonSchema/Serialization/MetadataDictionaryConverter.cs index 7ec8ffa5..1ea88021 100644 --- a/src/Elastic.CommonSchema/Serialization/MetadataDictionaryConverter.cs +++ b/src/Elastic.CommonSchema/Serialization/MetadataDictionaryConverter.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using System.Text.Json; using System.Text.Json.Serialization; @@ -53,6 +54,8 @@ internal class MetaDataSerializationFailure return dictionary.Count > 0 ? dictionary : null; } + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")] public override void Write(Utf8JsonWriter writer, MetadataDictionary value, JsonSerializerOptions options) { writer.WriteStartObject(); diff --git a/src/Elastic.Extensions.Logging.Common/LogEventJsonContext.cs b/src/Elastic.Extensions.Logging.Common/LogEventJsonContext.cs index 639ca79d..e947280c 100644 --- a/src/Elastic.Extensions.Logging.Common/LogEventJsonContext.cs +++ b/src/Elastic.Extensions.Logging.Common/LogEventJsonContext.cs @@ -7,4 +7,4 @@ namespace Elastic.Extensions.Logging.Common; /// [JsonSerializable(typeof(LogEvent))] [JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)] -public partial class LogEventJsonContext : JsonSerializerContext { } +public partial class LogEventJsonContext : JsonSerializerContext; diff --git a/src/Elastic.Extensions.Logging.Console/LoggingBuilderExtensions.cs b/src/Elastic.Extensions.Logging.Console/LoggingBuilderExtensions.cs index d354b684..db7b5653 100644 --- a/src/Elastic.Extensions.Logging.Console/LoggingBuilderExtensions.cs +++ b/src/Elastic.Extensions.Logging.Console/LoggingBuilderExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Microsoft.Extensions.Logging; namespace Elastic.Extensions.Logging.Console; @@ -6,7 +7,14 @@ namespace Elastic.Extensions.Logging.Console; public static class LoggingBuilderExtensions { /// Adds ECS output to console output - public static ILoggingBuilder AddEcsConsole(this ILoggingBuilder builder, LogLevel stdErrorThreshold = LogLevel.Warning, Action? configure = null) + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Action is not bound")] + [UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "Action is not bound")] + [UnconditionalSuppressMessage("AotAnalysis", "IL2092:DynamicallyAccessedMemberTypes", Justification = "More concrete then override")] + public static ILoggingBuilder AddEcsConsole( + this ILoggingBuilder builder, + LogLevel stdErrorThreshold = LogLevel.Warning, + Action? configure = null + ) { builder.AddConsole(c=> { diff --git a/src/Elastic.Extensions.Logging/Elastic.Extensions.Logging.csproj b/src/Elastic.Extensions.Logging/Elastic.Extensions.Logging.csproj index 7384c00d..f8c13c30 100644 --- a/src/Elastic.Extensions.Logging/Elastic.Extensions.Logging.csproj +++ b/src/Elastic.Extensions.Logging/Elastic.Extensions.Logging.csproj @@ -14,9 +14,6 @@ - - runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/src/Elastic.Ingest.Elasticsearch.CommonSchema/Elastic.Ingest.Elasticsearch.CommonSchema.csproj b/src/Elastic.Ingest.Elasticsearch.CommonSchema/Elastic.Ingest.Elasticsearch.CommonSchema.csproj index 77714fe7..ac3e4276 100644 --- a/src/Elastic.Ingest.Elasticsearch.CommonSchema/Elastic.Ingest.Elasticsearch.CommonSchema.csproj +++ b/src/Elastic.Ingest.Elasticsearch.CommonSchema/Elastic.Ingest.Elasticsearch.CommonSchema.csproj @@ -10,7 +10,7 @@ - + diff --git a/src/Elastic.NLog.Targets/ElasticsearchTarget.cs b/src/Elastic.NLog.Targets/ElasticsearchTarget.cs index 2585f249..01ac6a3e 100644 --- a/src/Elastic.NLog.Targets/ElasticsearchTarget.cs +++ b/src/Elastic.NLog.Targets/ElasticsearchTarget.cs @@ -26,8 +26,8 @@ namespace NLog.Targets public class ElasticsearchTarget : TargetWithLayout { /// - public override Layout Layout { get => _layout; set => _layout = value as Elastic.CommonSchema.NLog.EcsLayout ?? _layout; } - private Elastic.CommonSchema.NLog.EcsLayout _layout = new Elastic.CommonSchema.NLog.EcsLayout(); + public override Layout Layout { get => _layout; set => _layout = value as EcsLayout ?? _layout; } + private EcsLayout _layout = new EcsLayout(); private IBufferedChannel? _channel; /// @@ -189,6 +189,9 @@ public Layout? CloudId /// public Action>? ConfigureChannel { get; set; } + /// Provide a to the this target uses + public IRequestInvoker? RequestInvoker { get; set; } + /// public IChannelDiagnosticsListener? DiagnosticsListener => _channel?.DiagnosticsListener; @@ -200,7 +203,11 @@ protected override void InitializeTarget() var indexOffset = string.IsNullOrEmpty(indexOffsetHours) ? default(TimeSpan?) : TimeSpan.FromHours(int.Parse(indexOffsetHours)); var connectionPool = CreateNodePool(); - var config = new TransportConfigurationDescriptor(connectionPool, productRegistration: ElasticsearchProductRegistration.Default); + var config = new TransportConfigurationDescriptor( + connectionPool, + productRegistration: ElasticsearchProductRegistration.Default, + invoker: RequestInvoker + ); // Cloud sets authentication as required parameter in the constructor if (NodePoolType != ElasticPoolType.Cloud) config = SetAuthenticationOnTransport(config); @@ -254,7 +261,7 @@ private EcsDataStreamChannel CreateDataStreamChannel(Distribute var channelOptions = new DataStreamChannelOptions(transport) { DataStream = new DataStreamName(dataStreamType, dataStreamSet, dataStreamNamespace), - SerializerContexts = [EcsJsonContext.Default, Elastic.CommonSchema.NLog.NLogEcsJsonContext.Default] + SerializerContexts = [EcsJsonContext.Default, NLogEcsJsonContext.Default] }; SetupChannelOptions(channelOptions); var channel = new EcsDataStreamChannel(channelOptions, new[] { new InternalLoggerCallbackListener() }); @@ -270,7 +277,7 @@ private EcsIndexChannel CreateIndexChannel(DistributedTransport IndexOffset = indexOffset, TimestampLookup = l => l.Timestamp, OperationMode = indexOperation, - SerializerContexts = [EcsJsonContext.Default, Elastic.CommonSchema.NLog.NLogEcsJsonContext.Default] + SerializerContexts = [EcsJsonContext.Default, NLogEcsJsonContext.Default] }; if (_hasIndexEventId) indexChannelOptions.BulkOperationIdLookup = (logEvent) => (logEvent.Event?.Id)!; @@ -287,7 +294,7 @@ protected override void CloseTarget() } /// - protected override void Write(NLog.Common.AsyncLogEventInfo logEvent) + protected override void Write(Common.AsyncLogEventInfo logEvent) { try { @@ -296,7 +303,7 @@ protected override void Write(NLog.Common.AsyncLogEventInfo logEvent) logEvent.Continuation(null); else { - NLog.Common.InternalLogger.Error("ElasticSearch - Failed writing to Elastic channel (Buffer full)"); + Common.InternalLogger.Error("ElasticSearch - Failed writing to Elastic channel (Buffer full)"); logEvent.Continuation(new System.Threading.Tasks.TaskCanceledException("Failed writing to Elastic channel (Buffer full)")); } } @@ -389,15 +396,15 @@ public InternalLoggerCallbackListener() { ExportExceptionCallback = ex => { - NLog.Common.InternalLogger.Error(ex, "ElasticSearch - Export Exception"); + Common.InternalLogger.Error(ex, "ElasticSearch - Export Exception"); }; PublishToInboundChannelFailureCallback = () => { - NLog.Common.InternalLogger.Error("ElasticSearch - Inbound Channel Failure (Buffer full)"); + Common.InternalLogger.Error("ElasticSearch - Inbound Channel Failure (Buffer full)"); }; PublishToOutboundChannelFailureCallback = () => { - NLog.Common.InternalLogger.Error("ElasticSearch - Outbound Channel Failure (Flush failed)"); + Common.InternalLogger.Error("ElasticSearch - Outbound Channel Failure (Flush failed)"); }; ExportResponseCallback = (response, _) => { @@ -405,13 +412,13 @@ public InternalLoggerCallbackListener() return; if (response.TryGetElasticsearchServerError(out var error)) - NLog.Common.InternalLogger.Error("ElasticSearch - Export Response Server Error - {0}", error); + Common.InternalLogger.Error("ElasticSearch - Export Response Server Error - {0}", error); if (response.Items?.Count > 0) { foreach (var itemResult in response.Items) if (itemResult?.Status >= 300) - NLog.Common.InternalLogger.Error("ElasticSearch - Export Item failed to {0} document status {1} - {2}", itemResult.Action, itemResult.Status, itemResult.Error); + Common.InternalLogger.Error("ElasticSearch - Export Item failed to {0} document status {1} - {2}", itemResult.Action, itemResult.Status, itemResult.Error); } }; } diff --git a/src/Elastic.Serilog.Enrichers.Web/Elastic.Serilog.Enrichers.Web.csproj b/src/Elastic.Serilog.Enrichers.Web/Elastic.Serilog.Enrichers.Web.csproj index ff3f245b..f429997c 100644 --- a/src/Elastic.Serilog.Enrichers.Web/Elastic.Serilog.Enrichers.Web.csproj +++ b/src/Elastic.Serilog.Enrichers.Web/Elastic.Serilog.Enrichers.Web.csproj @@ -20,7 +20,7 @@ - + diff --git a/tests-integration/Elasticsearch.IntegrationDefaults/Elasticsearch.IntegrationDefaults.csproj b/tests-integration/Elasticsearch.IntegrationDefaults/Elasticsearch.IntegrationDefaults.csproj index 6647e3f4..05540e27 100644 --- a/tests-integration/Elasticsearch.IntegrationDefaults/Elasticsearch.IntegrationDefaults.csproj +++ b/tests-integration/Elasticsearch.IntegrationDefaults/Elasticsearch.IntegrationDefaults.csproj @@ -11,7 +11,7 @@ - + diff --git a/tests/Elastic.Apm.NLog.Tests/Elastic.Apm.NLog.Tests.csproj b/tests/Elastic.Apm.NLog.Tests/Elastic.Apm.NLog.Tests.csproj index e6d11190..28509887 100644 --- a/tests/Elastic.Apm.NLog.Tests/Elastic.Apm.NLog.Tests.csproj +++ b/tests/Elastic.Apm.NLog.Tests/Elastic.Apm.NLog.Tests.csproj @@ -7,8 +7,8 @@ - - + + diff --git a/tests/Elastic.Apm.NLog.Tests/NLogTests.cs b/tests/Elastic.Apm.NLog.Tests/NLogTests.cs index 479f64c4..15b18add 100644 --- a/tests/Elastic.Apm.NLog.Tests/NLogTests.cs +++ b/tests/Elastic.Apm.NLog.Tests/NLogTests.cs @@ -14,8 +14,15 @@ public class NLogTests { public NLogTests() { - var assembly = typeof(ApmTraceIdLayoutRenderer).Assembly; - global::NLog.Config.ConfigurationItemFactory.Default.RegisterItemsFromAssembly(assembly); + global::NLog.LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayoutRenderer(ApmTraceIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmTransactionIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmSpanIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceNameLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceVersionLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceNodeNameLayoutRenderer.Name); + }); var configuration = new MockConfiguration("my-service", "my-service-node-name", "0.2.1"); if (!Agent.IsConfigured) @@ -33,9 +40,9 @@ public void NLogWithTransaction() var target = new MemoryTarget(); target.Layout = "${ElasticApmTraceId}|${ElasticApmTransactionId}|${ElasticApmSpanId}|${message}"; - global::NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + var logFactory = new global::NLog.LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; - var logger = LogManager.GetLogger("Example"); + var logger = logFactory.GetLogger("Example"); logger.Debug("PreTransaction"); @@ -72,9 +79,9 @@ public void NLogWithServiceName() var target = new MemoryTarget(); target.Layout = "${ElasticApmServiceName}|${ElasticApmServiceNodeName}|${ElasticApmServiceVersion}|${message}"; - global::NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Debug); + var logFactory = new global::NLog.LogFactory().Setup().LoadConfiguration(cfg => cfg.ForLogger().WriteTo(target)).LogFactory; - var logger = LogManager.GetLogger("Example"); + var logger = logFactory.GetLogger("Example"); logger.Debug("Some log"); diff --git a/tests/Elastic.CommonSchema.Log4net.Tests/MessageTests.cs b/tests/Elastic.CommonSchema.Log4net.Tests/MessageTests.cs index 703cec5a..5152d95e 100644 --- a/tests/Elastic.CommonSchema.Log4net.Tests/MessageTests.cs +++ b/tests/Elastic.CommonSchema.Log4net.Tests/MessageTests.cs @@ -127,10 +127,9 @@ public void ToEcs_EventWithException_PopulatesErrorField() => TestLogger((log, g info.Error.Type.Should().Be(e.GetType().FullName); info.Error.StackTrace.Should().Contain(e.Message); - info.Error.StackTrace.Should().Contain("at void"); + info.Error.StackTrace.Should().Contain("at Elastic.CommonSchema.Log4net.Tests.MessageTests"); info.Error.StackTrace.Should().Contain(innerException.Message); - info.Error.StackTrace.Should().Contain("at void"); } }); diff --git a/tests/Elastic.CommonSchema.NLog.Tests/EcsFieldsInTemplateTests.cs b/tests/Elastic.CommonSchema.NLog.Tests/EcsFieldsInTemplateTests.cs index 4726cf6d..a3d1c81a 100644 --- a/tests/Elastic.CommonSchema.NLog.Tests/EcsFieldsInTemplateTests.cs +++ b/tests/Elastic.CommonSchema.NLog.Tests/EcsFieldsInTemplateTests.cs @@ -24,7 +24,7 @@ public void CanUseEcsFieldNamesAsTemplateProperty() => TestLogger((logger, getLo var ecsEvents = ToEcsEvents(logEvents); var (_, info) = ecsEvents.First(); - info.Message.Should().Be("Info \"my-trace-id\": true"); + info.Message.Should().Be("Info my-trace-id: true"); info.Labels.Should().BeNull(); info.Metadata.Should().BeNull(); diff --git a/tests/Elastic.CommonSchema.NLog.Tests/Elastic.CommonSchema.NLog.Tests.csproj b/tests/Elastic.CommonSchema.NLog.Tests/Elastic.CommonSchema.NLog.Tests.csproj index ead4355f..5abe6c21 100644 --- a/tests/Elastic.CommonSchema.NLog.Tests/Elastic.CommonSchema.NLog.Tests.csproj +++ b/tests/Elastic.CommonSchema.NLog.Tests/Elastic.CommonSchema.NLog.Tests.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/tests/Elastic.CommonSchema.NLog.Tests/LogTestsBase.cs b/tests/Elastic.CommonSchema.NLog.Tests/LogTestsBase.cs index f13255ed..9de8e34c 100644 --- a/tests/Elastic.CommonSchema.NLog.Tests/LogTestsBase.cs +++ b/tests/Elastic.CommonSchema.NLog.Tests/LogTestsBase.cs @@ -8,7 +8,6 @@ using Elastic.Apm.NLog; using Elastic.CommonSchema.Tests.Specs; using NLog; -using NLog.LayoutRenderers; using Config=NLog.Config; using NLog.Targets; using Xunit.Abstractions; @@ -31,19 +30,23 @@ protected void TestLogger(Action>> act) => protected void TestLoggerAndLayout(Action setup, Action>> act) { // These layout renderers need to registered statically as ultimately ConfigurationItemFactory.Default is called in the call stack. - LayoutRenderer.Register(ApmTraceIdLayoutRenderer.Name); //generic - LayoutRenderer.Register(ApmTransactionIdLayoutRenderer.Name); //generic - LayoutRenderer.Register(ApmSpanIdLayoutRenderer.Name); //generic - LayoutRenderer.Register(ApmServiceNameLayoutRenderer.Name); //generic - LayoutRenderer.Register(ApmServiceVersionLayoutRenderer.Name); //generic - LayoutRenderer.Register(ApmServiceNodeNameLayoutRenderer.Name); //generic + LogManager.Setup().SetupExtensions(ext => + { + ext.RegisterLayout(); + ext.RegisterLayoutRenderer(ApmTraceIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmTransactionIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmSpanIdLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceNameLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceVersionLayoutRenderer.Name); + ext.RegisterLayoutRenderer(ApmServiceNodeNameLayoutRenderer.Name); + }); var logFactory = new LogFactory(); var logConfig = new Config.LoggingConfiguration(logFactory); var ecsLayout = new EcsLayout { IncludeScopeProperties = true }; ecsLayout.ExcludeProperties.Add("NotX"); setup?.Invoke(ecsLayout); - var memoryTarget = new MemoryTarget { Layout = ecsLayout, OptimizeBufferReuse = true }; + var memoryTarget = new MemoryTarget { Layout = ecsLayout }; logConfig.AddRule(LogLevel.Trace, LogLevel.Fatal, memoryTarget); logConfig.DefaultCultureInfo = System.Globalization.CultureInfo.InvariantCulture; logFactory.Configuration = logConfig; @@ -54,7 +57,6 @@ List GetAndValidateLogEvents() { TestOut.WriteLine(log); Spec.Validate(log); - } return memoryTarget.Logs.ToList(); diff --git a/tests/Elastic.CommonSchema.NLog.Tests/MessageTests.cs b/tests/Elastic.CommonSchema.NLog.Tests/MessageTests.cs index 1e5f4bfe..e51a5bf8 100644 --- a/tests/Elastic.CommonSchema.NLog.Tests/MessageTests.cs +++ b/tests/Elastic.CommonSchema.NLog.Tests/MessageTests.cs @@ -41,7 +41,7 @@ public void SeesMessageWithProp() => TestLogger((logger, getLogEvents) => var ecsEvents = ToEcsEvents(logEvents); var (_, info) = ecsEvents.First(); - info.Message.Should().Be("Info \"X\" 2.2 42"); + info.Message.Should().Be("Info X 2.2 42"); info.Labels.Should().ContainKey("ValueX"); info.Metadata.Should().ContainKey("SomeY"); info.Metadata.Should().NotContainKey("NotX"); @@ -228,7 +228,7 @@ public void SeesMessageWithException() => TestLogger((logger, getLogEvents) => [Fact] public void MetadataWithSameKeys() => TestLogger((logger, getLogEvents) => { - using (MappedDiagnosticsLogicalContext.SetScoped("DupKey", "Mdlc")) + using (ScopeContext.PushProperty("DupKey", "Mdlc")) { logger.Info("Info {DupKey}", "LoggerArg"); @@ -239,7 +239,7 @@ public void MetadataWithSameKeys() => TestLogger((logger, getLogEvents) => var (json, info) = ecsEvents.First(); - info.Message.Should().Be("Info \"LoggerArg\""); + info.Message.Should().Be("Info LoggerArg"); info.Labels.Should().Contain("DupKey", "LoggerArg"); info.Labels.Should().Contain("DupKey_1", "Mdlc"); diff --git a/tests/Elastic.CommonSchema.NLog.Tests/WebTests.cs b/tests/Elastic.CommonSchema.NLog.Tests/WebTests.cs index 4998bdca..ab910a40 100644 --- a/tests/Elastic.CommonSchema.NLog.Tests/WebTests.cs +++ b/tests/Elastic.CommonSchema.NLog.Tests/WebTests.cs @@ -5,6 +5,7 @@ using System; using System.Reflection; using FluentAssertions; +using NLog; using NLog.Config; using Xunit; using Xunit.Abstractions; @@ -17,25 +18,16 @@ public WebTests(ITestOutputHelper output) : base(output) { } private void TestLayout(bool withNLogWeb, Action setup, Action act) { - // Setup basic factory without automatic loading of NLog extensions. - ConfigurationItemFactory.Default = new(typeof(ConfigurationItemFactory).Assembly); - - try - { - if (withNLogWeb) - { - var nlogWebAssemblyName = new AssemblyName("NLog.Web.AspNetCore"); - var nlogWebAssembly = Assembly.Load(nlogWebAssemblyName); - ConfigurationItemFactory.Default.RegisterItemsFromAssembly(nlogWebAssembly); - } - - TestLoggerAndLayout(setup, (layout, logger, events) => act(layout)); - } - finally + if (withNLogWeb) { - // Cleanup for the sake of other tests. - ConfigurationItemFactory.Default = null; + var nlogWebAssemblyName = new AssemblyName("NLog.Web.AspNetCore"); + var nlogWebAssembly = Assembly.Load(nlogWebAssemblyName); +#pragma warning disable CS0618 // Type or member is obsolete + ConfigurationItemFactory.Default.RegisterItemsFromAssembly(nlogWebAssembly); +#pragma warning restore CS0618 // Type or member is obsolete } + + TestLoggerAndLayout(setup, (layout, logger, events) => act(layout)); } [Theory] diff --git a/tests/Elastic.Serilog.Sinks.Tests/SerilogFailureOutputTests.cs b/tests/Elastic.Serilog.Sinks.Tests/SerilogFailureOutputTests.cs index a239ab35..36e4e982 100644 --- a/tests/Elastic.Serilog.Sinks.Tests/SerilogFailureOutputTests.cs +++ b/tests/Elastic.Serilog.Sinks.Tests/SerilogFailureOutputTests.cs @@ -1,9 +1,7 @@ using Elastic.Channels; using Elastic.Channels.Diagnostics; using Elastic.Transport; -using FluentAssertions; using Serilog; -using Serilog.Sinks.TestCorrelator; using Xunit; using DataStreamName = Elastic.Ingest.Elasticsearch.DataStreams.DataStreamName;