Skip to content

Commit ad9dc58

Browse files
authored
feat(tsc-opentelemetry): add new span attributes (#681)
* feat: add new attributes: user_agent、authorization,client_ip and response body * feat: add httpReponse middleware * fix: update
1 parent 0cd1946 commit ad9dc58

File tree

7 files changed

+114
-11
lines changed

7 files changed

+114
-11
lines changed

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/Extensions/ServiceExtensions.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static IServiceCollection AddObservable(this IServiceCollection services,
2626
Func<string>? otlpUrlConfigure = null,
2727
bool isBlazor = false,
2828
bool isInterruptSignalRTracing = true)
29-
{
29+
{
3030
ArgumentNullException.ThrowIfNull(optionsConfigure);
3131
var options = optionsConfigure();
3232
var otlpUrl = otlpUrlConfigure?.Invoke() ?? string.Empty;
@@ -45,7 +45,6 @@ public static IServiceCollection AddObservable(this IServiceCollection services,
4545
Uri? uri = null;
4646
if (!string.IsNullOrEmpty(otlpUrl) && !Uri.TryCreate(otlpUrl, UriKind.Absolute, out uri))
4747
throw new UriFormatException($"{nameof(otlpUrl)}:{otlpUrl} is invalid url");
48-
4948
services.AddOpenTelemetry()
5049
.ConfigureResource(resource => resource.AddMasaService(option))
5150
.AddMasaTracing(services, builder => builder.AddOtlpExporter(options => options.Endpoint = uri),
@@ -67,4 +66,9 @@ public static IServiceCollection AddObservable(this IServiceCollection services,
6766

6867
return services;
6968
}
69+
70+
public static IApplicationBuilder UseMASAHttpReponseLog(IApplicationBuilder app)
71+
{
72+
return app.UseMiddleware<HttpResponseMiddleware>();
73+
}
7074
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) MASA Stack All rights reserved.
2+
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
3+
4+
namespace Masa.Contrib.StackSdks.Tsc.OpenTelemetry;
5+
6+
internal class HttpResponseMiddleware
7+
{
8+
private readonly RequestDelegate _next;
9+
10+
public HttpResponseMiddleware(RequestDelegate next)
11+
{
12+
_next = next;
13+
}
14+
15+
public async Task InvokeAsync(HttpContext httpContext)
16+
{
17+
var httpResponse = httpContext.Response;
18+
using var ms = new MemoryStream();
19+
var rawStream = httpResponse.Body;
20+
httpResponse.Body = ms;
21+
await _next(httpContext);
22+
ms.Seek(0, SeekOrigin.Begin);
23+
var responseResult = new StreamReader(ms).ReadToEnd();
24+
ms.Seek(0, SeekOrigin.Begin);
25+
ms.CopyTo(rawStream);
26+
httpResponse.Body = rawStream;
27+
28+
if (httpResponse.StatusCode - 299 == 0 || httpResponse.StatusCode - 500 >= 0)
29+
{
30+
Activity.Current?.SetTag(OpenTelemetryAttributeName.Http.RESPONSE_CONTENT_BODY, responseResult);
31+
}
32+
else
33+
{
34+
OpenTelemetryInstrumentationOptions.Logger.LogInformation("response length: {length}, context: {context}", responseResult.Length, responseResult);
35+
}
36+
}
37+
}

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/Options/OpenTelemetryAttributeName.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ internal static class Http
114114
/// custom attr
115115
/// </summary>
116116
public const string RESPONSE_CONTENT_BODY = "http.response_content_body";
117+
118+
/// <summary>
119+
/// https://opentelemetry.io/docs/specs/semconv/http/http-spans/#common-attributes
120+
/// </summary>
121+
public const string REQUEST_USER_AGENT = "user_agent.original";
122+
123+
public const string REQUEST_AUTHORIZATION = "authorization";
117124
}
118125

119126
/// <summary>
@@ -144,7 +151,7 @@ internal static class Service
144151
public const string PROJECT_NAME = "service.project.name";
145152

146153
public const string LAYER = "service.layer";
147-
}
154+
}
148155

149156
internal static class ExceptionAttributeName
150157
{

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/Traceing/ActivityExtensions.cs

+25-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,22 @@ public static Activity AddMasaSupplement(this Activity activity, HttpRequest htt
99
{
1010
activity.SetTag(OpenTelemetryAttributeName.Http.FLAVOR, httpRequest.Protocol);
1111
activity.SetTag(OpenTelemetryAttributeName.Http.SCHEME, httpRequest.Scheme);
12-
activity.SetTag(OpenTelemetryAttributeName.Http.CLIENT_IP, httpRequest.HttpContext?.Connection?.RemoteIpAddress);
1312
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_CONTENT_LENGTH, httpRequest.ContentLength);
1413
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_CONTENT_TYPE, httpRequest.ContentType);
14+
if (httpRequest.Headers != null)
15+
{
16+
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_AUTHORIZATION, httpRequest.Headers.Authorization);
17+
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_USER_AGENT, httpRequest.Headers.UserAgent);
18+
var realIP = httpRequest.Headers["X-Real-IP"].ToString();
19+
realIP ??= httpRequest.HttpContext!.Connection.RemoteIpAddress!.ToString();
20+
activity.SetTag(OpenTelemetryAttributeName.Http.CLIENT_IP, realIP);
21+
}
22+
23+
if ((httpRequest.HttpContext.User?.Claims.Count() ?? 0) > 0)
24+
{
25+
activity.AddTag(OpenTelemetryAttributeName.EndUser.ID, httpRequest.HttpContext.User?.FindFirst("sub")?.Value ?? string.Empty);
26+
activity.AddTag(OpenTelemetryAttributeName.EndUser.USER_NICK_NAME, httpRequest.HttpContext.User?.FindFirst("https://masastack.com/security/authentication/MasaNickName")?.Value ?? string.Empty);
27+
}
1528
if (httpRequest.Body != null)
1629
{
1730
if (!httpRequest.Body.CanSeek)
@@ -26,7 +39,11 @@ public static Activity AddMasaSupplement(this Activity activity, HttpRequestMess
2639
{
2740
activity.SetTag(OpenTelemetryAttributeName.Http.SCHEME, httpRequest.RequestUri?.Scheme);
2841
activity.SetTag(OpenTelemetryAttributeName.Host.NAME, Dns.GetHostName());
29-
42+
if (httpRequest.Headers != null)
43+
{
44+
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_AUTHORIZATION, httpRequest.Headers.Authorization);
45+
activity.SetTag(OpenTelemetryAttributeName.Http.REQUEST_USER_AGENT, httpRequest.Headers.UserAgent);
46+
}
3047
if (httpRequest.Content != null)
3148
{
3249
SetActivityBody(activity,
@@ -48,7 +65,12 @@ public static Activity AddMasaSupplement(this Activity activity, HttpResponse ht
4865
activity.AddTag(OpenTelemetryAttributeName.EndUser.ID, httpResponse.HttpContext.User?.FindFirst("sub")?.Value ?? string.Empty);
4966
activity.AddTag(OpenTelemetryAttributeName.EndUser.USER_NICK_NAME, httpResponse.HttpContext.User?.FindFirst("https://masastack.com/security/authentication/MasaNickName")?.Value ?? string.Empty);
5067
}
51-
68+
if (httpResponse.HttpContext.Request != null && httpResponse.HttpContext.Request.Headers != null)
69+
{
70+
var realIP = httpResponse.HttpContext.Request.Headers["X-Real-IP"].ToString();
71+
realIP ??= httpResponse.HttpContext!.Connection.RemoteIpAddress!.ToString();
72+
activity.SetTag(OpenTelemetryAttributeName.Http.CLIENT_IP, realIP);
73+
}
5274
return activity;
5375
}
5476

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/Traceing/OpenTelemetryInstrumentationOptions.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ public class OpenTelemetryInstrumentationOptions
77
{
88
public OpenTelemetryInstrumentationOptions(IServiceProvider serviceProvider)
99
{
10-
Logger ??= serviceProvider.GetRequiredService<ILogger<OpenTelemetryInstrumentationOptions>>();
10+
if (Logger == null)
11+
{
12+
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
13+
Logger = loggerFactory.CreateLogger("Masa.Contrib.StackSdks.Tsc.OpenTelemetry");
14+
}
1115
}
1216

1317
private readonly static AspNetCoreInstrumentationHandler aspNetCoreInstrumentationHandler = new();
1418
private readonly static HttpClientInstrumentHandler httpClientInstrumentHandler = new();
19+
1520
internal static ILogger Logger { get; private set; }
1621
internal static long MaxBodySize { get; private set; } = 200 * 1 << 10;
1722

@@ -50,6 +55,10 @@ public OpenTelemetryInstrumentationOptions(IServiceProvider serviceProvider)
5055
options.ParseAndFormatRequest = true;
5156
};
5257

58+
public Func<IConnectionMultiplexer> ConnectionMultiplexerOptions { get; set; }
59+
60+
public Action<StackExchangeRedisInstrumentationOptions> StackExchangeRedisInstrumentationOptions { get; set; }
61+
5362
/// <summary>
5463
/// Build trace callback, allow to supplement the build process
5564
/// </summary>

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/Traceing/ServiceExtensions.cs

+25-4
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
11
// Copyright (c) MASA Stack All rights reserved.
22
// Licensed under the Apache License. See LICENSE.txt in the project root for license information.
33

4+
using StackExchange.Redis;
5+
46
namespace Microsoft.Extensions.DependencyInjection;
57

68
public static partial class ServiceExtensions
79
{
8-
public static IServiceCollection AddMasaTracing(this IServiceCollection services, Action<TracerProviderBuilder> builderConfigure, Action<OpenTelemetryInstrumentationOptions>? configure = null)
10+
public static IServiceCollection AddMasaTracing(this IServiceCollection services,
11+
Action<TracerProviderBuilder> builderConfigure,
12+
Action<OpenTelemetryInstrumentationOptions>? configure = null)
913
{
1014
services.AddOpenTelemetry().AddMasaTracing(services, builderConfigure, configure);
1115
return services;
1216
}
1317

14-
internal static OpenTelemetryBuilder AddMasaTracing(this OpenTelemetryBuilder builder, IServiceCollection services, Action<TracerProviderBuilder> builderConfigure, Action<OpenTelemetryInstrumentationOptions>? configure = null)
18+
internal static OpenTelemetryBuilder AddMasaTracing(this OpenTelemetryBuilder builder,
19+
IServiceCollection services,
20+
Action<TracerProviderBuilder> builderConfigure,
21+
Action<OpenTelemetryInstrumentationOptions>? openTelemetryInstrumentationOptions = null)
1522
{
1623
return builder.WithTracing(builder =>
1724
{
1825
builder.SetSampler(new AlwaysOnSampler());
19-
var option = new OpenTelemetryInstrumentationOptions(services.BuildServiceProvider());
20-
configure?.Invoke(option);
26+
var option = services.BuildServiceProvider().GetService<OpenTelemetryInstrumentationOptions>();
27+
option ??= new OpenTelemetryInstrumentationOptions(services.BuildServiceProvider());
28+
openTelemetryInstrumentationOptions?.Invoke(option);
2129

2230
if (option.AspNetCoreInstrumentationOptions != null)
2331
builder.AddAspNetCoreInstrumentation(option.AspNetCoreInstrumentationOptions);
@@ -31,6 +39,19 @@ internal static OpenTelemetryBuilder AddMasaTracing(this OpenTelemetryBuilder bu
3139
if (option.ElasticsearchClientInstrumentationOptions != null)
3240
builder.AddElasticsearchClientInstrumentation(option.ElasticsearchClientInstrumentationOptions);
3341

42+
if (option.ConnectionMultiplexerOptions != null)
43+
{
44+
foreach (Delegate handle in option.ConnectionMultiplexerOptions.GetInvocationList())
45+
{
46+
var obj = handle.DynamicInvoke();
47+
builder.AddRedisInstrumentation((IConnectionMultiplexer)obj!, options =>
48+
{
49+
options.SetVerboseDatabaseStatements = true;
50+
option.StackExchangeRedisInstrumentationOptions?.Invoke(options);
51+
});
52+
}
53+
}
54+
3455
builderConfigure?.Invoke(builder);
3556
option.BuildTraceCallback?.Invoke(builder);
3657
});

src/Contrib/StackSdks/Masa.Contrib.StackSdks.Tsc.OpenTelemetry/_Imports.cs

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
global using Masa.Contrib.StackSdks.Tsc.OpenTelemetry;
66
global using Masa.Contrib.StackSdks.Tsc.OpenTelemetry.Metric.Instrumentation.Http;
77
global using Masa.Contrib.StackSdks.Tsc.OpenTelemetry.Tracing.Handler;
8+
global using Microsoft.AspNetCore.Builder;
89
global using Microsoft.AspNetCore.Http;
910
global using Microsoft.Extensions.Configuration;
1011
global using Microsoft.Extensions.DependencyInjection;
@@ -14,10 +15,12 @@
1415
global using OpenTelemetry.Instrumentation.ElasticsearchClient;
1516
global using OpenTelemetry.Instrumentation.EntityFrameworkCore;
1617
global using OpenTelemetry.Instrumentation.Http;
18+
global using OpenTelemetry.Instrumentation.StackExchangeRedis;
1719
global using OpenTelemetry.Logs;
1820
global using OpenTelemetry.Metrics;
1921
global using OpenTelemetry.Resources;
2022
global using OpenTelemetry.Trace;
23+
global using StackExchange.Redis;
2124
global using System;
2225
global using System.Collections.Generic;
2326
global using System.Diagnostics;

0 commit comments

Comments
 (0)