using Newtonsoft.Json.Linq; using Sers.Core.Module.ApiTrace.Collector; using Sers.Core.Module.Message; using Sers.Core.Module.Rpc; using System; using System.Collections.Generic; using Vit.Core.Module.Log; using Vit.Extensions; using zipkin4net; using zipkin4net.Tracers.Zipkin; using zipkin4net.Transport.Http; namespace Sers.ApiTrace.Collector.Zipkin { public class ZipKinCollector : IApiTraceCollector { public void Init(JObject arg) { config = arg.Deserialize(); if (string.IsNullOrEmpty(config.rpcName)) config.rpcName = "ServiceCenter"; Logger.Info("[ApiTrace.ZipKinCollector]加载中", config); } public void AppBeforeStart() { if (config == null) return; #region (x.1)注册和启动 Zipkin if (config.SamplingRate <= 0) config.SamplingRate = 1; TraceManager.SamplingRate = config.SamplingRate; // 在链路追踪控制台获取 Zipkin Endpoint,注意 Endpoint 中不包含“/api/v2/spans”。 HttpZipkinSender httpSender = new HttpZipkinSender(config.zipkinCollectorUrl, "application/json"); var tracer = new ZipkinTracer(httpSender, new JSONSpanSerializer()); TraceManager.RegisterTracer(tracer); TraceManager.Start(new MyLogger()); #endregion Logger.Info("[ApiTrace.ZipKinCollector]加载成功"); } public void AppBeforeStop() { TraceManager.Stop(); } public object TraceStart(RpcContextData rpcData) { Trace trace; #region (x.1)get id(traceId spanId parentSpanId) and create trace try { long spanId = 0; long traceId = 0; long? parentSpanId = null; { var hexStr = rpcData.caller.rid; spanId = hexStr.Substring(0, 16).HexStringToInt64(); } { var hexStr = rpcData.caller_rootRid_Get(); if (hexStr == null) { traceId = spanId; } else { traceId = hexStr.Substring(0, 16).HexStringToInt64(); } } { var hexStr = rpcData.caller_parentRid_Get(); parentSpanId = hexStr?.Substring(0, 16).HexStringToInt64(); } var spanState = new zipkin4net.SpanState(traceId, parentSpanId, spanId, true, false); trace = Trace.CreateFromId(spanState); } catch (Exception ex) { trace = Trace.Create(); Logger.Error(ex); } Trace.Current = trace; #endregion trace.Record(Annotations.ClientSend()); trace.Record(Annotations.Rpc(config.rpcName)); trace.Record(Annotations.ServiceName(rpcData.apiStationName_Get() ?? "")); return trace; } public void TraceEnd(object traceData, RpcContextData rpcData, ApiMessage apiRequestMessage, Func GetApiReplyMessage) { Trace trace = (Trace)traceData; #region rpc data //trace.Record(Annotations.Tag("http.url", rpcData.http_url_Get())); //trace.Record(Annotations.Tag("http.method", rpcData.http_method_Get())); //trace.Record(Annotations.Tag("http.path", rpcData.route)); //try //{ // trace.Record(Annotations.Tag("ReqRpc", rpcData.oriJson.ToString())); // string str = apiRequestMessage.value_OriData.ArraySegmentByteToString() ?? ""; // trace.Record(Annotations.Tag("ReqData", str )); //} //catch //{ //} #endregion #region method getTagValue string requestRpc_oriString = null; JObject requestRpc_json = null; string requestData_oriString = null; JObject requestData_json = null; ApiMessage apiResponseMessage = null; string responseRpc_oriString = null; JObject responseRpc_json = null; string responseData_oriString = null; JObject responseData_json = null; string GetTagValue(string valueString) { if (string.IsNullOrEmpty(valueString)) return null; if (!valueString.StartsWith("{{") || !valueString.EndsWith("}}")) return valueString; try { valueString = valueString.Substring(2, valueString.Length - 4); string dataType; string path; var splitIndex = valueString.IndexOf('.'); if (splitIndex < 0) { dataType = valueString; path = ""; } else { dataType = valueString.Substring(0, splitIndex); path = valueString.Substring(splitIndex + 1); } switch (dataType) { case "requestRpc": if (requestRpc_oriString == null) { requestRpc_oriString = apiRequestMessage.rpcContextData_OriData.ArraySegmentByteToString(); } if (string.IsNullOrEmpty(path)) { return requestRpc_oriString; } if (requestRpc_json == null) { requestRpc_json = requestRpc_oriString.Deserialize(); } return requestRpc_json?.SelectToken(path).ConvertToString(); case "requestData": if (requestData_oriString == null) { requestData_oriString = apiRequestMessage.value_OriData.ArraySegmentByteToString(); } if (string.IsNullOrEmpty(path)) { return requestData_oriString; } if (requestData_json == null) { requestData_json = requestData_oriString.Deserialize(); } return requestData_json?.SelectToken(path).ConvertToString(); case "responseRpc": if (apiResponseMessage == null) { apiResponseMessage = GetApiReplyMessage(); } if (responseRpc_oriString == null) { responseRpc_oriString = apiResponseMessage.rpcContextData_OriData.ArraySegmentByteToString(); } if (string.IsNullOrEmpty(path)) { return responseRpc_oriString; } if (responseRpc_json == null) { responseRpc_json = responseRpc_oriString.Deserialize(); } return responseRpc_json?.SelectToken(path).ConvertToString(); case "responseData": if (apiResponseMessage == null) { apiResponseMessage = GetApiReplyMessage(); } if (responseData_oriString == null) { responseData_oriString = apiResponseMessage.value_OriData.ArraySegmentByteToString(); } if (string.IsNullOrEmpty(path)) { return responseData_oriString; } if (responseData_json == null) { responseData_json = responseData_oriString.Deserialize(); } return responseData_json?.SelectToken(path).ConvertToString(); } } catch { } return null; } #endregion //tags config.tags?.IEnumerable_ForEach(item => { var key = GetTagValue(item.Key); var value = GetTagValue(item.Value); if (key != null && value != null) { trace.Record(Annotations.Tag(key, value)); } }); trace.Record(Annotations.ClientRecv()); } Config config; #region Config Model class Config { public float SamplingRate; public string zipkinCollectorUrl; public string rpcName; public string serviceStationName; public IDictionary tags; } #endregion #region MyLogger class MyLogger : ILogger { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void LogError(string message) { Logger.Error("[zipkin]" + message); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void LogInformation(string message) { Logger.Info("[zipkin]" + message); } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void LogWarning(string message) { Logger.log.Log(Level.warn, "[zipkin]" + message); } } #endregion } }