diff --git a/NewLife.Core/Log/ISpan.cs b/NewLife.Core/Log/ISpan.cs index 31e6abb3f..c360138fb 100644 --- a/NewLife.Core/Log/ISpan.cs +++ b/NewLife.Core/Log/ISpan.cs @@ -22,6 +22,9 @@ public interface ISpan : IDisposable /// 唯一标识。随线程上下文、Http、Rpc传递,作为内部片段的父级 String Id { get; set; } + /// 埋点名 + String Name { get; set; } + /// 父级片段标识 String? ParentId { get; set; } @@ -64,13 +67,22 @@ public interface ISpan : IDisposable public class DefaultSpan : ISpan { #region 属性 + /// 跟踪器 + [XmlIgnore, ScriptIgnore, IgnoreDataMember] + public ITracer? Tracer { get; set; } + /// 构建器 + [Obsolete] [XmlIgnore, ScriptIgnore, IgnoreDataMember] public ISpanBuilder? Builder { get; set; } /// 唯一标识。随线程上下文、Http、Rpc传递,作为内部片段的父级 public String Id { get; set; } = null!; + /// 埋点名 + [XmlIgnore, ScriptIgnore, IgnoreDataMember] + public String Name { get; set; } = null!; + /// 父级片段标识 public String? ParentId { get; set; } @@ -115,8 +127,8 @@ public class DefaultSpan : ISpan public DefaultSpan() { } /// 实例化 - /// - public DefaultSpan(ISpanBuilder builder) => Builder = builder; + /// + public DefaultSpan(ITracer tracer) => Tracer = tracer; static DefaultSpan() { @@ -214,20 +226,22 @@ 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 // 类型或成员已过时 } /// 抛弃埋点,不计入采集 @@ -235,7 +249,9 @@ protected virtual void Finish() public virtual void Abandon() { _finished = 1; - Builder = null; + //Builder = null; + + // 不能还给对象池,因为外部还有引用,Dispose尚未调用 } /// 设置错误信息,ApiException除外 @@ -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; } @@ -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) @@ -296,8 +312,12 @@ public virtual void SetTag(Object? tag) /// 清空已有数据 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; @@ -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; } @@ -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) @@ -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) { // 判断类型和长度 diff --git a/NewLife.Core/Log/ISpanBuilder.cs b/NewLife.Core/Log/ISpanBuilder.cs index ee4bafe24..d3e521583 100644 --- a/NewLife.Core/Log/ISpanBuilder.cs +++ b/NewLife.Core/Log/ISpanBuilder.cs @@ -65,7 +65,7 @@ public class DefaultSpanBuilder : ISpanBuilder [XmlIgnore, ScriptIgnore, IgnoreDataMember] public ITracer? Tracer { get; set; } - /// 操作名 + /// 埋点名 public String Name { get; set; } = null!; /// 开始时间。Unix毫秒 @@ -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; } @@ -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); } } @@ -232,13 +240,21 @@ internal void Return(IList? spans) /// 清空已有数据 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; } /// 已重载。 diff --git a/NewLife.Core/Log/ITracer.cs b/NewLife.Core/Log/ITracer.cs index 4c44bc5df..adbe67ed9 100644 --- a/NewLife.Core/Log/ITracer.cs +++ b/NewLife.Core/Log/ITracer.cs @@ -66,9 +66,9 @@ static DefaultTracer() { // 注册默认类型,便于Json反序列化时为接口属性创造实例 var ioc = ObjectContainer.Current; - ioc.AddTransient(); - ioc.AddTransient(); - ioc.AddTransient(); + ioc.TryAddTransient(); + ioc.TryAddTransient(); + ioc.TryAddTransient(); } #endregion @@ -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); @@ -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 && diff --git a/NewLife.Core/Model/Actor.cs b/NewLife.Core/Model/Actor.cs index 9742f76df..f0d651fd0 100644 --- a/NewLife.Core/Model/Actor.cs +++ b/NewLife.Core/Model/Actor.cs @@ -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); diff --git a/NewLife.Core/Net/SessionBase.cs b/NewLife.Core/Net/SessionBase.cs index baa9a1bd3..94ac808fb 100644 --- a/NewLife.Core/Net/SessionBase.cs +++ b/NewLife.Core/Net/SessionBase.cs @@ -337,7 +337,7 @@ public Int32 Send(ReadOnlySpan 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); diff --git a/NewLife.Core/Net/UdpSession.cs b/NewLife.Core/Net/UdpSession.cs index e21ea7873..17b99f756 100644 --- a/NewLife.Core/Net/UdpSession.cs +++ b/NewLife.Core/Net/UdpSession.cs @@ -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; @@ -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; diff --git a/Samples/Zero.HttpServer/ClientTest.cs b/Samples/Zero.HttpServer/ClientTest.cs index 4e2788974..b411710f3 100644 --- a/Samples/Zero.HttpServer/ClientTest.cs +++ b/Samples/Zero.HttpServer/ClientTest.cs @@ -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 { @@ -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("/user", new { act = "Delete", uid = 1234 }); @@ -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; diff --git a/XUnitTest.Core/Log/TracerTests.cs b/XUnitTest.Core/Log/TracerTests.cs index a07543933..accffdb8a 100644 --- a/XUnitTest.Core/Log/TracerTests.cs +++ b/XUnitTest.Core/Log/TracerTests.cs @@ -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); } @@ -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); } } @@ -404,7 +404,7 @@ public void WebRequestUriTest() var request = WebRequest.CreateHttp(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.Equal("GET http://sso.newlifex.com/user/query?id=12345", span.Tag); } @@ -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); //} }