解决MySql布尔型新旧版本兼容问题,采用枚举来表示布尔型的数据表。由正向工程赋值
|
# 链路追踪ITracer
## 概述
å¯è§‚测性是衡é‡çŽ°ä»£åº”ç”¨è´¨é‡çš„æ ¸å¿ƒæŒ‡æ ‡ä¹‹ä¸€ã€‚NewLife.Core æä¾›äº†ä¸€å¥—完整的链路追踪规范:`ITracer`/`ISpan`,用于实现轻é‡çº§ APM(Application Performance Monitoring)。
ä¸Žä¼ ç»Ÿ APM 系统ä¸åŒï¼ŒNewLife 的链路追踪采用**æœ¬åœ°é‡‡æ ·+统计**的方å¼ï¼š
- åœ¨æœ¬åœ°å®Œæˆæ•°æ®é‡‡æ ·å’Œåˆæ¥ç»Ÿè®¡
- 仅上报统计数æ®å’Œå°‘é‡é‡‡æ ·æ ·æœ¬
- æžå¤§èŠ‚çœç½‘ç»œä¼ è¾“å’Œå˜å‚¨æˆæœ¬
NewLife 全系列项目(30+ 组件)å‡å†…置了 `ITracer` 埋点,开å‘者å¯ä»¥ï¼š
1. æ— ä¾µå…¥åœ°èŽ·å–组件è¿è¡ŒæŒ‡æ ‡
2. 接入星尘监控平å°å®žçŽ°åˆ†å¸ƒå¼è¿½è¸ª
3. 基于规范扩展自定义埋点
**Nuget 包**: `NewLife.Core`
**æºç **: [NewLife.Core/Log/ITracer.cs](https://github.com/NewLifeX/X/blob/master/NewLife.Core/Log/ITracer.cs)
---
## 快速入门
### 基础用法
最简å•的埋点示例:
```csharp
using var span = tracer?.NewSpan("æ“作åç§°");
```
使用 `using` 关键å—ç¡®ä¿ span åœ¨ä½œç”¨åŸŸç»“æŸæ—¶è‡ªåŠ¨å®Œæˆï¼Œè®°å½•耗时和次数。
### 完整示例
以下是网络接收数æ®çš„埋点示例:
```csharp
private void Ss_Received(Object? sender, ReceivedEventArgs e)
{
var ns = (this as INetSession).Host;
var tracer = ns?.Tracer;
// 创建埋点,记录接收æ“作
using var span = tracer?.NewSpan($"net:{ns?.Name}:Receive", e.Message);
try
{
OnReceive(e);
}
catch (Exception ex)
{
// æ ‡è®°é”™è¯¯å¹¶è®°å½•å¼‚å¸¸ä¿¡æ¯
span?.SetError(ex, e.Message ?? e.Packet);
throw;
}
}
```
这个示例演示了:
- **创建埋点**:使用动æ€ç”Ÿæˆçš„åç§°
- **æ•°æ®æ ‡ç¾**ï¼šç¬¬äºŒä¸ªå‚æ•° `e.Message` ä½œä¸ºæ•°æ®æ ‡ç¾
- **异常处ç†**:通过 `SetError` æ ‡è®°å¼‚å¸¸åŸ‹ç‚¹
- **自动记录**:`using` è¯å¥è‡ªåŠ¨è®°å½•è€—æ—¶
通过这个埋点,我们å¯ä»¥ï¼š
- 统计接收数æ®åŒ…的次数
- è®°å½•æ¯æ¬¡æŽ¥æ”¶çš„耗时
- 统计异常å‘生次数
- æŸ¥çœ‹é‡‡æ ·æ•°æ®å’Œæ ‡ç¾
---
## æ ¸å¿ƒæŽ¥å£
### ITracer 接å£
性能跟踪器,轻é‡çº§ APM è§„èŒƒçš„æ ¸å¿ƒæŽ¥å£ã€‚
```csharp
public interface ITracer
{
#region 属性
/// <summary>é‡‡æ ·å‘¨æœŸã€‚å•ä½ç§’,默认15ç§’</summary>
Int32 Period { get; set; }
/// <summary>最大æ£å¸¸é‡‡æ ·æ•°ã€‚é‡‡æ ·å‘¨æœŸå†…ï¼Œæœ€å¤šåªè®°å½•指定数é‡çš„æ£å¸¸äº‹ä»¶ï¼Œé»˜è®¤1</summary>
Int32 MaxSamples { get; set; }
/// <summary>æœ€å¤§å¼‚å¸¸é‡‡æ ·æ•°ã€‚é‡‡æ ·å‘¨æœŸå†…ï¼Œæœ€å¤šåªè®°å½•指定数é‡çš„异常事件,默认10</summary>
Int32 MaxErrors { get; set; }
/// <summary>è¶…æ—¶æ—¶é—´ã€‚è¶…è¿‡è¯¥æ—¶é—´æ—¶å¼ºåˆ¶é‡‡æ ·ï¼Œå•使¯«ç§’,默认5000</summary>
Int32 Timeout { get; set; }
/// <summary>æœ€å¤§æ ‡ç¾é•¿åº¦ã€‚超过该长度时将截æ–,默认1024å—符</summary>
Int32 MaxTagLength { get; set; }
/// <summary>å‘http/rpc请求注入TraceIdçš„å‚æ•°åï¼Œä¸ºç©ºè¡¨ç¤ºä¸æ³¨å…¥ï¼Œé»˜è®¤W3Cæ ‡å‡†çš„traceparent</summary>
String? AttachParameter { get; set; }
#endregion
/// <summary>建立Span构建器</summary>
/// <param name="name">æ“作åç§°ï¼Œç”¨äºŽæ ‡è¯†ä¸åŒçš„埋点类型</param>
ISpanBuilder BuildSpan(String name);
/// <summary>开始一个Span</summary>
/// <param name="name">æ“作åç§°ï¼Œç”¨äºŽæ ‡è¯†ä¸åŒçš„埋点类型</param>
ISpan NewSpan(String name);
/// <summary>开始一个Spanï¼ŒæŒ‡å®šæ•°æ®æ ‡ç¾</summary>
/// <param name="name">æ“作åç§°ï¼Œç”¨äºŽæ ‡è¯†ä¸åŒçš„埋点类型</param>
/// <param name="tag">æ•°æ®æ ‡ç¾ï¼Œè®°å½•关键傿•°ä¿¡æ¯</param>
ISpan NewSpan(String name, Object? tag);
/// <summary>æˆªæ–æ‰€æœ‰Span构建器数æ®ï¼Œé‡ç½®é›†åˆ</summary>
ISpanBuilder[] TakeAll();
}
```
#### 关键属性说明
| 属性 | 默认值 | 说明 |
|-----|-------|------|
| `Period` | 15ç§’ | é‡‡æ ·å‘¨æœŸï¼Œå®šæœŸä¸ŠæŠ¥æ•°æ®çš„æ—¶é—´é—´éš” |
| `MaxSamples` | 1 | æ¯ä¸ªå‘¨æœŸå†…记录的æ£å¸¸é‡‡æ ·æ•°ï¼Œç”¨äºŽç»˜åˆ¶è°ƒç”¨ä¾èµ–关系 |
| `MaxErrors` | 10 | æ¯ä¸ªå‘¨æœŸå†…è®°å½•çš„å¼‚å¸¸é‡‡æ ·æ•°ï¼Œç”¨äºŽé—®é¢˜è¯Šæ– |
| `Timeout` | 5000ms | è¶…æ—¶é˜ˆå€¼ï¼Œè¶…è¿‡æ¤æ—¶é—´çš„æ“ä½œä¼šè¢«å¼ºåˆ¶é‡‡æ · |
| `MaxTagLength` | 1024 | æ ‡ç¾æœ€å¤§é•¿åº¦ï¼Œè¶…è¿‡ä¼šè¢«æˆªæ– |
| `AttachParameter` | "traceparent" | HTTP/RPC è¯·æ±‚ä¸æ³¨å…¥ TraceId çš„å‚æ•°å,éµå¾ª W3C æ ‡å‡† |
è¿™äº›å‚æ•°é€šå¸¸ç”±æ˜Ÿå°˜ç›‘控ä¸å¿ƒåЍæ€ä¸‹å‘è°ƒæ•´ï¼Œæ— éœ€æ‰‹åŠ¨é…置。
#### æ ¸å¿ƒæ–¹æ³•
- **`NewSpan(String name)`**: 最常用的方法,创建一个新埋点
- **`NewSpan(String name, Object? tag)`**: åˆ›å»ºåŸ‹ç‚¹å¹¶é™„åŠ æ•°æ®æ ‡ç¾
- **`BuildSpan(String name)`**: èŽ·å–æˆ–创建 SpanBuilder,用于高级场景
### ISpan 接å£
性能跟踪片段,代表一个具体的埋点实例。
```csharp
public interface ISpan : IDisposable
{
/// <summary>å”¯ä¸€æ ‡è¯†ã€‚éšçº¿ç¨‹ä¸Šä¸‹æ–‡ã€Httpã€Rpcä¼ é€’ï¼Œä½œä¸ºå†…éƒ¨ç‰‡æ®µçš„çˆ¶çº§</summary>
String Id { get; set; }
/// <summary>埋点åã€‚ç”¨äºŽæ ‡è¯†ä¸åŒç±»åž‹çš„æ“ä½œ</summary>
String Name { get; set; }
/// <summary>çˆ¶çº§ç‰‡æ®µæ ‡è¯†ã€‚ç”¨äºŽæž„å»ºè°ƒç”¨é“¾</summary>
String? ParentId { get; set; }
/// <summary>è·Ÿè¸ªæ ‡è¯†ã€‚å¯ç”¨äºŽå…³è”多个片段,建立ä¾èµ–关系,éšçº¿ç¨‹ä¸Šä¸‹æ–‡ã€Httpã€Rpcä¼ é€’</summary>
String TraceId { get; set; }
/// <summary>开始时间。Unix毫秒时间戳</summary>
Int64 StartTime { get; set; }
/// <summary>ç»“æŸæ—¶é—´ã€‚Unix毫秒时间戳</summary>
Int64 EndTime { get; set; }
/// <summary>用户数值。记录数å—åž‹æ ‡é‡ï¼Œå¦‚æ¯æ¬¡æ•°æ®åº“æ“ä½œè¡Œæ•°ï¼Œæ˜Ÿå°˜å¹³å°æ±‡æ€»ç»Ÿè®¡</summary>
Int64 Value { get; set; }
/// <summary>æ•°æ®æ ‡ç¾ã€‚è®°å½•ä¸€äº›é™„åŠ æ•°æ®ï¼Œå¦‚è¯·æ±‚å‚æ•°ã€å“应结果ç‰</summary>
String? Tag { get; set; }
/// <summary>错误信æ¯ã€‚记录异常消æ¯</summary>
String? Error { get; set; }
/// <summary>设置错误信æ¯ï¼ŒApiException除外</summary>
void SetError(Exception ex, Object? tag = null);
/// <summary>è®¾ç½®æ•°æ®æ ‡ç¾ã€‚å†…éƒ¨æ ¹æ®æœ€å¤§é•¿åº¦æˆªæ–</summary>
void SetTag(Object tag);
/// <summary>抛弃埋点,ä¸è®¡å…¥é‡‡é›†</summary>
void Abandon();
}
```
#### 关键属性
| 属性 | 类型 | 说明 |
|-----|------|------|
| `Id` | String | Span å”¯ä¸€æ ‡è¯†ï¼Œéµå¾ª W3C æ ‡å‡† |
| `ParentId` | String? | 父级 Span IDï¼Œç”¨äºŽæž„å»ºè°ƒç”¨æ ‘ |
| `TraceId` | String | è·Ÿè¸ªé“¾æ ‡è¯†ï¼ŒåŒä¸€è°ƒç”¨é“¾ä¸çš„æ‰€æœ‰ Span å…±äº«ç›¸åŒ TraceId |
| `StartTime` | Int64 | 开始时间(Unix 毫秒) |
| `EndTime` | Int64 | ç»“æŸæ—¶é—´ï¼ˆUnix 毫秒) |
| `Value` | Int64 | 用户数值,å¯è®°å½•ä¸šåŠ¡æŒ‡æ ‡ï¼ˆå¦‚æ•°æ®åº“行数) |
| `Tag` | String? | æ•°æ®æ ‡ç¾ï¼Œè®°å½•è¯·æ±‚å‚æ•°ã€å“应数æ®ç‰ |
| `Error` | String? | é”™è¯¯ä¿¡æ¯ |
#### æ ¸å¿ƒæ–¹æ³•
- **`SetError(Exception ex, Object? tag)`**: æ ‡è®°å¼‚å¸¸ï¼Œ`ApiException` 类型会被特殊处ç†
- **`SetTag(Object tag)`**: è®¾ç½®æ•°æ®æ ‡ç¾ï¼Œæ”¯æŒå¤šç§ç±»åž‹è‡ªåЍåºåˆ—化
- **`Abandon()`**: 丢弃当å‰åŸ‹ç‚¹ï¼Œå¸¸ç”¨äºŽè¿‡æ»¤æ— 效请求(如404扫æï¼‰
---
## 使用最佳实践
### 1. 注入 ITracer
在 ASP.NET Core 应用ä¸ï¼Œé€šè¿‡æ˜Ÿå°˜æ‰©å±•注入:
```csharp
using NewLife.Stardust.Extensions;
var builder = WebApplication.CreateBuilder(args);
// æ³¨å…¥æ˜Ÿå°˜ï¼ŒåŒ…å« ITracer 实现
builder.Services.AddStardust();
```
详è§ï¼š[æ˜Ÿå°˜åˆ†å¸ƒå¼æœåС平å°](https://newlifex.com/blood/stardust)
### 2. èŽ·å– ITracer 实例
æœ‰å¤šç§æ–¹å¼èŽ·å– `ITracer`:
```csharp
// æ–¹å¼1ï¼šæž„é€ å‡½æ•°æ³¨å…¥ï¼ˆæŽ¨è)
public class MyService
{
private readonly ITracer _tracer;
public MyService(ITracer tracer)
{
_tracer = tracer;
}
}
// æ–¹å¼2:属性注入
public class MyService
{
public ITracer? Tracer { get; set; }
}
// æ–¹å¼3ï¼šä½¿ç”¨å…¨å±€é™æ€å®žä¾‹
var tracer = DefaultTracer.Instance;
```
### 3. 创建埋点
#### 基础埋点
```csharp
using var span = _tracer?.NewSpan("MyOperation");
// 业务逻辑
```
#### å¸¦æ•°æ®æ ‡ç¾çš„埋点
```csharp
var request = new { UserId = 123, Action = "Query" };
using var span = _tracer?.NewSpan("UserQuery", request);
// 业务逻辑
```
#### 带异常处ç†çš„埋点
```csharp
using var span = _tracer?.NewSpan("DatabaseQuery");
try
{
// 执行数æ®åº“查询
var result = await ExecuteQueryAsync();
}
catch (Exception ex)
{
span?.SetError(ex, "查询失败");
throw;
}
```
#### è®°å½•ä¸šåŠ¡æŒ‡æ ‡
```csharp
using var span = _tracer?.NewSpan("BatchProcess");
var count = ProcessRecords();
span.Value = count; // 记录处ç†çš„记录数
```
### 4. 定时任务埋点示例
```csharp
public class DataRetentionService : IHostedService
{
private readonly ITracer _tracer;
private readonly StarServerSetting _setting;
private TimerX? _timer;
public DataRetentionService(StarServerSetting setting, ITracer tracer)
{
_setting = setting;
_tracer = tracer;
}
public Task StartAsync(CancellationToken cancellationToken)
{
// æ¯600ç§’æ‰§è¡Œä¸€æ¬¡ï¼Œéšæœºå»¶è¿Ÿå¯åЍ
_timer = new TimerX(DoWork, null,
DateTime.Today.AddMinutes(Rand.Next(60)),
600 * 1000)
{
Async = true
};
return Task.CompletedTask;
}
private void DoWork(Object? state)
{
var set = _setting;
if (set.DataRetention <= 0) return;
var time = DateTime.Now.AddDays(-set.DataRetention);
var time2 = DateTime.Now.AddDays(-set.DataRetention2);
var time3 = DateTime.Now.AddDays(-set.DataRetention3);
// 创建埋点,记录清ç†ä»»åŠ¡
using var span = _tracer?.NewSpan("DataRetention", new { time, time2, time3 });
try
{
// åˆ é™¤æ—§æ•°æ®
var rs = AppMinuteStat.DeleteBefore(time);
XTrace.WriteLine("åˆ é™¤[{0}]之å‰çš„AppMinuteStat共:{1:n0}", time, rs);
rs = TraceMinuteStat.DeleteBefore(time);
XTrace.WriteLine("åˆ é™¤[{0}]之å‰çš„TraceMinuteStat共:{1:n0}", time, rs);
}
catch (Exception ex)
{
span?.SetError(ex, null);
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.TryDispose();
return Task.CompletedTask;
}
}
```
### 5. è¿‡æ»¤æ— æ•ˆè¯·æ±‚
在 Web 应用ä¸ï¼Œç»å¸¸é‡åˆ°å„ç§æ‰«æè¯·æ±‚,å¯ä»¥ä½¿ç”¨ `Abandon()` 丢弃这些埋点:
```csharp
public IActionResult ProcessRequest()
{
using var span = _tracer?.NewSpan("WebRequest");
// æ£€æµ‹åˆ°æ— æ•ˆè¯·æ±‚ï¼ˆå¦‚404扫æï¼‰
if (IsInvalidScan())
{
span?.Abandon(); // 丢弃埋点,ä¸è®¡å…¥ç»Ÿè®¡
return NotFound();
}
// æ£å¸¸å¤„ç†
return Ok();
}
```
---
## 分布å¼é“¾è·¯è¿½è¸ª
### è·¨æœåŠ¡ä¼ é€’
ISpan æä¾›äº†æ‰©å±•方法,支æŒé€šè¿‡ HTTP/RPC ä¼ é€’è·Ÿè¸ªä¸Šä¸‹æ–‡ï¼š
```csharp
// HTTP 请求注入
var request = new HttpRequestMessage(HttpMethod.Get, url);
span?.Attach(request); // 注入 traceparent 头
await httpClient.SendAsync(request);
// RPC 调用注入
var args = new { UserId = 123 };
span?.Attach(args); // æ³¨å…¥è·Ÿè¸ªå‚æ•°
await rpcClient.InvokeAsync("Method", args);
```
æœåŠ¡ç«¯æ”¶åˆ°è¯·æ±‚åŽï¼Œä¼šè‡ªåŠ¨è§£æž `traceparent` 傿•°ï¼Œæ¢å¤è·Ÿè¸ªä¸Šä¸‹æ–‡ã€‚
### 调用链构建
åŒä¸€ä¸ª `TraceId` 下的所有 Span 会自动形æˆè°ƒç”¨é“¾ï¼š
```
TraceId: ac1b1e8617342790000015eb0ea2a6
├─ DataRetention (父)
├─ SQL:AppMinuteStat.DeleteBefore (å)
├─ SQL:TraceMinuteStat.DeleteBefore (å)
└─ SQL:TraceHourStat.DeleteBefore (å)
```
在星尘监控平å°å¯ä»¥æŸ¥çœ‹ï¼š
- [调用链ç«ç„°å›¾](https://star.newlifex.com/trace?id=ac1b1e8617342790000015eb0ea2a6)
- [调用链日志视图](https://star.newlifex.com/trace?id=ac1b1e8617342790000015eb0ea2a6&layout=detail)
---
## 监控与分æž
### 查看统计数æ®
在星尘监控平å°å¯ä»¥æŸ¥çœ‹åŸ‹ç‚¹ç»Ÿè®¡ï¼š
- 调用总次数
- 异常次数
- å¹³å‡è€—æ—¶ã€æœ€å¤§è€—æ—¶ã€æœ€å°è€—æ—¶
- QPS(æ¯ç§’请求数)
示例:[DataRetention 埋点统计](https://star.newlifex.com/Monitors/traceDayStat?appId=4&itemId=284)
### é‡‡æ ·ç–ç•¥
ITracer é‡‡ç”¨æ™ºèƒ½é‡‡æ ·ç–略:
1. **æ£å¸¸é‡‡æ ·**:æ¯ä¸ªå‘¨æœŸå†…最多ä¿ç•™ `MaxSamples` 个æ£å¸¸æ ·æœ¬ï¼ˆé»˜è®¤1个)
2. **å¼‚å¸¸é‡‡æ ·**:æ¯ä¸ªå‘¨æœŸå†…最多ä¿ç•™ `MaxErrors` ä¸ªå¼‚å¸¸æ ·æœ¬ï¼ˆé»˜è®¤10个)
3. **è¶…æ—¶é‡‡æ ·**:耗时超过 `Timeout` çš„æ“ä½œå¼ºåˆ¶é‡‡æ ·
4. **å…¨é“¾è·¯é‡‡æ ·**:设置 `TraceFlag` 的调用链全é‡é‡‡æ ·
### 本地统计机制
æ¯ä¸ªåŸ‹ç‚¹å对应一个 `SpanBuilder`ï¼Œç”¨äºŽç´¯åŠ ç»Ÿè®¡ï¼š
- **Total**: 总次数
- **Errors**: 异常次数
- **Cost**: 总耗时
- **MaxCost**: 最大耗时
- **MinCost**: 最å°è€—æ—¶
æ¯ä¸ªé‡‡æ ·å‘¨æœŸç»“æŸåŽï¼Œ`SpanBuilder` æ•°æ®æ‰“包上报,然åŽé‡ç½®è®¡æ•°å™¨ã€‚
---
## 高级特性
### 埋点命å规范
建议使用分层命å,便于分类统计:
```csharp
// 网络层
tracer.NewSpan("net:{åè®®}:Receive");
tracer.NewSpan("net:tcp:Send");
// æ•°æ®åº“层
tracer.NewSpan("db:{表å}:Select");
tracer.NewSpan("db:User:Insert");
// 业务层
tracer.NewSpan("biz:{模å—}:{æ“作}");
tracer.NewSpan("biz:Order:Create");
// 外部调用
tracer.NewSpan("http:{æœåŠ¡}:{方法}");
tracer.NewSpan("http:PaymentApi:Pay");
```
### 动æ€å‚数调整
å¯ä»¥åЍæ€è°ƒæ•´é‡‡æ ·å‚数:
```csharp
var tracer = DefaultTracer.Instance;
tracer.Period = 30; // 改为30秒周期
tracer.MaxSamples = 5; // å¢žåŠ æ£å¸¸é‡‡æ ·æ•°
tracer.Timeout = 10000; // 超时阈值改为10秒
tracer.MaxTagLength = 2048; // æ ‡ç¾é•¿åº¦æ”¹ä¸º2K
```
é€šå¸¸è¿™äº›å‚æ•°ç”±æ˜Ÿå°˜ç›‘控ä¸å¿ƒç»Ÿä¸€ä¸‹å‘。
### 自定义 ITracer 实现
å¯ä»¥å®žçŽ°è‡ªå·±çš„ `ITracer`:
```csharp
public class CustomTracer : ITracer
{
public Int32 Period { get; set; } = 15;
public Int32 MaxSamples { get; set; } = 1;
public Int32 MaxErrors { get; set; } = 10;
// ... å®žçŽ°æŽ¥å£æ–¹æ³•
public ISpan NewSpan(String name)
{
// 自定义埋点创建逻辑
var span = new CustomSpan { Name = name };
span.Start();
return span;
}
}
// 注册自定义实现
DefaultTracer.Instance = new CustomTracer();
```
---
## 性能优化
### 对象æ±
`DefaultTracer` å†…ç½®äº†å¯¹è±¡æ± ï¼Œå¤ç”¨ `ISpanBuilder` å’Œ `ISpan` 实例:
```csharp
public IPool<ISpanBuilder> BuilderPool { get; }
public IPool<ISpan> SpanPool { get; }
```
频ç¹åˆ›å»ºçš„åŸ‹ç‚¹å¯¹è±¡ä¼šè‡ªåŠ¨å½’è¿˜å¯¹è±¡æ± ï¼Œå‡å°‘ GC 压力。
### æ ‡ç¾é•¿åº¦æŽ§åˆ¶
å¯¹äºŽå¤§åž‹å¯¹è±¡ï¼Œå»ºè®®æŽ§åˆ¶æ ‡ç¾å†…容:
```csharp
// 䏿ލè:记录整个大对象
span.SetTag(largeObject);
// 推è:åªè®°å½•关键信æ¯
span.SetTag(new { Id = obj.Id, Name = obj.Name });
```
### æ¡ä»¶åŸ‹ç‚¹
对于éžå…³é”®è·¯å¾„,å¯ä»¥æ¡ä»¶æ€§åˆ›å»ºåŸ‹ç‚¹ï¼š
```csharp
ISpan? span = null;
if (_tracer != null && IsImportantOperation())
{
span = _tracer.NewSpan("ImportantOp");
}
try
{
// 业务逻辑
}
finally
{
span?.Dispose();
}
```
---
## 异常处ç†
### ApiException 特殊处ç†
业务异常 `ApiException` ä¸ä¼šè¢«æ ‡è®°ä¸ºé”™è¯¯ï¼š
```csharp
catch (ApiException aex)
{
span?.SetError(aex, request);
// 记录为æ£å¸¸åŸ‹ç‚¹ï¼ŒTagä¸åŒ…å«ä¸šåŠ¡é”™è¯¯ç
}
catch (Exception ex)
{
span?.SetError(ex, request);
// 记录为异常埋点,Errorä¸åŒ…å«å¼‚常消æ¯
}
```
### 异常埋点
æ¯ä¸ªå¼‚常会自动创建独立的异常埋点,便于按异常类型统计:
```csharp
// 原始埋点
using var span = tracer.NewSpan("DatabaseQuery");
try
{
// 抛出异常
throw new TimeoutException("查询超时");
}
catch (Exception ex)
{
span.SetError(ex, query);
// 自动创建 "ex:TimeoutException" 埋点
}
```
---
## 常è§é—®é¢˜
### 1. 埋点数æ®åœ¨æ˜Ÿå°˜å¹³å°çœ‹ä¸åˆ°ï¼Ÿ
æ£€æŸ¥ä»¥ä¸‹å‡ ç‚¹ï¼š
- 确认已注入星尘扩展:`services.AddStardust()`
- 检查网络连接到星尘æœåС噍
- æ£€æŸ¥åº”ç”¨åœ¨æ˜Ÿå°˜å¹³å°æ˜¯å¦å·²æ³¨å†Œ
- 查看本地日志,确认埋点æ£å¸¸åˆ›å»º
### 2. é‡‡æ ·æ•°æ®å¤ªå°‘?
è°ƒæ•´é‡‡æ ·å‚æ•°ï¼š
```csharp
tracer.MaxSamples = 10; // å¢žåŠ æ£å¸¸é‡‡æ ·æ•°
tracer.MaxErrors = 50; // å¢žåŠ å¼‚å¸¸é‡‡æ ·æ•°
tracer.Timeout = 1000; // é™ä½Žè¶…时阈值
```
### 3. 如何关é—埋点?
```csharp
// æ–¹å¼1ï¼šä¸æ³¨å…¥ ITracer
// services.AddStardust(); // 注释掉
// æ–¹å¼2:使用空实现
DefaultTracer.Instance = null;
```
### 4. 如何查看本地埋点数æ®ï¼Ÿ
DefaultTracer 会输出到日志:
```
Tracer[DataRetention] Total=10 Errors=0 Speed=0.02tps Cost=1500ms MaxCost=2000ms MinCost=1000ms
```
### 5. å¹¶å‘场景下 Span 会乱å—?
ä¸ä¼šã€‚`ISpan` 使用 `AsyncLocal<ISpan>` ä¿å˜ä¸Šä¸‹æ–‡ï¼Œæ¯ä¸ªå¼‚æ¥æµç¨‹ç‹¬ç«‹ï¼š
```csharp
public static AsyncLocal<ISpan?> Current { get; }
```
---
## å‚考资料
- **星尘监控平å°**: https://newlifex.com/blood/stardust
- **W3C Trace Context**: https://www.w3.org/TR/trace-context/
- **æºç 仓库**: https://github.com/NewLifeX/X
- **在线文档**: https://newlifex.com/core/tracer
---
## 更新日志
- **2024-12-16**: 完善文档,补充最佳实践和示例代ç
- **2024-08**: æ”¯æŒ .NET 9.0
- **2023-11**: å¢žåŠ `Abandon()` 方法
- **2023-06**: ä¼˜åŒ–å¯¹è±¡æ± ï¼Œæå‡æ€§èƒ½
- **2022**: åˆå§‹ç‰ˆæœ¬å‘布
|