diff --git a/build/scripts/Build.fs b/build/scripts/Build.fs
index 79442de1b..bddb5dd61 100644
--- a/build/scripts/Build.fs
+++ b/build/scripts/Build.fs
@@ -21,6 +21,7 @@ open Tooling
module Build =
+ //TODO remove oldDiagnosticSourceVersion
let private oldDiagnosticSourceVersion = SemVer.parse "4.6.0"
let private diagnosticSourceVersion6 = SemVer.parse "6.0.0"
diff --git a/src/Elastic.Apm/Api/IError.cs b/src/Elastic.Apm/Api/IError.cs
index 5bee5569b..d20e5942c 100644
--- a/src/Elastic.Apm/Api/IError.cs
+++ b/src/Elastic.Apm/Api/IError.cs
@@ -12,7 +12,7 @@ namespace Elastic.Apm.Api
/// Represents an error which was captured by the agent.
///
[Specification("error.json")]
- public interface IError
+ public interface IError : IIntakeRoot
{
///
/// The culprit that caused this error.
diff --git a/src/Elastic.Apm/Api/IIntakeRoot.cs b/src/Elastic.Apm/Api/IIntakeRoot.cs
new file mode 100644
index 000000000..0da34b588
--- /dev/null
+++ b/src/Elastic.Apm/Api/IIntakeRoot.cs
@@ -0,0 +1,8 @@
+// Licensed to Elasticsearch B.V under
+// one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+namespace Elastic.Apm.Api;
+
+public interface IIntakeRoot { }
diff --git a/src/Elastic.Apm/Api/IMetricSet.cs b/src/Elastic.Apm/Api/IMetricSet.cs
index 3cc25c7e8..ee862cfeb 100644
--- a/src/Elastic.Apm/Api/IMetricSet.cs
+++ b/src/Elastic.Apm/Api/IMetricSet.cs
@@ -12,7 +12,7 @@ namespace Elastic.Apm.Api
/// Data captured by the agent representing a metric occurring in a monitored service
///
[Specification("metricset.json")]
- public interface IMetricSet
+ public interface IMetricSet : IIntakeRoot
{
///
/// List of captured metrics as key - value pairs
diff --git a/src/Elastic.Apm/Api/ISpan.cs b/src/Elastic.Apm/Api/ISpan.cs
index 6c42f2bab..902fee066 100644
--- a/src/Elastic.Apm/Api/ISpan.cs
+++ b/src/Elastic.Apm/Api/ISpan.cs
@@ -10,7 +10,7 @@ namespace Elastic.Apm.Api
/// An event captured by an agent occurring in a monitored service
///
[Specification("span.json")]
- public interface ISpan : IExecutionSegment
+ public interface ISpan : IExecutionSegment, IIntakeRoot
{
///
/// The action of the span.
diff --git a/src/Elastic.Apm/Api/ITransaction.cs b/src/Elastic.Apm/Api/ITransaction.cs
index 14cd5b61e..8ca4171f0 100644
--- a/src/Elastic.Apm/Api/ITransaction.cs
+++ b/src/Elastic.Apm/Api/ITransaction.cs
@@ -19,7 +19,7 @@ namespace Elastic.Apm.Api
/// provide different transaction implementations.
///
[Specification("transaction.json")]
- public interface ITransaction : IExecutionSegment
+ public interface ITransaction : IExecutionSegment, IIntakeRoot
{
///
/// Contains data related to FaaS (Function as a Service) events.
diff --git a/src/Elastic.Apm/Elastic.Apm.csproj b/src/Elastic.Apm/Elastic.Apm.csproj
index 3570a8165..7844238ba 100644
--- a/src/Elastic.Apm/Elastic.Apm.csproj
+++ b/src/Elastic.Apm/Elastic.Apm.csproj
@@ -68,7 +68,8 @@
-
+
+
diff --git a/src/Elastic.Apm/Ingest/ApmChannel.cs b/src/Elastic.Apm/Ingest/ApmChannel.cs
new file mode 100644
index 000000000..d78780210
--- /dev/null
+++ b/src/Elastic.Apm/Ingest/ApmChannel.cs
@@ -0,0 +1,186 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
+using Elastic.Apm.Api;
+using Elastic.Apm.Logging;
+using Elastic.Apm.Report;
+using Elastic.Channels;
+using Elastic.Ingest.Transport;
+using Elastic.Transport;
+
+namespace Elastic.Apm.Ingest;
+
+#nullable enable
+internal static class ApmChannelStatics
+{
+ public static readonly byte[] LineFeed = { (byte)'\n' };
+
+ public static readonly DefaultRequestParameters RequestParams = new()
+ {
+ RequestConfiguration = new RequestConfiguration { ContentType = "application/x-ndjson" }
+ };
+
+ public static readonly JsonSerializerOptions SerializerOptions = new()
+ {
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ MaxDepth = 64,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ };
+}
+
+///
+/// An implementation that sends V2 intake API data
+/// to APM server.
+///
+public class ApmChannel
+ : TransportChannelBase
+ , IPayloadSender
+{
+ private readonly List> _transactionFilters = new();
+ private readonly List> _spanFilters = new();
+ private readonly List> _errorFilters = new();
+
+ ///
+ public ApmChannel(ApmChannelOptions options, IApmLogger? logger = null) : base(options) =>
+ PayloadSenderV2.SetUpFilters(_transactionFilters, _spanFilters, _errorFilters, null, logger ?? new TraceLogger(LogLevel.Trace));
+
+ public IError? Filter(IError error) => _errorFilters.Aggregate(error, (current, filter) => filter(current)!);
+
+ public ISpan? Filter(ISpan span) => _spanFilters.Aggregate(span, (current, filter) => filter(current)!);
+
+ public ITransaction? Filter(ITransaction span) => _transactionFilters.Aggregate(span, (current, filter) => filter(current)!);
+
+ public bool TryFilter(IError error, [NotNullWhen(true)] out IError? filtered)
+ {
+ filtered = _errorFilters.Select(f => f(error)).TakeWhile(e => e != null).LastOrDefault();
+ return filtered != null;
+ }
+
+ public bool TryFilter(ISpan span, [NotNullWhen(true)] out ISpan? filtered)
+ {
+ filtered = _spanFilters.Select(f => f(span)).TakeWhile(e => e != null).LastOrDefault();
+ return filtered != null;
+ }
+
+ public bool TryFilter(ITransaction transaction, [NotNullWhen(true)] out ITransaction? filtered)
+ {
+ filtered = _transactionFilters.Select(f => f(transaction)).TakeWhile(e => e != null).LastOrDefault();
+ return filtered != null;
+ }
+
+ public virtual void QueueMetrics(IMetricSet metrics) => TryWrite(metrics);
+
+ public virtual void QueueError(IError error)
+ {
+ if (TryFilter(error, out var e))
+ TryWrite(e);
+ }
+
+ public virtual void QueueSpan(ISpan span)
+ {
+ if (TryFilter(span, out var s))
+ TryWrite(s);
+ }
+
+ public virtual void QueueTransaction(ITransaction transaction)
+ {
+ if (TryFilter(transaction, out var t))
+ TryWrite(t);
+ }
+
+ //retry if APM server returns 429
+ ///
+ protected override bool Retry(EventIntakeResponse response) => response.ApiCallDetails.HttpStatusCode == 429;
+
+ ///
+ protected override bool RetryAllItems(EventIntakeResponse response) => response.ApiCallDetails.HttpStatusCode == 429;
+
+ //APM does not return the status for all events sent. Therefor we always return an empty set for individual items to retry
+ ///
+ protected override List<(IIntakeRoot, IntakeErrorItem)> Zip(EventIntakeResponse response, IReadOnlyCollection page) =>
+ _emptyZip;
+
+ private readonly List<(IIntakeRoot, IntakeErrorItem)> _emptyZip = new();
+
+ ///
+ protected override bool RetryEvent((IIntakeRoot, IntakeErrorItem) @event) => false;
+
+ ///
+ protected override bool RejectEvent((IIntakeRoot, IntakeErrorItem) @event) => false;
+
+ ///
+ protected override Task
+ ExportAsync(HttpTransport transport, ArraySegment page, CancellationToken ctx = default) =>
+ transport.RequestAsync(HttpMethod.POST, "/intake/v2/events",
+ PostData.StreamHandler(page,
+ (_, _) =>
+ {
+ /* NOT USED */
+ },
+ async (b, stream, ctx) => { await WriteBufferToStreamAsync(b, stream, ctx).ConfigureAwait(false); })
+ , ApmChannelStatics.RequestParams, ctx);
+
+ private async Task WriteStanzaToStreamAsync(Stream stream, CancellationToken ctx)
+ {
+ // {"metadata":{"process":{"pid":1234,"title":"/usr/lib/jvm/java-10-openjdk-amd64/bin/java","ppid":1,"argv":["-v"]},
+ // "system":{"architecture":"amd64","detected_hostname":"8ec7ceb99074","configured_hostname":"host1","platform":"Linux","container":{"id":"8ec7ceb990749e79b37f6dc6cd3628633618d6ce412553a552a0fa6b69419ad4"},
+ // "kubernetes":{"namespace":"default","pod":{"uid":"b17f231da0ad128dc6c6c0b2e82f6f303d3893e3","name":"instrumented-java-service"},"node":{"name":"node-name"}}},
+ // "service":{"name":"1234_service-12a3","version":"4.3.0","node":{"configured_name":"8ec7ceb990749e79b37f6dc6cd3628633618d6ce412553a552a0fa6b69419ad4"},"environment":"production","language":{"name":"Java","version":"10.0.2"},
+ // "agent":{"version":"1.10.0","name":"java","ephemeral_id":"e71be9ac-93b0-44b9-a997-5638f6ccfc36"},"framework":{"name":"spring","version":"5.0.0"},"runtime":{"name":"Java","version":"10.0.2"}},"labels":{"group":"experimental","ab_testing":true,"segment":5}}}
+ // TODO cache
+ var p = Process.GetCurrentProcess();
+ var metadata = new
+ {
+ metadata = new
+ {
+ process = new { pid = p.Id, title = p.ProcessName },
+ service = new
+ {
+ name = System.Text.RegularExpressions.Regex.Replace(p.ProcessName, "[^a-zA-Z0-9 _-]", "_"),
+ version = "1.0.0",
+ agent = new { name = "dotnet", version = "0.0.1" }
+ }
+ }
+ };
+ await JsonSerializer.SerializeAsync(stream, metadata, metadata.GetType(), ApmChannelStatics.SerializerOptions, ctx)
+ .ConfigureAwait(false);
+ await stream.WriteAsync(ApmChannelStatics.LineFeed, 0, 1, ctx).ConfigureAwait(false);
+ }
+
+ private async Task WriteBufferToStreamAsync(IReadOnlyCollection b, Stream stream, CancellationToken ctx)
+ {
+ await WriteStanzaToStreamAsync(stream, ctx).ConfigureAwait(false);
+ foreach (var @event in b)
+ {
+ if (@event == null)
+ continue;
+
+ var type = @event switch
+ {
+ ITransaction => "transaction",
+ ISpan => "span",
+ IError => "error",
+ IMetricSet => "metricset",
+ _ => "unknown"
+ };
+ var dictionary = new Dictionary() { { type, @event } };
+
+ await JsonSerializer.SerializeAsync(stream, dictionary, dictionary.GetType(), ApmChannelStatics.SerializerOptions, ctx)
+ .ConfigureAwait(false);
+
+ await stream.WriteAsync(ApmChannelStatics.LineFeed, 0, 1, ctx).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/src/Elastic.Apm/Ingest/ApmChannelOptions.cs b/src/Elastic.Apm/Ingest/ApmChannelOptions.cs
new file mode 100644
index 000000000..f86e52490
--- /dev/null
+++ b/src/Elastic.Apm/Ingest/ApmChannelOptions.cs
@@ -0,0 +1,25 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System;
+using Elastic.Apm.Api;
+using Elastic.Ingest.Transport;
+using Elastic.Transport;
+
+namespace Elastic.Apm.Ingest;
+
+///
+/// Channel options for
+///
+public class ApmChannelOptions : TransportChannelOptionsBase
+{
+ ///
+ private ApmChannelOptions(HttpTransport transport) : base(transport) { }
+
+ public ApmChannelOptions(Uri serverEndpoint, TransportClient transportClient = null)
+ : this(new DefaultHttpTransport(new TransportConfiguration(new SingleNodePool(serverEndpoint), connection: transportClient!)))
+ {
+
+ }
+}
diff --git a/src/Elastic.Apm/Ingest/IngestResponse.cs b/src/Elastic.Apm/Ingest/IngestResponse.cs
new file mode 100644
index 000000000..c8321af9e
--- /dev/null
+++ b/src/Elastic.Apm/Ingest/IngestResponse.cs
@@ -0,0 +1,34 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Elastic.Transport;
+
+namespace Elastic.Apm.Ingest;
+
+///
+public class EventIntakeResponse : TransportResponse
+{
+ ///
+ [JsonPropertyName("accepted")]
+ public long Accepted { get; set; }
+
+ ///
+ [JsonPropertyName("errors")]
+ //[JsonConverter(typeof(ResponseItemsConverter))]
+ public IReadOnlyCollection Errors { get; set; } = null!;
+}
+
+///
+public class IntakeErrorItem
+{
+ ///
+ [JsonPropertyName("message")]
+ public string Message { get; set; } = null!;
+
+ ///
+ [JsonPropertyName("document")]
+ public string Document { get; set; } = null!;
+}
diff --git a/src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/NullableAttributes.cs b/src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/NullableAttributes.cs
index 0816c1e8c..560d01ade 100644
--- a/src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/NullableAttributes.cs
+++ b/src/Elastic.Apm/Libraries/Newtonsoft.Json/Utilities/NullableAttributes.cs
@@ -26,6 +26,7 @@
#endregion
#nullable enable
+#if !NET6_0_OR_GREATER
namespace System.Diagnostics.CodeAnalysis
{
/// Specifies that an output will not be null even if the corresponding type allows it.
@@ -78,3 +79,4 @@ internal class DoesNotReturnIfAttribute : Attribute
public bool ParameterValue { get; }
}
}
+#endif
diff --git a/src/Elastic.Ingest.Apm/Elastic.Ingest.Apm.csproj b/src/Elastic.Ingest.Apm/Elastic.Ingest.Apm.csproj
new file mode 100644
index 000000000..fe7a4ff17
--- /dev/null
+++ b/src/Elastic.Ingest.Apm/Elastic.Ingest.Apm.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj b/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj
index 109cdb28d..6fa722a09 100644
--- a/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj
+++ b/src/startuphook/Elastic.Apm.StartupHook.Loader/Elastic.Apm.StartupHook.Loader.csproj
@@ -1,8 +1,7 @@
-
- netcoreapp2.2
+ netstandard2.0
false
@@ -19,5 +18,9 @@
StartupHookLogger.cs
+
+
+
+
diff --git a/test/Elastic.Apm.Tests.Utilities/MockPayloadSender.cs b/test/Elastic.Apm.Tests.Utilities/MockPayloadSender.cs
index 715ecf737..8a5dfd87f 100644
--- a/test/Elastic.Apm.Tests.Utilities/MockPayloadSender.cs
+++ b/test/Elastic.Apm.Tests.Utilities/MockPayloadSender.cs
@@ -10,33 +10,34 @@
using System.Linq;
using System.Threading;
using Elastic.Apm.Api;
+using Elastic.Apm.Ingest;
using Elastic.Apm.Libraries.Newtonsoft.Json.Linq;
using Elastic.Apm.Logging;
using Elastic.Apm.Metrics;
using Elastic.Apm.Model;
using Elastic.Apm.Report;
+using Elastic.Transport;
using FluentAssertions;
+#nullable enable
namespace Elastic.Apm.Tests.Utilities
{
- internal class MockPayloadSender : IPayloadSender
+ internal class MockPayloadSender : ApmChannel
{
private static readonly JObject JsonSpanTypesData =
JObject.Parse(File.ReadAllText("./TestResources/json-specs/span_types.json"));
- private readonly List _errors = new List();
- private readonly List> _errorFilters = new List>();
- private readonly object _spanLock = new object();
- private readonly object _transactionLock = new object();
- private readonly object _metricsLock = new object();
- private readonly object _errorLock = new object();
- private readonly List _metrics = new List();
- private readonly List> _spanFilters = new List>();
- private readonly List _spans = new List();
- private readonly List> _transactionFilters = new List>();
- private readonly List _transactions = new List();
-
- public MockPayloadSender(IApmLogger logger = null)
+ private readonly object _spanLock = new();
+ private readonly object _transactionLock = new();
+ private readonly object _metricsLock = new();
+ private readonly object _errorLock = new();
+ private readonly List _metrics = new();
+ private readonly List _errors = new();
+ private readonly List _spans = new();
+ private readonly List _transactions = new();
+
+ public MockPayloadSender(IApmLogger? logger = null)
+ : base(new ApmChannelOptions(new Uri("http://localhost:8080"), transportClient: new InMemoryConnection()), logger)
{
_waitHandles = new[] { new AutoResetEvent(false), new AutoResetEvent(false), new AutoResetEvent(false), new AutoResetEvent(false) };
@@ -45,7 +46,6 @@ public MockPayloadSender(IApmLogger logger = null)
_errorWaitHandle = _waitHandles[2];
_metricSetWaitHandle = _waitHandles[3];
- PayloadSenderV2.SetUpFilters(_transactionFilters, _spanFilters, _errorFilters, MockApmServerInfo.Version710, logger ?? new NoopLogger());
}
///
@@ -61,6 +61,58 @@ public MockPayloadSender(IApmLogger logger = null)
private readonly AutoResetEvent[] _waitHandles;
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromMinutes(1);
+ public override bool TryWrite(IIntakeRoot item)
+ {
+ var written = base.TryWrite(item);
+ switch (item)
+ {
+ case IError error:
+ _errors.Add(error);
+ _errorWaitHandle.Set();
+ break;
+ case ITransaction transaction:
+ _transactions.Add(transaction);
+ _transactionWaitHandle.Set();
+ break;
+ case ISpan span:
+ _spans.Add(span);
+ _spanWaitHandle.Set();
+ break;
+ case IMetricSet metricSet:
+ _metrics.Add(metricSet);
+ _metricSetWaitHandle.Set();
+ break;
+ }
+ return written;
+ }
+
+ public override void QueueError(IError error)
+ {
+ lock (_errorLock)
+ base.QueueError(error);
+ }
+
+ public override void QueueTransaction(ITransaction transaction)
+ {
+ lock (_transactionLock)
+ base.QueueTransaction(transaction);
+ }
+
+ public override void QueueSpan(ISpan span)
+ {
+ VerifySpan(span);
+ lock (_spanLock)
+ base.QueueSpan(span);
+ }
+
+ public override void QueueMetrics(IMetricSet metricSet)
+ {
+ lock (_metricsLock)
+ base.QueueMetrics(metricSet);
+ }
+
+
+
///
/// Waits for any events to be queued
///
@@ -191,19 +243,19 @@ public IReadOnlyList Errors
get
{
lock (_errorLock)
- return CreateImmutableSnapshot(_errors);
+ return CreateImmutableSnapshot(_errors);
}
}
- public Error FirstError => Errors.FirstOrDefault() as Error;
- public MetricSet FirstMetric => Metrics.FirstOrDefault() as MetricSet;
+ public Error? FirstError => Errors.FirstOrDefault() as Error;
+ public MetricSet? FirstMetric => Metrics.FirstOrDefault() as MetricSet;
///
/// The 1. Span on the 1. Transaction
///
- public Span FirstSpan => Spans.FirstOrDefault() as Span;
+ public Span? FirstSpan => Spans.FirstOrDefault() as Span;
- public Transaction FirstTransaction =>
+ public Transaction? FirstTransaction =>
Transactions.FirstOrDefault() as Transaction;
public IReadOnlyList Metrics
@@ -211,7 +263,7 @@ public IReadOnlyList Metrics
get
{
lock (_metricsLock)
- return CreateImmutableSnapshot(_metrics);
+ return CreateImmutableSnapshot(_metrics);
}
}
@@ -220,7 +272,7 @@ public IReadOnlyList Spans
get
{
lock (_spanLock)
- return CreateImmutableSnapshot(_spans);
+ return CreateImmutableSnapshot(_spans);
}
}
@@ -229,45 +281,15 @@ public IReadOnlyList Transactions
get
{
lock (_transactionLock)
- return CreateImmutableSnapshot(_transactions);
+ return CreateImmutableSnapshot(_transactions);
}
}
public Span[] SpansOnFirstTransaction =>
- Spans.Where(n => n.TransactionId == Transactions.First().Id).Select(n => n as Span).ToArray();
-
- public void QueueError(IError error)
- {
- lock (_errorLock)
- {
- error = _errorFilters.Aggregate(error,
- (current, filter) => filter(current));
- _errors.Add(error);
- _errorWaitHandle.Set();
- }
- }
-
- public virtual void QueueTransaction(ITransaction transaction)
- {
- lock (_transactionLock)
- {
- transaction = _transactionFilters.Aggregate(transaction,
- (current, filter) => filter(current));
- _transactions.Add(transaction);
- _transactionWaitHandle.Set();
- }
- }
-
- public void QueueSpan(ISpan span)
- {
- VerifySpan(span);
- lock (_spanLock)
- {
- span = _spanFilters.Aggregate(span, (current, filter) => filter(current));
- _spans.Add(span);
- _spanWaitHandle.Set();
- }
- }
+ Spans
+ .Where(n => n.TransactionId == Transactions.First().Id)
+ .Select(n => (Span)n)
+ .ToArray();
private void VerifySpan(ISpan span)
{
@@ -279,7 +301,7 @@ private void VerifySpan(ISpan span)
var spanTypeInfo = JsonSpanTypesData[type] as JObject;
spanTypeInfo.Should().NotBeNull($"span type '{type}' is not allowed by the spec");
- var allowNullSubtype = spanTypeInfo["allow_null_subtype"]?.Value();
+ var allowNullSubtype = spanTypeInfo!["allow_null_subtype"]?.Value();
var allowUnlistedSubtype = spanTypeInfo["allow_unlisted_subtype"]?.Value();
var subTypes = spanTypeInfo["subtypes"];
var hasSubtypes = subTypes != null && subTypes.Any();
@@ -289,7 +311,7 @@ private void VerifySpan(ISpan span)
{
if (!allowUnlistedSubtype.GetValueOrDefault() && hasSubtypes)
{
- var subTypeInfo = subTypes[subType];
+ var subTypeInfo = subTypes![subType];
subTypeInfo.Should()
.NotBeNull($"span subtype '{subType}' is not allowed by the spec for type '{type}'");
}
@@ -305,15 +327,6 @@ private void VerifySpan(ISpan span)
}
}
- public void QueueMetrics(IMetricSet metricSet)
- {
- lock (_metricsLock)
- {
- _metrics.Add(metricSet);
- _metricSetWaitHandle.Set();
- }
- }
-
public void Clear()
{
lock (_spanLock)
diff --git a/test/azure/applications/Elastic.AzureFunctionApp.InProcess/Elastic.AzureFunctionApp.InProcess.csproj b/test/azure/applications/Elastic.AzureFunctionApp.InProcess/Elastic.AzureFunctionApp.InProcess.csproj
index a6d937ee1..c7cf2039b 100644
--- a/test/azure/applications/Elastic.AzureFunctionApp.InProcess/Elastic.AzureFunctionApp.InProcess.csproj
+++ b/test/azure/applications/Elastic.AzureFunctionApp.InProcess/Elastic.AzureFunctionApp.InProcess.csproj
@@ -8,7 +8,7 @@
false
-
+
diff --git a/test/azure/applications/Elastic.AzureFunctionApp.Isolated/Elastic.AzureFunctionApp.Isolated.csproj b/test/azure/applications/Elastic.AzureFunctionApp.Isolated/Elastic.AzureFunctionApp.Isolated.csproj
index 766752f4b..1b477734a 100644
--- a/test/azure/applications/Elastic.AzureFunctionApp.Isolated/Elastic.AzureFunctionApp.Isolated.csproj
+++ b/test/azure/applications/Elastic.AzureFunctionApp.Isolated/Elastic.AzureFunctionApp.Isolated.csproj
@@ -8,11 +8,11 @@
false
-
-
+
+
-
-
+
+
diff --git a/test/opentelemetry/Elastic.Apm.OpenTelemetry.Tests/OpenTelemetryTests.cs b/test/opentelemetry/Elastic.Apm.OpenTelemetry.Tests/OpenTelemetryTests.cs
index 1537eb25d..da3c2103c 100644
--- a/test/opentelemetry/Elastic.Apm.OpenTelemetry.Tests/OpenTelemetryTests.cs
+++ b/test/opentelemetry/Elastic.Apm.OpenTelemetry.Tests/OpenTelemetryTests.cs
@@ -24,10 +24,10 @@ public void MixApisTest1()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.Sample2(agent.Tracer);
- payloadSender.FirstTransaction.Name.Should().Be("Sample2");
+ payloadSender.FirstTransaction!.Name.Should().Be("Sample2");
payloadSender.Spans.Should().HaveCount(2);
- payloadSender.FirstSpan.Name.Should().Be("foo");
+ payloadSender.FirstSpan!.Name.Should().Be("foo");
payloadSender.Spans.ElementAt(1).Name.Should().Be("ElasticApmSpan");
payloadSender.FirstSpan.ParentId.Should().Be(payloadSender.FirstTransaction.Id);
@@ -39,7 +39,7 @@ public void MixApisTest1()
private void AssertOnTraceIds(MockPayloadSender payloadSender)
{
foreach (var span in payloadSender.Spans)
- span.TraceId.Should().Be(payloadSender.FirstTransaction.TraceId);
+ span.TraceId.Should().Be(payloadSender.FirstTransaction!.TraceId);
}
[Fact]
@@ -50,10 +50,10 @@ public void MixApisTest2()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.Sample3(agent.Tracer);
- payloadSender.FirstTransaction.Name.Should().Be("Sample3");
+ payloadSender.FirstTransaction!.Name.Should().Be("Sample3");
payloadSender.Spans.Should().HaveCount(2);
- payloadSender.FirstSpan.Name.Should().Be("ElasticApmSpan");
+ payloadSender.FirstSpan!.Name.Should().Be("ElasticApmSpan");
payloadSender.Spans.ElementAt(1).Name.Should().Be("foo");
payloadSender.Spans.ElementAt(1).ParentId.Should().Be(payloadSender.FirstTransaction.Id);
@@ -72,10 +72,10 @@ public void MixApisTest3()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.Sample4(agent.Tracer);
- payloadSender.FirstTransaction.Name.Should().Be("Sample4");
+ payloadSender.FirstTransaction!.Name.Should().Be("Sample4");
payloadSender.Spans.Should().HaveCount(2);
- payloadSender.FirstSpan.Name.Should().Be("ElasticApmSpan");
+ payloadSender.FirstSpan!.Name.Should().Be("ElasticApmSpan");
payloadSender.Spans.ElementAt(1).Name.Should().Be("foo");
payloadSender.Spans.ElementAt(1).ParentId.Should().Be(payloadSender.FirstTransaction.Id);
@@ -92,7 +92,7 @@ public void TestOtelFieldsWith1Span()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.OneSpanWithAttributes();
- payloadSender.FirstTransaction.Name.Should().Be("foo");
+ payloadSender.FirstTransaction!.Name.Should().Be("foo");
payloadSender.FirstTransaction.Otel.Should().NotBeNull();
payloadSender.FirstTransaction.Otel.SpanKind.Should().Be("Server");
payloadSender.FirstTransaction.Otel.Attributes.Should().NotBeNull();
@@ -107,13 +107,13 @@ public void TestOtelFieldsWith3Spans()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.TwoSpansWithAttributes();
- payloadSender.FirstTransaction.Name.Should().Be("foo");
+ payloadSender.FirstTransaction!.Name.Should().Be("foo");
payloadSender.FirstTransaction.Otel.Should().NotBeNull();
payloadSender.FirstTransaction.Otel.SpanKind.Should().Be("Server");
payloadSender.FirstTransaction.Otel.Attributes.Should().NotBeNull();
payloadSender.FirstTransaction.Otel.Attributes.Should().Contain("foo1", "bar1");
- payloadSender.FirstSpan.Name.Should().Be("bar");
+ payloadSender.FirstSpan!.Name.Should().Be("bar");
payloadSender.FirstSpan.Otel.Should().NotBeNull();
payloadSender.FirstSpan.Otel.SpanKind.Should().Be("Internal");
payloadSender.FirstSpan.Otel.Attributes.Should().NotBeNull();
@@ -128,7 +128,7 @@ public void SpanKindTests()
configuration: new MockConfiguration(openTelemetryBridgeEnabled: "true"))))
OTSamples.SpanKindSample();
- payloadSender.FirstSpan.Type.Should().Be(ApiConstants.TypeExternal);
+ payloadSender.FirstSpan!.Type.Should().Be(ApiConstants.TypeExternal);
payloadSender.FirstSpan.Subtype.Should().Be(ApiConstants.SubtypeHttp);
payloadSender.Spans.ElementAt(1).Type.Should().Be(ApiConstants.TypeDb);
@@ -178,7 +178,7 @@ public void DistributedTracingTest()
payloadSender.WaitForTransactions(count: 2);
- payloadSender.FirstTransaction.TraceId.Should()
+ payloadSender.FirstTransaction!.TraceId.Should()
.Be(payloadSender.Transactions[1].TraceId, because: "The transactions should be under the same trace.");
}
@@ -196,7 +196,7 @@ public void ActivityAndTransactionTraceIdSynced()
using (var activity = src.StartActivity("foo", ActivityKind.Server))
traceId = activity?.TraceId.ToString();
traceId.Should().NotBeNull();
- payloadSender.FirstTransaction.TraceId.Should().Be(traceId);
+ payloadSender.FirstTransaction!.TraceId.Should().Be(traceId);
}
[Fact]