首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在启动期间注册OpenTelemetry,并在运行时动态添加更多功能

在启动期间注册OpenTelemetry,并在运行时动态添加更多功能
EN

Stack Overflow用户
提问于 2021-11-29 07:23:45
回答 1查看 568关注 0票数 2

ConfigureServices类中的Startup方法中,我注册OpenTelemetry如下:

代码语言:javascript
复制
services.AddOpenTelemetryTracing((builder) =>
                    builder
                    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyService"))
                        .AddAspNetCoreInstrumentation()
                        .AddHttpClientInstrumentation()
                        .AddOtlpExporter(otlpOptions =>
                        {
                            otlpOptions.Endpoint = new Uri("http://localhost:4317");
                        }));

我也想添加Redis工具,但只有在为请求提供服务时,我才能访问Redis连接字符串,在请求中提取ClientId并从相应的客户端配置中提取该客户端的Redis连接字符串。再次在Startup中,在读取ClientInfo时,我添加了用于检测Redis调用的OpenTelemetry跟踪。

代码语言:javascript
复制
services.AddScoped<ClientInfo>(sp =>
            {
                var context = sp.GetService<IHttpContextAccessor>().HttpContext;
                var clientId = context.Request.Headers["ClientId"].ToString();
                var clientInfo = await GetClientInfo(clientId).Result;
                // ClientInfo will contain Redis connection string. I cache this to avoid fetching repeatedly for same client


                // I cache this ClientId in a dictionary and make sure the below registration happens
                // only once per client Id.
                // RedisConnection is of type IConnectionMultiplexer
                var redisConnection = RedisHelper.GetConnection(clientInfo.RedisConnectionString);
                services.AddOpenTelemetryTracing((builder) =>
                    builder
                    .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("MyService"))
                    .AddRedisInstrumentation(redisConnection)
                        .AddOtlpExporter(otlpOptions =>
                        {
                            otlpOptions.Endpoint = new Uri("http://localhost:4317");
                        }));

                return clientInfo;
            });

当我执行代码时,它只为传入的HTTP请求和传出的HTTP请求创建范围。但并不是在利用Redis的电话。但是,如果我在注册AddAspNetCoreInstrumentation的第一个调用中添加了Redis工具,那么它可以正常工作。

是否有一种方法可以在启动期间添加一些工具,并在运行时通过添加更多的工具来构建这些工具?

EN

回答 1

Stack Overflow用户

发布于 2022-10-05 14:18:27

据我所知,目前还没有这样的解决方案,在github上有一个开放的问题。但是,如果使用反射,则可以添加这样的功能。

代码语言:javascript
复制
public interface IRedisRuntimeInstrumentation
{
    void AddInstrumentation(IConnectionMultiplexer multiplexer);
}

internal class RedisRuntimeInstrumentation : IRedisRuntimeInstrumentation
{
    private static readonly BindingFlags BindingFlags = BindingFlags.Instance | BindingFlags.NonPublic;

    private readonly PropertyInfo _instrumentationsProperty;
    private readonly FieldInfo _disposedField;
    private readonly TracerProvider _tracerProvider;
    private readonly IOptions<StackExchangeRedisCallsInstrumentationOptions> _options;

    public RedisRuntimeInstrumentation(TracerProvider tracerProvider,
        IOptions<StackExchangeRedisCallsInstrumentationOptions> options)
    {
        var tracerProviderType = tracerProvider?.GetType() ?? throw new ArgumentNullException(nameof(tracerProvider));

        _instrumentationsProperty = tracerProviderType.GetProperty("Instrumentations", BindingFlags)
            ?? throw new InvalidOperationException($"Failed to get property 'Instrumentations' from type - {tracerProviderType.FullName}");

        _disposedField = tracerProviderType.GetField("disposed", BindingFlags)
            ?? throw new InvalidOperationException($"Failed to get field 'disposed' from type - {tracerProviderType.FullName}");

        _tracerProvider = tracerProvider;
        _options = options;
    }

    public void AddInstrumentation(IConnectionMultiplexer multiplexer)
    {
        if (multiplexer == null)
            throw new ArgumentNullException(nameof(multiplexer));

        if (_disposedField.GetValue(_tracerProvider) is bool disposed && disposed)
            throw new InvalidOperationException("Unable to add instrumentation to disposed trace provider");

        var instrumentationsPropertyValue = _instrumentationsProperty.GetValue(_tracerProvider);
        if (instrumentationsPropertyValue is List<object> instrumentations)
            instrumentations.Add(StackExchangeRedisCallsInstrumentationHelper.CreateInstance(multiplexer, _options.Value));
        else
            throw new InvalidOperationException("Failed to add instrumentation");
    }
}

internal static class StackExchangeRedisCallsInstrumentationHelper
{
    internal const string TypeFullName = "OpenTelemetry.Instrumentation.StackExchangeRedis.StackExchangeRedisCallsInstrumentation";

    internal static readonly Type Type = typeof(StackExchangeRedisCallsInstrumentationOptions).Assembly.GetType(TypeFullName)
        ?? throw new InvalidOperationException($"Failed to get type - {TypeFullName}");

    private static readonly ConstructorInfo TypeConstructor = Type
        .GetConstructor(new[] { typeof(IConnectionMultiplexer), typeof(StackExchangeRedisCallsInstrumentationOptions) })
            ?? throw new InvalidOperationException($"Failed to get constructor from type - {TypeFullName}");

    internal static object CreateInstance(IConnectionMultiplexer multiplexer,
        StackExchangeRedisCallsInstrumentationOptions options) =>
            TypeConstructor.Invoke(new object[] { multiplexer, options });
}

public static class OpenTelemetryExtensions
{
    public static IServiceCollection AddRedisRuntimeInstrumentation(this IServiceCollection services)
    {
        services.AddSingleton<IRedisRuntimeInstrumentation, RedisRuntimeInstrumentation>();

        return services;
    }

    public static TracerProviderBuilder AddRedisRuntimeInstrumentationSource(this TracerProviderBuilder builder)
    {
        var activitySourceNameField = StackExchangeRedisCallsInstrumentationHelper.Type
            .GetField("ActivitySourceName", BindingFlags.Static | BindingFlags.NonPublic);

        if (activitySourceNameField == null)
        {
            throw new InvalidOperationException(
                $"Failed to get field 'ActivitySourceName' from type - {StackExchangeRedisCallsInstrumentationHelper.TypeFullName}");
        }

        builder.AddSource((string)activitySourceNameField.GetValue(null));

        return builder;
    }
}

然后在Startup.cs中,通过DI调用AddRedisRuntimeInstrumentation()AddRedisRuntimeInstrumentationSource()来使用IRedisRuntimeInstrumentation,并在需要的地方调用AddInstrumentation

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70150973

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档