Skip to content

Commit

Permalink
[refactor] ISpan直接关联ITracer,避免长时间埋点完成时,发现Builder已经被上传处理,导致埋点丢失。
Browse files Browse the repository at this point in the history
  • Loading branch information
nnhy committed Nov 28, 2024
1 parent f66fef5 commit b963566
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 37 deletions.
47 changes: 33 additions & 14 deletions NewLife.Core/Log/ISpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public interface ISpan : IDisposable
/// <summary>唯一标识。随线程上下文、Http、Rpc传递,作为内部片段的父级</summary>
String Id { get; set; }

/// <summary>埋点名</summary>
String Name { get; set; }

/// <summary>父级片段标识</summary>
String? ParentId { get; set; }

Expand Down Expand Up @@ -64,13 +67,22 @@ public interface ISpan : IDisposable
public class DefaultSpan : ISpan
{
#region 属性
/// <summary>跟踪器</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember]
public ITracer? Tracer { get; set; }

/// <summary>构建器</summary>
[Obsolete]
[XmlIgnore, ScriptIgnore, IgnoreDataMember]
public ISpanBuilder? Builder { get; set; }

/// <summary>唯一标识。随线程上下文、Http、Rpc传递,作为内部片段的父级</summary>
public String Id { get; set; } = null!;

/// <summary>埋点名</summary>
[XmlIgnore, ScriptIgnore, IgnoreDataMember]
public String Name { get; set; } = null!;

/// <summary>父级片段标识</summary>
public String? ParentId { get; set; }

Expand Down Expand Up @@ -115,8 +127,8 @@ public class DefaultSpan : ISpan
public DefaultSpan() { }

/// <summary>实例化</summary>
/// <param name="builder"></param>
public DefaultSpan(ISpanBuilder builder) => Builder = builder;
/// <param name="tracer"></param>
public DefaultSpan(ITracer tracer) => Tracer = tracer;

static DefaultSpan()
{
Expand Down Expand Up @@ -214,28 +226,32 @@ protected virtual void Finish()
// 从本线程中清除跟踪标识
Current = _parent;

var builder = Builder;
if (builder == null) return;
var tracer = Tracer;
if (tracer == null) return;

var name = builder.Name;
var name = Name;
if (!name.IsNullOrEmpty())
{
// Builder这一批可能已经上传,重新取一次,以防万一
builder = builder.Tracer?.BuildSpan(name);
//!!! Builder这一批可能已经上传,重新取一次,以防万一。如果在星尘平台没见到埋点数据,大概率是这里的问题
var builder = tracer?.BuildSpan(name);
builder?.Finish(this);
}

// 打断对Builder的引用,当前Span可能还被放在AsyncLocal字典中
// 也有可能原来的Builder已经上传,现在加入了新的builder集合
#pragma warning disable CS0612 // 类型或成员已过时
Builder = null;
#pragma warning restore CS0612 // 类型或成员已过时
}

/// <summary>抛弃埋点,不计入采集</summary>
/// <remarks>在Web应用中,经常遇到各种扫描,导致记录大量并不存在的异常埋点,此时需要抛弃这些404埋点</remarks>
public virtual void Abandon()
{
_finished = 1;
Builder = null;
//Builder = null;

// 不能还给对象池,因为外部还有引用,Dispose尚未调用
}

/// <summary>设置错误信息,ApiException除外</summary>
Expand All @@ -259,7 +275,7 @@ public virtual void SetError(Exception ex, Object? tag)
Error = ex.GetMessage();

// 所有异常,独立记录埋点,便于按异常分类统计
using var span = Builder?.Tracer?.NewSpan(name, tag);
using var span = Tracer?.NewSpan(name, tag);
span?.AppendTag(ex.ToString());
if (span != null) span.StartTime = StartTime;
}
Expand All @@ -271,7 +287,7 @@ public virtual void SetTag(Object? tag)
{
if (tag == null) return;

var len = Builder?.Tracer?.MaxTagLength ?? 1024;
var len = Tracer?.MaxTagLength ?? 1024;
if (len <= 0) return;

if (tag is String str)
Expand All @@ -296,8 +312,12 @@ public virtual void SetTag(Object? tag)
/// <summary>清空已有数据</summary>
public void Clear()
{
Tracer = null;
#pragma warning disable CS0612 // 类型或成员已过时
Builder = null;
#pragma warning restore CS0612 // 类型或成员已过时
Id = null!;
Name = null!;
ParentId = null;
TraceId = null!;
StartTime = 0;
Expand All @@ -323,8 +343,7 @@ public static class SpanExtension
#region 扩展方法
private static String? GetAttachParameter(ISpan span)
{
var builder = (span as DefaultSpan)?.Builder;
var tracer = (builder as DefaultSpanBuilder)?.Tracer;
var tracer = (span as DefaultSpan)?.Tracer;
return tracer?.AttachParameter;
}

Expand Down Expand Up @@ -520,7 +539,7 @@ public static void AppendTag(this ISpan span, Object tag, Int64 value)

if (tag != null && span is DefaultSpan ds && ds.TraceFlag > 0)
{
var maxLength = ds.Builder?.Tracer?.MaxTagLength ?? 1024;
var maxLength = ds.Tracer?.MaxTagLength ?? 1024;
if (span.Tag.IsNullOrEmpty())
span.SetTag(tag);
else if (span.Tag.Length < maxLength)
Expand All @@ -546,7 +565,7 @@ public static void AppendTag(this ISpan span, HttpResponseMessage response)

if (span is DefaultSpan ds && ds.TraceFlag > 0)
{
var maxLength = ds.Builder?.Tracer?.MaxTagLength ?? 1024;
var maxLength = ds.Tracer?.MaxTagLength ?? 1024;
if (span.Tag.IsNullOrEmpty() || span.Tag.Length < maxLength)
{
// 判断类型和长度
Expand Down
32 changes: 24 additions & 8 deletions NewLife.Core/Log/ISpanBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class DefaultSpanBuilder : ISpanBuilder
[XmlIgnore, ScriptIgnore, IgnoreDataMember]
public ITracer? Tracer { get; set; }

/// <summary>操作名</summary>
/// <summary>埋点名</summary>
public String Name { get; set; } = null!;

/// <summary>开始时间。Unix毫秒</summary>
Expand Down Expand Up @@ -133,17 +133,25 @@ public void Init(String name)
public virtual ISpan Start()
{
DefaultSpan? span = null;
if (Tracer is DefaultTracer tracer)
var tracer = Tracer;
if (tracer is DefaultTracer tracer2)
{
span = tracer.SpanPool.Get() as DefaultSpan;
if (span != null) span.Builder = this;
span = tracer2.SpanPool.Get() as DefaultSpan;
if (span != null)
{
span.Tracer = tracer2;
span.Name = Name;
#pragma warning disable CS0612 // 类型或成员已过时
span.Builder = this;
#pragma warning restore CS0612 // 类型或成员已过时
}
}

span ??= new DefaultSpan(this);
span ??= new DefaultSpan(tracer!);
span.Start();

// 指示当前节点开始的后续节点强制采样
if (span.TraceFlag == 0 && Tracer != null && Total < Tracer.MaxSamples) span.TraceFlag = 1;
if (span.TraceFlag == 0 && tracer != null && Total < tracer.MaxSamples) span.TraceFlag = 1;

return span;
}
Expand Down Expand Up @@ -205,9 +213,9 @@ public virtual void Finish(ISpan span)
}

// 如果埋点没有加入链表,则归还对象池
if (!flag && Tracer is DefaultTracer tracer2 && ds != null)
if (!flag && Tracer is DefaultTracer tracer2)
{
ds.Clear();
ds?.Clear();
tracer2.SpanPool.Return(span);
}
}
Expand All @@ -232,13 +240,21 @@ internal void Return(IList<ISpan>? spans)
/// <summary>清空已有数据</summary>
public void Clear()
{
//!!! 不能清空Tracer,否则长时间Span完成时,Builder已被处理,Span无法创建新的Builder来统计埋点数据
Tracer = null;

Name = null!;
StartTime = 0;
EndTime = 0;
Value = 0;
Samples = null;
ErrorSamples = null;

_Total = 0;
_Errors = 0;
_Cost = 0;
MaxCost = 0;
MinCost = 0;
}

/// <summary>已重载。</summary>
Expand Down
10 changes: 5 additions & 5 deletions NewLife.Core/Log/ITracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ static DefaultTracer()
{
// 注册默认类型,便于Json反序列化时为接口属性创造实例
var ioc = ObjectContainer.Current;
ioc.AddTransient<ITracer, DefaultTracer>();
ioc.AddTransient<ISpanBuilder, DefaultSpanBuilder>();
ioc.AddTransient<ISpan, DefaultSpan>();
ioc.TryAddTransient<ITracer, DefaultTracer>();
ioc.TryAddTransient<ISpanBuilder, DefaultSpanBuilder>();
ioc.TryAddTransient<ISpan, DefaultSpan>();
}
#endregion

Expand Down Expand Up @@ -248,7 +248,7 @@ public virtual ISpan NewSpan(String name, Object? tag)
{
// 头尾是Xml/Json时,使用字符串格式
var total = pk.Total;
if (total >= 2 && (pk[0] == '{' || pk[0] == '<' || pk[total - 1] == '}' || pk[total - 1] == '>'))
if (total >= 2 && (pk[0] == '{' || pk[0] == '<') && (pk[total - 1] == '}' || pk[total - 1] == '>'))
span.Tag = pk.ToStr(null, 0, len);
else
span.Tag = pk.ToHex(len / 2);
Expand Down Expand Up @@ -406,7 +406,7 @@ private static ISpan CreateSpan(ITracer tracer, String method, Uri uri, HttpRequ

if (span is DefaultSpan ds && ds.TraceFlag > 0 && request != null)
{
var maxLength = ds.Builder?.Tracer?.MaxTagLength ?? 1024;
var maxLength = ds.Tracer?.MaxTagLength ?? 1024;
if (request.Content is ByteArrayContent content &&
content.Headers.ContentLength != null &&
content.Headers.ContentLength < 1024 * 8 &&
Expand Down
2 changes: 1 addition & 1 deletion NewLife.Core/Model/Actor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ protected override void Dispose(Boolean disposing)
{
if (Active) return _task;

if (Tracer == null && TracerParent is DefaultSpan ds) Tracer = ds.Builder?.Tracer;
if (Tracer == null && TracerParent is DefaultSpan ds) Tracer = ds.Tracer;

using var span = Tracer?.NewSpan("actor:Start", Name);

Expand Down
2 changes: 1 addition & 1 deletion NewLife.Core/Net/SessionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ public Int32 Send(ReadOnlySpan<Byte> data)

if (!Open() || Client == null) return null;

using var span = Tracer?.NewSpan($"net:{Name}:Receive", BufferSize + "");
using var span = Tracer?.NewSpan($"net:{Name}:Receive");
try
{
var pk = new OwnerPacket(BufferSize);
Expand Down
4 changes: 2 additions & 2 deletions NewLife.Core/Net/UdpSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public IOwnerPacket Receive()
if (Disposed) throw new ObjectDisposedException(GetType().Name);
if (Server?.Client == null) throw new InvalidOperationException(nameof(Server));

using var span = Tracer?.NewSpan($"net:{Name}:Receive", Server.BufferSize + "");
using var span = Tracer?.NewSpan($"net:{Name}:Receive");
try
{
var ep = Remote.EndPoint as EndPoint;
Expand All @@ -314,7 +314,7 @@ public IOwnerPacket Receive()
if (Disposed) throw new ObjectDisposedException(GetType().Name);
if (Server?.Client == null) throw new InvalidOperationException(nameof(Server));

using var span = Tracer?.NewSpan($"net:{Name}:Receive", Server.BufferSize + "");
using var span = Tracer?.NewSpan($"net:{Name}:Receive");
try
{
var ep = Remote.EndPoint as EndPoint;
Expand Down
14 changes: 12 additions & 2 deletions Samples/Zero.HttpServer/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public static async Task HttpClientTest()
XTrace.WriteLine("");
XTrace.WriteLine("Http客户端开始连接!");

var tracer = DefaultTracer.Instance;
using var span = tracer?.NewSpan(nameof(HttpClientTest));

// 基础请求
var client = new HttpClient
{
Expand All @@ -27,8 +30,11 @@ public static async Task HttpClientTest()
XTrace.WriteLine(html);

// Api接口请求
var http = new ApiHttpClient("http://127.0.0.5:8080");
http.Log = XTrace.Log;
var http = new ApiHttpClient("http://127.0.0.5:8080")
{
Tracer = tracer,
Log = XTrace.Log
};

// 请求接口,返回data部分
var rs = await http.GetAsync<String>("/user", new { act = "Delete", uid = 1234 });
Expand Down Expand Up @@ -68,9 +74,13 @@ public static async Task WebSocketClientTest()
XTrace.WriteLine("");
XTrace.WriteLine("WebSocketClient开始连接!");

var tracer = DefaultTracer.Instance;
using var span = tracer?.NewSpan(nameof(WebSocketClientTest));

var client = new WebSocketClient("ws://127.0.0.7:8080/ws")
{
Name = "小ws客户",
Tracer = tracer,
Log = XTrace.Log
};
if (client is TcpSession tcp) tcp.MaxAsync = 0;
Expand Down
8 changes: 4 additions & 4 deletions XUnitTest.Core/Log/TracerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ public void HttpRequestMessageUriTest()
var request = new HttpRequestMessage(HttpMethod.Get, url);
var span = tracer.NewSpan(request) as DefaultSpan;

Assert.Equal("http://sso.newlifex.com/user/query", span.Builder.Name);
Assert.Equal("http://sso.newlifex.com/user/query", span.Name);
Assert.StartsWith("GET http://sso.newlifex.com/user/query?id=12345", span.Tag);
}

Expand All @@ -389,7 +389,7 @@ public void HttpRequestMessageUriTest()
var request = new HttpRequestMessage(HttpMethod.Get, url);
var span = tracer.NewSpan(request) as DefaultSpan;

Assert.Equal("user/query", span.Builder.Name);
Assert.Equal("user/query", span.Name);
Assert.StartsWith("GET user/query?id=12345", span.Tag);
}
}
Expand All @@ -404,7 +404,7 @@ public void WebRequestUriTest()
var request = WebRequest.CreateHttp(url);

Check warning on line 404 in XUnitTest.Core/Log/TracerTests.cs

View workflow job for this annotation

GitHub Actions / test

'WebRequest.CreateHttp(string)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
var span = tracer.NewSpan(request) as DefaultSpan;

Assert.Equal("http://sso.newlifex.com/user/query", span.Builder.Name);
Assert.Equal("http://sso.newlifex.com/user/query", span.Name);
Assert.Equal("GET http://sso.newlifex.com/user/query?id=12345", span.Tag);
}

Expand All @@ -413,7 +413,7 @@ public void WebRequestUriTest()
// var request = WebRequest.CreateHttp(new Uri(url, UriKind.RelativeOrAbsolute));
// var span = tracer.NewSpan(request) as DefaultSpan;

// Assert.Equal("http://sso.newlifex.com/user/query", span.Builder.Name);
// Assert.Equal("http://sso.newlifex.com/user/query", span.Name);
// Assert.Equal("/user/query?id=12345", span.Tag);
//}
}
Expand Down

0 comments on commit b963566

Please sign in to comment.