-
Notifications
You must be signed in to change notification settings - Fork 302
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
using System.Diagnostics.Metrics; | ||
using System.Reflection; | ||
using OpenTelemetry.Internal; | ||
|
||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation; | ||
|
||
internal class RedisMetrics : IDisposable | ||
{ | ||
internal const string MetricRequestDurationName = "redis.client.request.duration"; | ||
internal const string MetricWaitingResponseName = "redis.client.request.waiting_response"; | ||
internal const string MetricTimeInQueueName = "redis.client.request.time_in_queue"; | ||
|
||
internal static readonly Assembly Assembly = typeof(StackExchangeRedisInstrumentation).Assembly; | ||
internal static readonly AssemblyName AssemblyName = Assembly.GetName(); | ||
internal static readonly string InstrumentationName = AssemblyName.Name; | ||
internal static readonly string InstrumentationVersion = Assembly.GetPackageVersion(); | ||
|
||
private readonly Meter meter; | ||
|
||
public RedisMetrics() | ||
{ | ||
this.meter = new Meter(InstrumentationName, InstrumentationVersion); | ||
|
||
this.QueueHistogram = this.meter.CreateHistogram<double>( | ||
MetricTimeInQueueName, | ||
unit: "s", | ||
description: "Total time the redis request was waiting in queue before being sent to the server."); | ||
|
||
this.WaitingResponseHistogram = this.meter.CreateHistogram<double>( | ||
MetricWaitingResponseName, | ||
unit: "s", | ||
description: "Duration of redis requests since sent the request to receive the response."); | ||
|
||
this.RequestHistogram = this.meter.CreateHistogram<double>( | ||
MetricRequestDurationName, | ||
unit: "s", | ||
description: "Total client request duration, including processing, queue and server duration."); | ||
} | ||
|
||
public static RedisMetrics Instance { get; } = new RedisMetrics(); | ||
|
||
public Histogram<double> QueueHistogram { get; } | ||
|
||
public Histogram<double> WaitingResponseHistogram { get; } | ||
|
||
public Histogram<double> RequestHistogram { get; } | ||
|
||
public bool Enabled => RequestHistogram.Enabled; | ||
|
||
public void Dispose() | ||
{ | ||
this.meter.Dispose(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ | |
|
||
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation; | ||
|
||
internal static class RedisProfilerEntryToActivityConverter | ||
internal static class RedisProfilerEntryInstrumenter | ||
{ | ||
private static readonly Lazy<Func<object, (string?, string?)>> MessageDataGetter = new(() => | ||
{ | ||
|
@@ -73,7 +73,11 @@ static bool GetCommandAndKey( | |
}); | ||
}); | ||
|
||
public static Activity? ProfilerCommandToActivity(Activity? parentActivity, IProfiledCommand command, StackExchangeRedisInstrumentationOptions options) | ||
public static Activity? ProfilerCommandInstrument( | ||
Activity? parentActivity, | ||
IProfiledCommand command, | ||
RedisMetrics metrics, | ||
StackExchangeRedisInstrumentationOptions options) | ||
{ | ||
var name = command.Command; // Example: SET; | ||
if (string.IsNullOrEmpty(name)) | ||
|
@@ -88,30 +92,36 @@ static bool GetCommandAndKey( | |
StackExchangeRedisConnectionInstrumentation.CreationTags, | ||
startTime: command.CommandCreated); | ||
|
||
if (activity == null) | ||
if (activity is null && metrics.Enabled is false) | ||
{ | ||
return null; | ||
} | ||
|
||
activity.SetEndTime(command.CommandCreated + command.ElapsedTime); | ||
activity?.SetEndTime(command.CommandCreated + command.ElapsedTime); | ||
var meterTags = metrics.Enabled ? | ||
(IList<KeyValuePair<string, object?>>)new TagList(StackExchangeRedisConnectionInstrumentation.CreationTags.ToArray()) : | ||
default; | ||
|
||
if (activity.IsAllDataRequested) | ||
{ | ||
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md | ||
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md | ||
|
||
// Timing example: | ||
// command.CommandCreated; //2019-01-10 22:18:28Z | ||
// Timing example: | ||
// command.CommandCreated; //2019-01-10 22:18:28Z | ||
|
||
// command.CreationToEnqueued; // 00:00:32.4571995 | ||
// command.EnqueuedToSending; // 00:00:00.0352838 | ||
// command.SentToResponse; // 00:00:00.0060586 | ||
// command.ResponseToCompletion; // 00:00:00.0002601 | ||
// command.CreationToEnqueued; // 00:00:32.4571995 | ||
// command.EnqueuedToSending; // 00:00:00.0352838 | ||
// command.SentToResponse; // 00:00:00.0060586 | ||
// command.ResponseToCompletion; // 00:00:00.0002601 | ||
|
||
// Total: | ||
// command.ElapsedTime; // 00:00:32.4988020 | ||
// Total: | ||
// command.ElapsedTime; // 00:00:32.4988020 | ||
|
||
activity.SetTag(StackExchangeRedisConnectionInstrumentation.RedisFlagsKeyName, command.Flags.ToString()); | ||
var flags = command.Flags.ToString(); | ||
activity?.SetTag(SemanticConventions.AttributeDbRedisFlagsKeyName, flags); | ||
meterTags?.Add(SemanticConventions.AttributeDbRedisFlagsKeyName, flags); | ||
meterTags?.Add(SemanticConventions.AttributeDbStatement, command.Command ?? string.Empty); | ||
|
||
if (activity is not null) | ||
{ | ||
if (options.SetVerboseDatabaseStatements) | ||
{ | ||
var (commandAndKey, script) = MessageDataGetter.Value.Invoke(command); | ||
|
@@ -135,55 +145,93 @@ static bool GetCommandAndKey( | |
// Example: "db.statement": SET; | ||
activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command); | ||
} | ||
} | ||
|
||
if (command.EndPoint != null) | ||
if (command.EndPoint != null) | ||
{ | ||
if (command.EndPoint is IPEndPoint ipEndPoint) | ||
{ | ||
if (command.EndPoint is IPEndPoint ipEndPoint) | ||
{ | ||
activity.SetTag(SemanticConventions.AttributeNetPeerIp, ipEndPoint.Address.ToString()); | ||
activity.SetTag(SemanticConventions.AttributeNetPeerPort, ipEndPoint.Port); | ||
} | ||
else if (command.EndPoint is DnsEndPoint dnsEndPoint) | ||
{ | ||
activity.SetTag(SemanticConventions.AttributeNetPeerName, dnsEndPoint.Host); | ||
activity.SetTag(SemanticConventions.AttributeNetPeerPort, dnsEndPoint.Port); | ||
} | ||
else | ||
{ | ||
activity.SetTag(SemanticConventions.AttributePeerService, command.EndPoint.ToString()); | ||
} | ||
var ip = ipEndPoint.Address.ToString(); | ||
var port = ipEndPoint.Port; | ||
|
||
activity?.SetTag(SemanticConventions.AttributeNetPeerIp, ip); | ||
activity?.SetTag(SemanticConventions.AttributeNetPeerPort, port); | ||
|
||
meterTags?.Add(SemanticConventions.AttributeNetPeerIp, ip); | ||
meterTags?.Add(SemanticConventions.AttributeNetPeerPort, port); | ||
} | ||
else if (command.EndPoint is DnsEndPoint dnsEndPoint) | ||
{ | ||
var host = dnsEndPoint.Host; | ||
var port = dnsEndPoint.Port; | ||
|
||
activity?.SetTag(SemanticConventions.AttributeNetPeerName, host); | ||
activity?.SetTag(SemanticConventions.AttributeNetPeerPort, port); | ||
|
||
meterTags?.Add(SemanticConventions.AttributeNetPeerName, host); | ||
meterTags?.Add(SemanticConventions.AttributeNetPeerPort, port); | ||
} | ||
else | ||
{ | ||
var service = command.EndPoint.ToString(); | ||
|
||
activity?.SetTag(SemanticConventions.AttributePeerService, service); | ||
meterTags?.Add(SemanticConventions.AttributePeerService, service); | ||
} | ||
} | ||
|
||
activity.SetTag(StackExchangeRedisConnectionInstrumentation.RedisDatabaseIndexKeyName, command.Db); | ||
var db = command.Db; | ||
activity?.SetTag(SemanticConventions.AttributeDbRedisDatabaseIndex, db); | ||
meterTags?.Add(SemanticConventions.AttributeDbRedisDatabaseIndex, db); | ||
|
||
// TODO: deal with the re-transmission | ||
// command.RetransmissionOf; | ||
// command.RetransmissionReason; | ||
// TODO: deal with the re-transmission | ||
// command.RetransmissionOf; | ||
// command.RetransmissionReason; | ||
|
||
if (activity?.IsAllDataRequested ?? false) | ||
{ | ||
var enqueued = command.CommandCreated.Add(command.CreationToEnqueued); | ||
var send = enqueued.Add(command.EnqueuedToSending); | ||
var response = send.Add(command.SentToResponse); | ||
var completion = send.Add(command.ResponseToCompletion); | ||
|
||
if (options.EnrichActivityWithTimingEvents) | ||
{ | ||
activity.AddEvent(new ActivityEvent("Enqueued", enqueued)); | ||
activity.AddEvent(new ActivityEvent("Sent", send)); | ||
activity.AddEvent(new ActivityEvent("ResponseReceived", response)); | ||
activity.AddEvent(new ActivityEvent("Completion", completion)); | ||
} | ||
|
||
options.Enrich?.Invoke(activity, command); | ||
} | ||
|
||
activity.Stop(); | ||
if (metrics.Enabled && meterTags is TagList meterTagList) | ||
{ | ||
metrics.QueueHistogram.Record(command.EnqueuedToSending.TotalSeconds, meterTagList.ToArray()); | ||
metrics.WaitingResponseHistogram.Record(command.SentToResponse.TotalSeconds, meterTagList.ToArray()); | ||
metrics.RequestHistogram.Record(command.ElapsedTime.TotalSeconds, meterTagList.ToArray()); | ||
} | ||
|
||
activity?.Stop(); | ||
|
||
return activity; | ||
} | ||
|
||
public static void DrainSession(Activity? parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisInstrumentationOptions options) | ||
private static void Add(this IList<KeyValuePair<string, object?>> tags, string ket, object? value) | ||
{ | ||
tags?.Add(new KeyValuePair<string, object?>(ket, value)); | ||
} | ||
|
||
public static void DrainSession( | ||
Check failure on line 226 in src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryInstrumenter.cs GitHub Actions / build-test-instrumentation-stackexchangeredis / build-test (ubuntu-latest, net8.0)
Check failure on line 226 in src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryInstrumenter.cs GitHub Actions / build-test-instrumentation-stackexchangeredis / build-test (ubuntu-latest, net6.0)
Check failure on line 226 in src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryInstrumenter.cs GitHub Actions / build-test-instrumentation-stackexchangeredis / build-test (ubuntu-latest, net7.0)
Check failure on line 226 in src/OpenTelemetry.Instrumentation.StackExchangeRedis/Implementation/RedisProfilerEntryInstrumenter.cs GitHub Actions / build-test-instrumentation-stackexchangeredis / build-test (windows-latest, net7.0)
|
||
Activity? parentActivity, | ||
IEnumerable<IProfiledCommand> sessionCommands, | ||
RedisMetrics redisMetrics, | ||
StackExchangeRedisInstrumentationOptions options) | ||
{ | ||
foreach (var command in sessionCommands) | ||
{ | ||
ProfilerCommandToActivity(parentActivity, command, options); | ||
ProfilerCommandInstrument(parentActivity, command, redisMetrics, options); | ||
} | ||
} | ||
|
||
|