OtlpExporter peer.service resolution (#1392)
* Refactor peer service logic into a shared location. Support building peer.service tag in OtlpExporter. * Added unit tests for OtlpExporter peer service support. Refactored a few things around to resolve types ending up in multiple assemblies. * Refactored Zipkin to use the new PeerServiceResolver. * Updated changelog. Co-authored-by: Eddy Nakamura <ednakamu@microsoft.com> Co-authored-by: Cijo Thomas <cithomas@microsoft.com>
This commit is contained in:
parent
6e7e4b7ed4
commit
41cd4f5d9c
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry.Shims.OpenTracing.Tests" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
|
||||
if (tracestate.Count > MaxKeyValuePairsCount)
|
||||
{
|
||||
LogTooManyItemsInTracestate();
|
||||
OpenTelemetryApiEventSource.Log.TooManyItemsInTracestate();
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -106,11 +106,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if API
|
||||
OpenTelemetryApiEventSource.Log.TracestateExtractException(ex);
|
||||
#else
|
||||
OpenTelemetrySdkEventSource.Log.TracestateExtractException(ex);
|
||||
#endif
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -128,7 +124,7 @@ namespace OpenTelemetry.Context.Propagation
|
|||
var pairsCount = traceState.Count();
|
||||
if (pairsCount > MaxKeyValuePairsCount)
|
||||
{
|
||||
LogTooManyItemsInTracestate();
|
||||
OpenTelemetryApiEventSource.Log.TooManyItemsInTracestate();
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
|
@ -169,14 +165,14 @@ namespace OpenTelemetry.Context.Propagation
|
|||
key = pair.Slice(0, keyEndIdx).TrimStart();
|
||||
if (!ValidateKey(key))
|
||||
{
|
||||
LogKeyIsInvalid(key);
|
||||
OpenTelemetryApiEventSource.Log.TracestateKeyIsInvalid(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
value = pair.Slice(valueStartIdx, pair.Length - valueStartIdx).Trim();
|
||||
if (!ValidateValue(value))
|
||||
{
|
||||
LogValueIsInvalid(value);
|
||||
OpenTelemetryApiEventSource.Log.TracestateValueIsInvalid(value);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -273,32 +269,5 @@ namespace OpenTelemetry.Context.Propagation
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void LogTooManyItemsInTracestate()
|
||||
{
|
||||
#if API
|
||||
OpenTelemetryApiEventSource.Log.TooManyItemsInTracestate();
|
||||
#else
|
||||
OpenTelemetrySdkEventSource.Log.TooManyItemsInTracestate();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void LogKeyIsInvalid(ReadOnlySpan<char> key)
|
||||
{
|
||||
#if API
|
||||
OpenTelemetryApiEventSource.Log.TracestateKeyIsInvalid(key);
|
||||
#else
|
||||
OpenTelemetrySdkEventSource.Log.TracestateKeyIsInvalid(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void LogValueIsInvalid(ReadOnlySpan<char> value)
|
||||
{
|
||||
#if API
|
||||
OpenTelemetryApiEventSource.Log.TracestateValueIsInvalid(value);
|
||||
#else
|
||||
OpenTelemetrySdkEventSource.Log.TracestateValueIsInvalid(value);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,8 @@
|
|||
<TargetFrameworks>net452;netstandard2.0</TargetFrameworks>
|
||||
<Description>OpenTelemetry .NET API</Description>
|
||||
<RootNamespace>OpenTelemetry</RootNamespace>
|
||||
<DefineConstants>$(DefineConstants);API</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\OpenTelemetry\Internal\EnumerationHelper.cs" Link="Internal\EnumerationHelper.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticSourcePkgVer)" />
|
||||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(SystemReflectionEmitLightweightPkgVer)" />
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
|
@ -42,15 +43,6 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
|
|||
private const long TicksPerMicrosecond = TimeSpan.TicksPerMillisecond / 1000;
|
||||
private const long UnixEpochMicroseconds = UnixEpochTicks / TicksPerMicrosecond; // 62,135,596,800,000,000
|
||||
|
||||
private static readonly Dictionary<string, int> PeerServiceKeyResolutionDictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[SemanticConventions.AttributePeerService] = 0, // priority 0 (highest).
|
||||
["peer.hostname"] = 1,
|
||||
["peer.address"] = 1,
|
||||
[SemanticConventions.AttributeHttpHost] = 2, // peer.service for Http.
|
||||
[SemanticConventions.AttributeDbInstance] = 2, // peer.service for Redis.
|
||||
};
|
||||
|
||||
public static JaegerSpan ToJaegerSpan(this Activity activity)
|
||||
{
|
||||
var jaegerTags = new TagEnumerationState
|
||||
|
|
@ -63,29 +55,9 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
|
|||
string peerServiceName = null;
|
||||
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
|
||||
{
|
||||
// If priority = 0 that means peer.service may have already been included in tags
|
||||
var addPeerServiceTag = jaegerTags.PeerServicePriority > 0;
|
||||
PeerServiceResolver.Resolve(ref jaegerTags, out peerServiceName, out bool addAsTag);
|
||||
|
||||
var hostNameOrIpAddress = jaegerTags.HostName ?? jaegerTags.IpAddress;
|
||||
|
||||
// peer.service has not already been included, but net.peer.name/ip and optionally net.peer.port are present
|
||||
if ((jaegerTags.PeerService == null || addPeerServiceTag)
|
||||
&& hostNameOrIpAddress != null)
|
||||
{
|
||||
peerServiceName = jaegerTags.Port == default
|
||||
? hostNameOrIpAddress
|
||||
: $"{hostNameOrIpAddress}:{jaegerTags.Port}";
|
||||
|
||||
// Add the peer.service tag
|
||||
addPeerServiceTag = true;
|
||||
}
|
||||
|
||||
if (peerServiceName == null && jaegerTags.PeerService != null)
|
||||
{
|
||||
peerServiceName = jaegerTags.PeerService;
|
||||
}
|
||||
|
||||
if (peerServiceName != null && addPeerServiceTag)
|
||||
if (peerServiceName != null && addAsTag)
|
||||
{
|
||||
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag(SemanticConventions.AttributePeerService, JaegerTagType.STRING, vStr: peerServiceName));
|
||||
}
|
||||
|
|
@ -274,50 +246,34 @@ namespace OpenTelemetry.Exporter.Jaeger.Implementation
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void ProcessJaegerTag(ref TagEnumerationState state, string key, JaegerTag jaegerTag)
|
||||
{
|
||||
if (jaegerTag.VStr != null)
|
||||
{
|
||||
if (PeerServiceKeyResolutionDictionary.TryGetValue(key, out int priority)
|
||||
&& (state.PeerService == null || priority < state.PeerServicePriority))
|
||||
{
|
||||
state.PeerService = jaegerTag.VStr;
|
||||
state.PeerServicePriority = priority;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerName)
|
||||
{
|
||||
state.HostName = jaegerTag.VStr;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerIp)
|
||||
{
|
||||
state.IpAddress = jaegerTag.VStr;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerPort && long.TryParse(jaegerTag.VStr, out var port))
|
||||
{
|
||||
state.Port = port;
|
||||
}
|
||||
PeerServiceResolver.InspectTag(ref state, key, jaegerTag.VStr);
|
||||
}
|
||||
else if (jaegerTag.VLong.HasValue && key == SemanticConventions.AttributeNetPeerPort)
|
||||
else if (jaegerTag.VLong.HasValue)
|
||||
{
|
||||
state.Port = jaegerTag.VLong.Value;
|
||||
PeerServiceResolver.InspectTag(ref state, key, jaegerTag.VLong.Value);
|
||||
}
|
||||
|
||||
PooledList<JaegerTag>.Add(ref state.Tags, jaegerTag);
|
||||
}
|
||||
|
||||
private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>
|
||||
private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>, PeerServiceResolver.IPeerServiceState
|
||||
{
|
||||
public PooledList<JaegerTag> Tags;
|
||||
|
||||
public string PeerService;
|
||||
public string PeerService { get; set; }
|
||||
|
||||
public int PeerServicePriority;
|
||||
public int? PeerServicePriority { get; set; }
|
||||
|
||||
public string HostName;
|
||||
public string HostName { get; set; }
|
||||
|
||||
public string IpAddress;
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public long Port;
|
||||
public long Port { get; set; }
|
||||
|
||||
public bool ForEach(KeyValuePair<string, object> activityTag)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,9 +11,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Internal\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Trace\SemanticConventions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Implementation\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Trace\SemanticConventions.cs" Link="Includes\SemanticConventions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Exporter\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
* `peer.service` tag is now added to outgoing spans (went not already specified)
|
||||
following the [Zipkin remote endpoint
|
||||
rules](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk_exporters/zipkin.md#remote-endpoint)
|
||||
([#1392](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1392))
|
||||
|
||||
## 0.7.0-beta.1
|
||||
|
||||
Released 2020-Oct-16
|
||||
|
|
|
|||
|
|
@ -181,6 +181,23 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
|
||||
TagEnumerationState otlpTags = default;
|
||||
activity.EnumerateTags(ref otlpTags);
|
||||
|
||||
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
|
||||
{
|
||||
PeerServiceResolver.Resolve(ref otlpTags, out string peerServiceName, out bool addAsTag);
|
||||
|
||||
if (peerServiceName != null && addAsTag)
|
||||
{
|
||||
PooledList<OtlpCommon.KeyValue>.Add(
|
||||
ref otlpTags.Tags,
|
||||
new OtlpCommon.KeyValue
|
||||
{
|
||||
Key = SemanticConventions.AttributePeerService,
|
||||
Value = new OtlpCommon.AnyValue { StringValue = peerServiceName },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (otlpTags.Created)
|
||||
{
|
||||
otlpSpan.Attributes.AddRange(otlpTags.Tags);
|
||||
|
|
@ -435,7 +452,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
return new OtlpCommon.KeyValue { Key = key, Value = value };
|
||||
}
|
||||
|
||||
private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>
|
||||
private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>, PeerServiceResolver.IPeerServiceState
|
||||
{
|
||||
public bool Created;
|
||||
|
||||
|
|
@ -445,6 +462,16 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
|
||||
public string StatusDescription;
|
||||
|
||||
public string PeerService { get; set; }
|
||||
|
||||
public int? PeerServicePriority { get; set; }
|
||||
|
||||
public string HostName { get; set; }
|
||||
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public long Port { get; set; }
|
||||
|
||||
public bool ForEach(KeyValuePair<string, object> activityTag)
|
||||
{
|
||||
if (activityTag.Value == null)
|
||||
|
|
@ -452,7 +479,9 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
return true;
|
||||
}
|
||||
|
||||
switch (activityTag.Key)
|
||||
var key = activityTag.Key;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
case SpanAttributeConstants.StatusCodeKey:
|
||||
this.StatusCode = activityTag.Value as int?;
|
||||
|
|
@ -471,50 +500,52 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation
|
|||
switch (activityTag.Value)
|
||||
{
|
||||
case string s:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { StringValue = s }));
|
||||
PeerServiceResolver.InspectTag(ref this, key, s);
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { StringValue = s }));
|
||||
break;
|
||||
case bool b:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { BoolValue = b }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { BoolValue = b }));
|
||||
break;
|
||||
case int i:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { IntValue = i }));
|
||||
PeerServiceResolver.InspectTag(ref this, key, i);
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { IntValue = i }));
|
||||
break;
|
||||
case long l:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { IntValue = l }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { IntValue = l }));
|
||||
break;
|
||||
case double d:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { DoubleValue = d }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { DoubleValue = d }));
|
||||
break;
|
||||
case int[] intArray:
|
||||
foreach (var item in intArray)
|
||||
{
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { IntValue = item }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { IntValue = item }));
|
||||
}
|
||||
|
||||
break;
|
||||
case double[] doubleArray:
|
||||
foreach (var item in doubleArray)
|
||||
{
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { DoubleValue = item }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { DoubleValue = item }));
|
||||
}
|
||||
|
||||
break;
|
||||
case bool[] boolArray:
|
||||
foreach (var item in boolArray)
|
||||
{
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { BoolValue = item }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { BoolValue = item }));
|
||||
}
|
||||
|
||||
break;
|
||||
case string[] stringArray:
|
||||
foreach (var item in stringArray)
|
||||
{
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { StringValue = item }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { StringValue = item }));
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(activityTag.Key, new OtlpCommon.AnyValue { StringValue = activityTag.Value.ToString() }));
|
||||
PooledList<OtlpCommon.KeyValue>.Add(ref this.Tags, CreateOtlpKeyValue(key, new OtlpCommon.AnyValue { StringValue = activityTag.Value.ToString() }));
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@
|
|||
<PackageReference Include="Grpc" Version="$(GrpcPkgVer)" />
|
||||
<PackageReference Include="Grpc.Tools" Version="$(GrpcToolsPkgVer)" PrivateAssets="all" />
|
||||
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Internal\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Implementation\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Trace\SemanticConventions.cs" Link="Includes\SemanticConventions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Exporter\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -29,15 +29,6 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation
|
|||
private const long UnixEpochTicks = 621355968000000000L; // = DateTimeOffset.FromUnixTimeMilliseconds(0).Ticks
|
||||
private const long UnixEpochMicroseconds = UnixEpochTicks / TicksPerMicrosecond;
|
||||
|
||||
private static readonly Dictionary<string, int> RemoteEndpointServiceNameKeyResolutionDictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[SemanticConventions.AttributePeerService] = 0, // priority 0 (highest).
|
||||
["peer.hostname"] = 1,
|
||||
["peer.address"] = 1,
|
||||
[SemanticConventions.AttributeHttpHost] = 2, // RemoteEndpoint.ServiceName for Http.
|
||||
[SemanticConventions.AttributeDbInstance] = 2, // RemoteEndpoint.ServiceName for Redis.
|
||||
};
|
||||
|
||||
private static readonly string InvalidSpanId = default(ActivitySpanId).ToHexString();
|
||||
|
||||
#if !NET452
|
||||
|
|
@ -76,29 +67,19 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation
|
|||
ZipkinEndpoint remoteEndpoint = null;
|
||||
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
|
||||
{
|
||||
var hostNameOrIpAddress = tagState.HostName ?? tagState.IpAddress;
|
||||
PeerServiceResolver.Resolve(ref tagState, out string peerServiceName, out bool addAsTag);
|
||||
|
||||
if ((tagState.RemoteEndpointServiceName == null || tagState.RemoteEndpointServiceNamePriority > 0)
|
||||
&& hostNameOrIpAddress != null)
|
||||
if (peerServiceName != null)
|
||||
{
|
||||
#if !NET452
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd((hostNameOrIpAddress, tagState.Port), ZipkinEndpoint.Create);
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd((peerServiceName, default), ZipkinEndpoint.Create);
|
||||
#else
|
||||
var remoteEndpointStr = tagState.Port != default
|
||||
? $"{hostNameOrIpAddress}:{tagState.Port}"
|
||||
: hostNameOrIpAddress;
|
||||
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd(remoteEndpointStr, ZipkinEndpoint.Create);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (remoteEndpoint == null && tagState.RemoteEndpointServiceName != null)
|
||||
{
|
||||
#if !NET452
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd((tagState.RemoteEndpointServiceName, default), ZipkinEndpoint.Create);
|
||||
#else
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd(tagState.RemoteEndpointServiceName, ZipkinEndpoint.Create);
|
||||
remoteEndpoint = RemoteEndpointCache.GetOrAdd(peerServiceName, ZipkinEndpoint.Create);
|
||||
#endif
|
||||
if (addAsTag)
|
||||
{
|
||||
PooledList<KeyValuePair<string, object>>.Add(ref tagState.Tags, new KeyValuePair<string, object>(SemanticConventions.AttributePeerService, peerServiceName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,19 +152,19 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation
|
|||
};
|
||||
}
|
||||
|
||||
internal struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>
|
||||
internal struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>, PeerServiceResolver.IPeerServiceState
|
||||
{
|
||||
public PooledList<KeyValuePair<string, object>> Tags;
|
||||
|
||||
public string RemoteEndpointServiceName;
|
||||
public string PeerService { get; set; }
|
||||
|
||||
public int RemoteEndpointServiceNamePriority;
|
||||
public int? PeerServicePriority { get; set; }
|
||||
|
||||
public string HostName;
|
||||
public string HostName { get; set; }
|
||||
|
||||
public string IpAddress;
|
||||
public string IpAddress { get; set; }
|
||||
|
||||
public int Port;
|
||||
public long Port { get; set; }
|
||||
|
||||
public bool ForEach(KeyValuePair<string, object> activityTag)
|
||||
{
|
||||
|
|
@ -192,40 +173,19 @@ namespace OpenTelemetry.Exporter.Zipkin.Implementation
|
|||
return true;
|
||||
}
|
||||
|
||||
string key = activityTag.Key;
|
||||
|
||||
if (activityTag.Value is string strVal)
|
||||
{
|
||||
string key = activityTag.Key;
|
||||
if (RemoteEndpointServiceNameKeyResolutionDictionary.TryGetValue(key, out int priority)
|
||||
&& (this.RemoteEndpointServiceName == null || priority < this.RemoteEndpointServiceNamePriority))
|
||||
{
|
||||
this.RemoteEndpointServiceName = strVal;
|
||||
this.RemoteEndpointServiceNamePriority = priority;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerName)
|
||||
{
|
||||
this.HostName = strVal;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerIp)
|
||||
{
|
||||
this.IpAddress = strVal;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerPort && int.TryParse(strVal, out var port))
|
||||
{
|
||||
this.Port = port;
|
||||
}
|
||||
|
||||
PooledList<KeyValuePair<string, object>>.Add(ref this.Tags, new KeyValuePair<string, object>(key, strVal));
|
||||
PeerServiceResolver.InspectTag(ref this, key, strVal);
|
||||
}
|
||||
else
|
||||
else if (activityTag.Value is int intVal && activityTag.Key == SemanticConventions.AttributeNetPeerPort)
|
||||
{
|
||||
if (activityTag.Value is int intVal && activityTag.Key == SemanticConventions.AttributeNetPeerPort)
|
||||
{
|
||||
this.Port = intVal;
|
||||
}
|
||||
|
||||
PooledList<KeyValuePair<string, object>>.Add(ref this.Tags, activityTag);
|
||||
PeerServiceResolver.InspectTag(ref this, key, intVal);
|
||||
}
|
||||
|
||||
PooledList<KeyValuePair<string, object>>.Add(ref this.Tags, activityTag);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Internal\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Trace\SemanticConventions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Implementation\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Includes\ExceptionExtensions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Trace\SemanticConventions.cs" Link="Includes\SemanticConventions.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\PooledList.cs" Link="Includes\PooledList.cs" />
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Exporter\PeerServiceResolver.cs" Link="Includes\PeerServiceResolver.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -17,20 +17,5 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry.Tests" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests" + AssemblyInfo.PublicKey)]
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2" + AssemblyInfo.MoqPublicKey)]
|
||||
[assembly: InternalsVisibleTo("Benchmarks" + AssemblyInfo.PublicKey)]
|
||||
|
||||
#if SIGNED
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051c1562a090fb0c9f391012a32198b5e5d9a60e9b80fa2d7b434c9e5ccb7259bd606e66f9660676afc6692b8cdc6793d190904551d2103b7b22fa636dcbb8208839785ba402ea08fc00c8f1500ccef28bbf599aa64ffb1e1d5dc1bf3420a3777badfe697856e9d52070a50c3ea5821c80bef17ca3acffa28f89dd413f096f898";
|
||||
public const string MoqPublicKey = ", PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7";
|
||||
}
|
||||
#else
|
||||
internal static class AssemblyInfo
|
||||
{
|
||||
public const string PublicKey = "";
|
||||
public const string MoqPublicKey = "";
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
// <copyright file="PeerServiceResolver.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
namespace OpenTelemetry.Exporter
|
||||
{
|
||||
internal static class PeerServiceResolver
|
||||
{
|
||||
private static readonly Dictionary<string, int> PeerServiceKeyResolutionDictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
[SemanticConventions.AttributePeerService] = 0, // priority 0 (highest).
|
||||
["peer.hostname"] = 1,
|
||||
["peer.address"] = 1,
|
||||
[SemanticConventions.AttributeHttpHost] = 2, // peer.service for Http.
|
||||
[SemanticConventions.AttributeDbInstance] = 2, // peer.service for Redis.
|
||||
};
|
||||
|
||||
public interface IPeerServiceState
|
||||
{
|
||||
string PeerService { get; set; }
|
||||
|
||||
int? PeerServicePriority { get; set; }
|
||||
|
||||
string HostName { get; set; }
|
||||
|
||||
string IpAddress { get; set; }
|
||||
|
||||
long Port { get; set; }
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InspectTag<T>(ref T state, string key, string value)
|
||||
where T : struct, IPeerServiceState
|
||||
{
|
||||
if (PeerServiceKeyResolutionDictionary.TryGetValue(key, out int priority)
|
||||
&& (state.PeerService == null || priority < state.PeerServicePriority))
|
||||
{
|
||||
state.PeerService = value;
|
||||
state.PeerServicePriority = priority;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerName)
|
||||
{
|
||||
state.HostName = value;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerIp)
|
||||
{
|
||||
state.IpAddress = value;
|
||||
}
|
||||
else if (key == SemanticConventions.AttributeNetPeerPort && long.TryParse(value, out var port))
|
||||
{
|
||||
state.Port = port;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InspectTag<T>(ref T state, string key, long value)
|
||||
where T : struct, IPeerServiceState
|
||||
{
|
||||
if (key == SemanticConventions.AttributeNetPeerPort)
|
||||
{
|
||||
state.Port = value;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Resolve<T>(ref T state, out string peerServiceName, out bool addAsTag)
|
||||
where T : struct, IPeerServiceState
|
||||
{
|
||||
peerServiceName = state.PeerService;
|
||||
|
||||
// If priority = 0 that means peer.service was included in tags
|
||||
addAsTag = state.PeerServicePriority != 0;
|
||||
|
||||
if (addAsTag)
|
||||
{
|
||||
var hostNameOrIpAddress = state.HostName ?? state.IpAddress;
|
||||
|
||||
// peer.service has not already been included, but net.peer.name/ip and optionally net.peer.port are present
|
||||
if (hostNameOrIpAddress != null)
|
||||
{
|
||||
peerServiceName = state.Port == default
|
||||
? hostNameOrIpAddress
|
||||
: $"{hostNameOrIpAddress}:{state.Port}";
|
||||
}
|
||||
else if (state.PeerService != null)
|
||||
{
|
||||
peerServiceName = state.PeerService;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,10 +8,6 @@
|
|||
<NoWarn>$(NoWarn),1591</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry.Api\Internal\ExceptionExtensions.cs" Link="Internal\ExceptionExtensions.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutablePkgVer)" />
|
||||
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="$(SystemReflectionEmitLightweightPkgVer)" />
|
||||
|
|
|
|||
|
|
@ -18,9 +18,14 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.OpenTelemetryProtocol\OpenTelemetry.Exporter.OpenTelemetryProtocol.csproj" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\EventSourceTestHelper.cs" Link="EventSourceTestHelper.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestActivityProcessor.cs" Link="TestActivityProcessor.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestEventListener.cs" Link="TestEventListener.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="$(RepoRoot)\src\OpenTelemetry\Internal\DateTimeOffsetExtensions.net452.cs" Link="Includes\DateTimeOffsetExtensions.net452.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\EventSourceTestHelper.cs" Link="Includes\EventSourceTestHelper.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestActivityProcessor.cs" Link="Includes\TestActivityProcessor.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestEventListener.cs" Link="Includes\TestEventListener.cs" />
|
||||
<Compile Include="$(RepoRoot)\test\OpenTelemetry.Tests\Shared\TestExporter.cs" Link="Includes\TestExporter.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
|
|||
.SetResource(resource)
|
||||
.Build();
|
||||
|
||||
var activities = new CircularBuffer<Activity>(512);
|
||||
var processor = new BatchExportProcessor<Activity>(new TestExporter<Activity>(RunTest));
|
||||
const int numOfSpans = 10;
|
||||
bool isEven;
|
||||
for (var i = 0; i < numOfSpans; i++)
|
||||
|
|
@ -88,44 +88,49 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
|
|||
var activityTags = isEven ? evenTags : oddTags;
|
||||
|
||||
using Activity activity = source.StartActivity($"span-{i}", activityKind, parentContext: default, activityTags);
|
||||
activities.Add(activity);
|
||||
processor.OnEnd(activity);
|
||||
}
|
||||
|
||||
var request = new OtlpCollector.ExportTraceServiceRequest();
|
||||
processor.Shutdown();
|
||||
|
||||
request.AddBatch(new Batch<Activity>(activities, numOfSpans));
|
||||
|
||||
Assert.Single(request.ResourceSpans);
|
||||
var oltpResource = request.ResourceSpans.First().Resource;
|
||||
Assert.Equal(resource.Attributes.First().Key, oltpResource.Attributes.First().Key);
|
||||
Assert.Equal(resource.Attributes.First().Value, oltpResource.Attributes.First().Value.StringValue);
|
||||
|
||||
foreach (var instrumentationLibrarySpans in request.ResourceSpans.First().InstrumentationLibrarySpans)
|
||||
void RunTest(Batch<Activity> batch)
|
||||
{
|
||||
Assert.Equal(numOfSpans / 2, instrumentationLibrarySpans.Spans.Count);
|
||||
Assert.NotNull(instrumentationLibrarySpans.InstrumentationLibrary);
|
||||
var request = new OtlpCollector.ExportTraceServiceRequest();
|
||||
|
||||
var expectedSpanNames = new List<string>();
|
||||
var start = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even" ? 0 : 1;
|
||||
for (var i = start; i < numOfSpans; i += 2)
|
||||
request.AddBatch(batch);
|
||||
|
||||
Assert.Single(request.ResourceSpans);
|
||||
var oltpResource = request.ResourceSpans.First().Resource;
|
||||
Assert.Equal(resource.Attributes.First().Key, oltpResource.Attributes.First().Key);
|
||||
Assert.Equal(resource.Attributes.First().Value, oltpResource.Attributes.First().Value.StringValue);
|
||||
|
||||
foreach (var instrumentationLibrarySpans in request.ResourceSpans.First().InstrumentationLibrarySpans)
|
||||
{
|
||||
expectedSpanNames.Add($"span-{i}");
|
||||
}
|
||||
Assert.Equal(numOfSpans / 2, instrumentationLibrarySpans.Spans.Count);
|
||||
Assert.NotNull(instrumentationLibrarySpans.InstrumentationLibrary);
|
||||
|
||||
var otlpSpans = instrumentationLibrarySpans.Spans;
|
||||
Assert.Equal(expectedSpanNames.Count, otlpSpans.Count);
|
||||
var expectedSpanNames = new List<string>();
|
||||
var start = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even" ? 0 : 1;
|
||||
for (var i = start; i < numOfSpans; i += 2)
|
||||
{
|
||||
expectedSpanNames.Add($"span-{i}");
|
||||
}
|
||||
|
||||
var kv0 = new OtlpCommon.KeyValue { Key = "k0", Value = new OtlpCommon.AnyValue { StringValue = "v0" } };
|
||||
var kv1 = new OtlpCommon.KeyValue { Key = "k1", Value = new OtlpCommon.AnyValue { StringValue = "v1" } };
|
||||
var otlpSpans = instrumentationLibrarySpans.Spans;
|
||||
Assert.Equal(expectedSpanNames.Count, otlpSpans.Count);
|
||||
|
||||
var expectedTag = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even"
|
||||
? kv0
|
||||
: kv1;
|
||||
var kv0 = new OtlpCommon.KeyValue { Key = "k0", Value = new OtlpCommon.AnyValue { StringValue = "v0" } };
|
||||
var kv1 = new OtlpCommon.KeyValue { Key = "k1", Value = new OtlpCommon.AnyValue { StringValue = "v1" } };
|
||||
|
||||
foreach (var otlpSpan in otlpSpans)
|
||||
{
|
||||
Assert.Contains(otlpSpan.Name, expectedSpanNames);
|
||||
Assert.Contains(expectedTag, otlpSpan.Attributes);
|
||||
var expectedTag = instrumentationLibrarySpans.InstrumentationLibrary.Name == "even"
|
||||
? kv0
|
||||
: kv1;
|
||||
|
||||
foreach (var otlpSpan in otlpSpans)
|
||||
{
|
||||
Assert.Contains(otlpSpan.Name, expectedSpanNames);
|
||||
Assert.Contains(expectedTag, otlpSpan.Attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +138,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
|
|||
[Fact]
|
||||
public void ToOtlpSpanTest()
|
||||
{
|
||||
var activitySource = new ActivitySource(nameof(this.ToOtlpSpanTest));
|
||||
using var activitySource = new ActivitySource(nameof(this.ToOtlpSpanTest));
|
||||
|
||||
using var rootActivity = activitySource.StartActivity("root", ActivityKind.Producer);
|
||||
|
||||
|
|
@ -237,6 +242,25 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ToOtlpSpanPeerServiceTest()
|
||||
{
|
||||
using var activitySource = new ActivitySource(nameof(this.ToOtlpSpanTest));
|
||||
|
||||
using var rootActivity = activitySource.StartActivity("root", ActivityKind.Client);
|
||||
|
||||
rootActivity.SetTag(SemanticConventions.AttributeHttpHost, "opentelemetry.io");
|
||||
|
||||
var otlpSpan = rootActivity.ToOtlpSpan();
|
||||
|
||||
Assert.NotNull(otlpSpan);
|
||||
|
||||
var peerService = otlpSpan.Attributes.FirstOrDefault(kvp => kvp.Key == SemanticConventions.AttributePeerService);
|
||||
|
||||
Assert.NotNull(peerService);
|
||||
Assert.Equal("opentelemetry.io", peerService.Value.StringValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseOpenTelemetryProtocolActivityExporterWithCustomActivityProcessor()
|
||||
{
|
||||
|
|
@ -264,7 +288,7 @@ namespace OpenTelemetry.Exporter.OpenTelemetryProtocol.Tests
|
|||
.AddOtlpExporter()
|
||||
.Build();
|
||||
|
||||
var source = new ActivitySource(ActivitySourceName);
|
||||
using var source = new ActivitySource(ActivitySourceName);
|
||||
var activity = source.StartActivity("Test Otlp Activity");
|
||||
activity?.Stop();
|
||||
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ namespace OpenTelemetry.Exporter.Zipkin.Tests
|
|||
var traceId = useShortTraceIds ? TraceId.Substring(TraceId.Length - 16, 16) : TraceId;
|
||||
|
||||
Assert.Equal(
|
||||
$@"[{{""traceId"":""{traceId}"",""name"":""Name"",""parentId"":""{ZipkinActivityConversionExtensions.EncodeSpanId(activity.ParentSpanId)}"",""id"":""{ZipkinActivityConversionExtensions.EncodeSpanId(context.SpanId)}"",""kind"":""CLIENT"",""timestamp"":{timestamp},""duration"":60000000,""localEndpoint"":{{""serviceName"":""{serviceName}""{ipInformation}}},""remoteEndpoint"":{{""serviceName"":""http://localhost:44312/""}},""annotations"":[{{""timestamp"":{eventTimestamp},""value"":""Event1""}},{{""timestamp"":{eventTimestamp},""value"":""Event2""}}],""tags"":{{{resoureTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""True"",""http.host"":""http://localhost:44312/"",""library.name"":""CreateTestActivity""}}}}]",
|
||||
$@"[{{""traceId"":""{traceId}"",""name"":""Name"",""parentId"":""{ZipkinActivityConversionExtensions.EncodeSpanId(activity.ParentSpanId)}"",""id"":""{ZipkinActivityConversionExtensions.EncodeSpanId(context.SpanId)}"",""kind"":""CLIENT"",""timestamp"":{timestamp},""duration"":60000000,""localEndpoint"":{{""serviceName"":""{serviceName}""{ipInformation}}},""remoteEndpoint"":{{""serviceName"":""http://localhost:44312/""}},""annotations"":[{{""timestamp"":{eventTimestamp},""value"":""Event1""}},{{""timestamp"":{eventTimestamp},""value"":""Event2""}}],""tags"":{{{resoureTags}""stringKey"":""value"",""longKey"":""1"",""longKey2"":""1"",""doubleKey"":""1"",""doubleKey2"":""1"",""longArrayKey"":""1,2"",""boolKey"":""True"",""http.host"":""http://localhost:44312/"",""library.name"":""CreateTestActivity"",""peer.service"":""http://localhost:44312/""}}}}]",
|
||||
Responses[requestId]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry\OpenTelemetry.csproj" />
|
||||
<ProjectReference Include="$(RepoRoot)\src\OpenTelemetry.Exporter.InMemory\OpenTelemetry.Exporter.InMemory.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
// <copyright file="TestExporter.cs" company="OpenTelemetry Authors">
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// </copyright>
|
||||
|
||||
using System;
|
||||
|
||||
namespace OpenTelemetry.Tests
|
||||
{
|
||||
internal class TestExporter<T> : BaseExporter<T>
|
||||
where T : class
|
||||
{
|
||||
private readonly Action<Batch<T>> processBatchAction;
|
||||
|
||||
public TestExporter(Action<Batch<T>> processBatchAction)
|
||||
{
|
||||
this.processBatchAction = processBatchAction ?? throw new ArgumentNullException(nameof(processBatchAction));
|
||||
}
|
||||
|
||||
public override ExportResult Export(in Batch<T> batch)
|
||||
{
|
||||
this.processBatchAction(batch);
|
||||
|
||||
return ExportResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue