Skip to content

Commit

Permalink
File scoped namespace for StackExchangeRedis (#552)
Browse files Browse the repository at this point in the history
  • Loading branch information
Kielek authored Aug 1, 2022
1 parent c062c5a commit 355643c
Show file tree
Hide file tree
Showing 6 changed files with 723 additions and 729 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,190 +23,189 @@
using OpenTelemetry.Trace;
using StackExchange.Redis.Profiling;

namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation
namespace OpenTelemetry.Instrumentation.StackExchangeRedis.Implementation;

internal static class RedisProfilerEntryToActivityConverter
{
internal static class RedisProfilerEntryToActivityConverter
private static readonly Lazy<Func<object, (string, string)>> MessageDataGetter = new(() =>
{
private static readonly Lazy<Func<object, (string, string)>> MessageDataGetter = new(() =>
{
var redisAssembly = typeof(IProfiledCommand).Assembly;
Type profiledCommandType = redisAssembly.GetType("StackExchange.Redis.Profiling.ProfiledCommand");
Type scriptMessageType = redisAssembly.GetType("StackExchange.Redis.RedisDatabase+ScriptEvalMessage");
var redisAssembly = typeof(IProfiledCommand).Assembly;
Type profiledCommandType = redisAssembly.GetType("StackExchange.Redis.Profiling.ProfiledCommand");
Type scriptMessageType = redisAssembly.GetType("StackExchange.Redis.RedisDatabase+ScriptEvalMessage");
var messageDelegate = CreateFieldGetter<object>(profiledCommandType, "Message", BindingFlags.NonPublic | BindingFlags.Instance);
var scriptDelegate = CreateFieldGetter<string>(scriptMessageType, "script", BindingFlags.NonPublic | BindingFlags.Instance);
var commandAndKeyFetcher = new PropertyFetcher<string>("CommandAndKey");
var messageDelegate = CreateFieldGetter<object>(profiledCommandType, "Message", BindingFlags.NonPublic | BindingFlags.Instance);
var scriptDelegate = CreateFieldGetter<string>(scriptMessageType, "script", BindingFlags.NonPublic | BindingFlags.Instance);
var commandAndKeyFetcher = new PropertyFetcher<string>("CommandAndKey");
if (messageDelegate == null)
{
return new Func<object, (string, string)>(source => (null, null));
}
if (messageDelegate == null)
return new Func<object, (string, string)>(source =>
{
if (source == null)
{
return new Func<object, (string, string)>(source => (null, null));
return (null, null);
}
return new Func<object, (string, string)>(source =>
var message = messageDelegate(source);
if (message == null)
{
if (source == null)
{
return (null, null);
}
var message = messageDelegate(source);
if (message == null)
{
return (null, null);
}
return (null, null);
}
string script = null;
if (message.GetType() == scriptMessageType)
{
script = scriptDelegate.Invoke(message);
}
string script = null;
if (message.GetType() == scriptMessageType)
{
script = scriptDelegate.Invoke(message);
}
if (commandAndKeyFetcher.TryFetch(message, out var value))
{
return (value, script);
}
if (commandAndKeyFetcher.TryFetch(message, out var value))
{
return (value, script);
}
return (null, script);
});
return (null, script);
});
});

public static Activity ProfilerCommandToActivity(Activity parentActivity, IProfiledCommand command, StackExchangeRedisCallsInstrumentationOptions options)
public static Activity ProfilerCommandToActivity(Activity parentActivity, IProfiledCommand command, StackExchangeRedisCallsInstrumentationOptions options)
{
var name = command.Command; // Example: SET;
if (string.IsNullOrEmpty(name))
{
var name = command.Command; // Example: SET;
if (string.IsNullOrEmpty(name))
{
name = StackExchangeRedisCallsInstrumentation.ActivityName;
}
name = StackExchangeRedisCallsInstrumentation.ActivityName;
}

var activity = StackExchangeRedisCallsInstrumentation.ActivitySource.StartActivity(
name,
ActivityKind.Client,
parentActivity?.Context ?? default,
StackExchangeRedisCallsInstrumentation.CreationTags,
startTime: command.CommandCreated);
var activity = StackExchangeRedisCallsInstrumentation.ActivitySource.StartActivity(
name,
ActivityKind.Client,
parentActivity?.Context ?? default,
StackExchangeRedisCallsInstrumentation.CreationTags,
startTime: command.CommandCreated);

if (activity == null)
{
return null;
}
if (activity == null)
{
return null;
}

activity.SetEndTime(command.CommandCreated + command.ElapsedTime);
activity.SetEndTime(command.CommandCreated + command.ElapsedTime);

if (activity.IsAllDataRequested)
{
// see https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md
if (activity.IsAllDataRequested)
{
// 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.SetStatus(Status.Unset);
activity.SetStatus(Status.Unset);

activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString());
activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisFlagsKeyName, command.Flags.ToString());

if (options.SetVerboseDatabaseStatements)
if (options.SetVerboseDatabaseStatements)
{
var (commandAndKey, script) = MessageDataGetter.Value.Invoke(command);

if (!string.IsNullOrEmpty(commandAndKey) && !string.IsNullOrEmpty(script))
{
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey + " " + script);
}
else if (!string.IsNullOrEmpty(commandAndKey))
{
var (commandAndKey, script) = MessageDataGetter.Value.Invoke(command);

if (!string.IsNullOrEmpty(commandAndKey) && !string.IsNullOrEmpty(script))
{
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey + " " + script);
}
else if (!string.IsNullOrEmpty(commandAndKey))
{
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey);
}
else if (command.Command != null)
{
// Example: "db.statement": SET;
activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command);
}
activity.SetTag(SemanticConventions.AttributeDbStatement, commandAndKey);
}
else if (command.Command != null)
{
// Example: "db.statement": SET;
activity.SetTag(SemanticConventions.AttributeDbStatement, command.Command);
}
}
else if (command.Command != null)
{
// 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());
}
activity.SetTag(SemanticConventions.AttributeNetPeerIp, ipEndPoint.Address.ToString());
activity.SetTag(SemanticConventions.AttributeNetPeerPort, ipEndPoint.Port);
}

activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName, command.Db);

// TODO: deal with the re-transmission
// command.RetransmissionOf;
// command.RetransmissionReason;

var enqueued = command.CommandCreated.Add(command.CreationToEnqueued);
var send = enqueued.Add(command.EnqueuedToSending);
var response = send.Add(command.SentToResponse);

if (options.EnrichActivityWithTimingEvents)
else if (command.EndPoint is DnsEndPoint dnsEndPoint)
{
activity.AddEvent(new ActivityEvent("Enqueued", enqueued));
activity.AddEvent(new ActivityEvent("Sent", send));
activity.AddEvent(new ActivityEvent("ResponseReceived", response));
activity.SetTag(SemanticConventions.AttributeNetPeerName, dnsEndPoint.Host);
activity.SetTag(SemanticConventions.AttributeNetPeerPort, dnsEndPoint.Port);
}
else
{
activity.SetTag(SemanticConventions.AttributePeerService, command.EndPoint.ToString());
}

options.Enrich?.Invoke(activity, command);
}

activity.Stop();
activity.SetTag(StackExchangeRedisCallsInstrumentation.RedisDatabaseIndexKeyName, command.Db);

return activity;
}
// TODO: deal with the re-transmission
// command.RetransmissionOf;
// command.RetransmissionReason;

public static void DrainSession(Activity parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisCallsInstrumentationOptions options)
{
foreach (var command in sessionCommands)
var enqueued = command.CommandCreated.Add(command.CreationToEnqueued);
var send = enqueued.Add(command.EnqueuedToSending);
var response = send.Add(command.SentToResponse);

if (options.EnrichActivityWithTimingEvents)
{
ProfilerCommandToActivity(parentActivity, command, options);
activity.AddEvent(new ActivityEvent("Enqueued", enqueued));
activity.AddEvent(new ActivityEvent("Sent", send));
activity.AddEvent(new ActivityEvent("ResponseReceived", response));
}

options.Enrich?.Invoke(activity, command);
}

/// <summary>
/// Creates getter for a field defined in private or internal type
/// represented with classType variable.
/// </summary>
private static Func<object, TField> CreateFieldGetter<TField>(Type classType, string fieldName, BindingFlags flags)
activity.Stop();

return activity;
}

public static void DrainSession(Activity parentActivity, IEnumerable<IProfiledCommand> sessionCommands, StackExchangeRedisCallsInstrumentationOptions options)
{
foreach (var command in sessionCommands)
{
FieldInfo field = classType.GetField(fieldName, flags);
if (field != null)
{
string methodName = classType.FullName + ".get_" + field.Name;
DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(TField), new[] { typeof(object) }, true);
ILGenerator generator = getterMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, classType);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Ret);

return (Func<object, TField>)getterMethod.CreateDelegate(typeof(Func<object, TField>));
}
ProfilerCommandToActivity(parentActivity, command, options);
}
}

return null;
/// <summary>
/// Creates getter for a field defined in private or internal type
/// represented with classType variable.
/// </summary>
private static Func<object, TField> CreateFieldGetter<TField>(Type classType, string fieldName, BindingFlags flags)
{
FieldInfo field = classType.GetField(fieldName, flags);
if (field != null)
{
string methodName = classType.FullName + ".get_" + field.Name;
DynamicMethod getterMethod = new DynamicMethod(methodName, typeof(TField), new[] { typeof(object) }, true);
ILGenerator generator = getterMethod.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, classType);
generator.Emit(OpCodes.Ldfld, field);
generator.Emit(OpCodes.Ret);

return (Func<object, TField>)getterMethod.CreateDelegate(typeof(Func<object, TField>));
}

return null;
}
}
Loading

0 comments on commit 355643c

Please sign in to comment.