Skip to content

Commit 3f847e6

Browse files
committed
请求内容是否作为数据标签。默认true
1 parent ec032a6 commit 3f847e6

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

NewLife.Core/Log/ITracerResolver.cs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System.Net;
2+
3+
namespace NewLife.Log;
4+
5+
/// <summary>追踪器解析器</summary>
6+
public interface ITracerResolver
7+
{
8+
/// <summary>从Uri中解析出埋点名称</summary>
9+
/// <param name="uri"></param>
10+
/// <param name="userState"></param>
11+
/// <returns></returns>
12+
String ResolveName(Uri uri, Object? userState);
13+
14+
/// <summary>解析埋点名称</summary>
15+
/// <param name="name"></param>
16+
/// <param name="userState"></param>
17+
/// <returns></returns>
18+
String ResolveName(String name, Object? userState);
19+
20+
/// <summary>创建Http请求埋点</summary>
21+
ISpan CreateSpan(ITracer tracer, Uri uri, Object? userState);
22+
}
23+
24+
/// <summary>默认追踪器解析器</summary>
25+
/// <remarks>
26+
/// 解析器给予用户自定义创建Http请求埋点的机会,可以根据需要自定义埋点名称和标签。
27+
/// 在星尘扩展中,再次扩展解析,支持自定义WebApi接口的埋点名称和标签。
28+
/// </remarks>
29+
public class DefaultTracerResolver : ITracerResolver
30+
{
31+
/// <summary>请求内容是否作为数据标签。默认true</summary>
32+
/// <remarks>丰富的数据标签可以辅助分析问题,关闭后提升性能</remarks>
33+
public Boolean RequestContentAsTag { get; set; } = true;
34+
35+
/// <summary>支持作为标签数据的内容类型</summary>
36+
public String[] TagTypes { get; set; } = [
37+
"text/plain", "text/xml", "application/json", "application/xml", "application/x-www-form-urlencoded"
38+
];
39+
40+
/// <summary>标签数据中要排除的头部</summary>
41+
public String[] ExcludeHeaders { get; set; } = ["traceparent", "Cookie"];
42+
43+
/// <summary>从Uri中解析出埋点名称</summary>
44+
/// <param name="uri"></param>
45+
/// <param name="userState"></param>
46+
/// <returns></returns>
47+
public String ResolveName(Uri uri, Object? userState)
48+
{
49+
var url = uri.ToString();
50+
51+
// 太长的Url分段,不适合作为埋点名称
52+
if (url.Length > 20 + 16)
53+
{
54+
var ss = url.Split('/', '?');
55+
// 从第三段开始查,跳过开头的http://和域名
56+
for (var i = 3; i < ss.Length; i++)
57+
{
58+
if (ss[i].Length > 16)
59+
{
60+
url = ss.Take(i).Join("/");
61+
break;
62+
}
63+
}
64+
}
65+
66+
var p1 = url.IndexOf('?');
67+
var name = p1 < 0 ? url : url[..p1];
68+
return ResolveName(name, userState);
69+
}
70+
71+
/// <summary>解析埋点名称</summary>
72+
/// <param name="name"></param>
73+
/// <param name="userState"></param>
74+
/// <returns></returns>
75+
public String ResolveName(String name, Object? userState) => name;
76+
77+
/// <summary>创建Http请求埋点</summary>
78+
public ISpan CreateSpan(ITracer tracer, Uri uri, Object? userState)
79+
{
80+
var name = tracer.Resolver.ResolveName(uri, userState);
81+
var span = tracer.NewSpan(name);
82+
83+
var request = userState as HttpRequestMessage;
84+
var method = request?.Method.Method ?? (userState as WebRequest)?.Method ?? "GET";
85+
var tag = $"{method} {uri}";
86+
87+
if (RequestContentAsTag && tag.Length < tracer.MaxTagLength &&
88+
span is DefaultSpan ds && ds.TraceFlag > 0 && request != null)
89+
{
90+
var maxLength = ds.Tracer?.MaxTagLength ?? 1024;
91+
if (request.Content is ByteArrayContent content &&
92+
content.Headers.ContentLength != null &&
93+
content.Headers.ContentLength < 1024 * 8 &&
94+
content.Headers.ContentType != null &&
95+
content.Headers.ContentType.MediaType.StartsWithIgnoreCase(TagTypes))
96+
{
97+
// 既然都读出来了,不管多长,都要前面1024字符
98+
var str = request.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
99+
if (!str.IsNullOrEmpty()) tag += "\r\n" + (str.Length > maxLength ? str[..maxLength] : str);
100+
}
101+
102+
if (tag.Length < 500)
103+
{
104+
var vs = request.Headers.Where(e => !e.Key.EqualIgnoreCase(ExcludeHeaders)).ToDictionary(e => e.Key, e => e.Value.Join(";"));
105+
tag += "\r\n" + vs.Join("\r\n", e => $"{e.Key}: {e.Value}");
106+
}
107+
}
108+
span.SetTag(tag);
109+
110+
return span;
111+
}
112+
}

0 commit comments

Comments
 (0)