|
# Stardust Go SDK
星尘监控(Stardust)Go SDK,提供 APM 监控和配置中心的接入能力。
## 特性
- ✅ APM 链路追踪
- ✅ 配置中心
- ✅ 心跳性能指标上报
- ✅ 令牌自动续期与 401 重登录
- ✅ 服务端动态采样周期
- ✅ HTTP 入站/出站自动埋点(W3C traceparent 跨语言互通)
- ✅ database/sql 驱动自动埋点
- ✅ Redis 通用包装与可选 go-redis v9 Hook
- ✅ 无第三方依赖,仅使用 Go 标准库
- ✅ 支持 Go 1.18+
## 安装
```bash
go get github.com/NewLifeX/Stardust/SDK/Go/stardust
```
## APM 监控快速开始
```go
package main
import (
"github.com/NewLifeX/Stardust/SDK/Go/stardust"
)
func main() {
// 一行接入:创建、登录并启动自动埋点
tracer := stardust.Init("http://star.example.com:6600", "MyGoApp", "MySecret")
defer tracer.Stop()
// 需要自定义时改用 builder:
// tracer := stardust.NewTracer(server, appID, secret).SetVersion("1.0.0")
// tracer.Start()
// 手动埋点
span := tracer.NewSpan("业务操作", "")
span.Tag = "参数信息"
doSomething()
span.Finish()
}
```
## 自动埋点
`NewTracer` 创建后会自动完成两件事:设为默认追踪器,并把出站埋点安装到 `http.DefaultTransport`。
`Start()` 只负责登录、心跳与上报,不参与埋点安装。后创建的追踪器会成为新的默认追踪器;
多实例场景请显式使用实例方法,或最后创建主实例。
```go
tracer := stardust.NewTracer("http://star.example.com:6600", "MyGoApp", "MySecret")
tracer.SetAutoInstrument(false) // 如不需要默认传输自动埋点,可关闭
tracer.Start()
defer tracer.Stop()
```
### 链路上下文(W3C traceparent)
自动埋点遵循 W3C Trace Context:
```text
traceparent: 00-<trace-id>-<span-id>-<flags>
```
- SDK 生成 32 位小写十六进制 TraceId 与 16 位 SpanId。
- 入站解析 `traceparent`,复用 TraceId 并把上游 SpanId 作为入口片段的 ParentId;缺失或非法时新建链路。
- 为兼容 .NET 历史实现,入站允许短于 32 位的 trace-id。
- 出站以当前片段注入 `traceparent`;调用方已显式设置时不覆盖。
- SDK 自身的登录、心跳、上报与配置中心请求不创建片段,也不注入 `traceparent`。
手动读取/传播上下文:
```go
ctx, span := tracer.NewSpanFromContext(ctx, "业务操作")
defer span.Finish()
if current := stardust.SpanFromContext(ctx); current != nil {
fmt.Println(current.TraceID, current.ID)
}
```
### HTTP 出站
默认传输已自动埋点,普通 `http.Client` 即可;也可显式包装:
```go
client := tracer.WrapClient(&http.Client{Timeout: 5 * time.Second})
resp, err := client.Do(req.WithContext(ctx))
```
- 片段名称:`{METHOD} {host}{path}`,Tag 为完整 URL。
- 客户端 4xx/5xx 或网络异常记为错误片段。
### HTTP 入站(一行接入)
```go
handler := stardust.WrapHandler(mux)
http.ListenAndServe(":8080", handler)
// 或处理函数
http.HandleFunc("/api", stardust.WrapHandlerFunc(api))
```
- 片段名称:`{METHOD} {path}`,Tag 为原始 RequestURI。
- 服务端 5xx 或 panic 记为错误片段,panic 会记录后原样抛出。
- 嵌套包装会通过请求标记去重,同一请求只产生一个入口片段。
- Gin/Echo 等框架请使用其自身中间件 + `NewSpanFromContext`,或把框架的 `http.Handler` 交给 `WrapHandler`。
### 数据库(一行接入)
```go
// 方式一:直接打开(driver.Driver 为真实驱动实例)
db, err := stardust.OpenDB(&sqlite3.SQLiteDriver{}, "file:app.db", tracer)
// 方式二:注册包装后的驱动
sql.Register("mydb", stardust.WrapDriver(&mysql.MySQLDriver{}, tracer))
// 方式三:包装连接器
db := sql.OpenDB(stardust.WrapConnector(connector, tracer))
```
- 片段名称:`db:{system}:{action}:{tables}`,如 `db:sqlite:select:users`;Tag 为 SQL 文本(上报时按 `MaxTagLength` 截断)。
- 表名识别 `from`、`insert into`、`update`、`join`、`drop table`、`truncate table`,并去掉 `_20240101` 等日期分表后缀。
- 覆盖 `driver.Driver/Conn/Stmt/Tx`,以及 `ExecerContext`、`QueryerContext`、`ConnBeginTx`、`ConnPrepareContext`、`Pinger`、`SessionResetter`、`Validator` 等可选接口。
- 底层返回 `driver.ErrSkip` 时按 `database/sql` 约定回退,不产生多余片段。
- 上下文已有片段时作为其子片段,否则新建链路。
### Redis
核心模块提供通用包装,适合任意 Redis 客户端:
```go
err := tracer.TraceRedis(ctx, "GET", func(ctx context.Context) error {
return rdb.Get(ctx, "user:1").Err()
})
// 带参数摘要
err = tracer.TraceRedisWithTag(ctx, "SET", "user:1", func(ctx context.Context) error {
return rdb.Set(ctx, "user:1", "alice", 0).Err()
})
```
go-redis v9 可使用独立模块 `SDK/Go/stardust-redis`(核心包仍保持零第三方依赖):
```go
import stardustredis "github.com/NewLifeX/Stardust/SDK/Go/stardust-redis"
rdb := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
rdb.AddHook(stardustredis.NewHook(tracer))
```
> `stardust-redis` 通过核心模块伪版本依赖核心包;仓库内 `SDK/Go/go.work` 已同时加入两个模块,开发和测试无需手工 `replace`。若核心提交尚未发布,可在业务 `go.mod` 中临时 `replace` 到本地源码。核心包仍保持零第三方依赖。
- 片段名称:`redis:{COMMAND}`,如 `redis:GET`;Tag 为 key/参数摘要。
- 管道记为 `redis:PIPELINE`,Tag 为前几个命令名称。
### 自动埋点限制
- 只覆盖 Go 标准库 `net/http` 与 `database/sql`;gRPC、消息队列等需手动埋点。
- 标准库没有路由模板信息,入站片段名称使用实际路径;需要模板名称时请结合框架中间件自行创建片段。
- 数据库表名基于简单 SQL 关键字解析,复杂 SQL、动态表名或存储过程可能无法识别。
- 数据库类型从驱动类型名推断(如 sqlite/mysql/postgres/sqlserver/oracle),自定义驱动可能显示为类型名前缀。
- 核心模块不内置 Redis 客户端依赖;go-redis 需额外引入 `stardust-redis` 模块。
- 自动埋点自身不改变业务返回值;上游 `traceparent` 非法时自动降级为新链路。
## APM 配置项
所有设置方法均返回 `*Tracer`,支持链式调用:
| 方法 | 说明 | 默认值 |
|------|------|--------|
| `SetAppName(name)` | 应用名称 | 与 AppId 相同 |
| `SetVersion(version)` | 应用版本号。建议显式设置(SDK 默认 `1.0`);服务端只有收到 Version 才下发 `Excludes`/`EnableMeter` | `1.0` |
| `SetIP(ip)` | 本机 IP。为空时自动枚举网卡(跳过回环地址,必要时 UDP 探测) | 自动获取 |
| `SetEnableMeter(enable)` | 是否在监控上报中携带进程性能信息(心跳始终携带) | `true` |
| `SetPeriod(seconds)` | 初始采样周期(秒),服务端响应会覆盖 | `60` |
| `SetHTTPClient(client)` | 自定义 HTTP 客户端 | 10 秒超时 |
| `SetAutoInstrument(enable)` | 开/关默认 HTTP 传输自动埋点 | `true`(`NewTracer` 自动安装) |
> 登录请求会携带 `Version` 与 `IP`;上报响应中的 `Period`、`MaxSamples`、`MaxErrors`、`Timeout`、`MaxTagLength`、`Excludes`、`EnableMeter` 会即时应用到后续采样。
## 心跳性能指标
启用后,心跳请求(`POST /App/Ping`)的请求体为 `AppInfo`,包含:
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | int | 进程 ID |
| `Name` / `Version` / `IP` | string | 应用名、版本、本机 IP |
| `CommandLine` | string | 启动命令行 |
| `UserName` / `MachineName` | string | 当前用户与机器名 |
| `StartTime` | string | 进程启动时间(RFC3339) |
| `ProcessorTime` / `CpuUsage` | int64 / float64 | 处理器时间与 CPU 占比(Go SDK 暂为 0) |
| `WorkingSet` / `HeapSize` | int64 | 进程内存(`MemStats.Sys`)与堆分配(`HeapAlloc`) |
| `Threads` | int | Goroutine 数量 |
| `GCCount` | int | 距上次采集的 GC 次数增量 |
| `Time` | int64 | 当前 Unix 毫秒时间戳 |
其余线程池、句柄、连接数字段保留协议兼容,Go SDK 暂填 0。
## 令牌与采样周期
- 登录响应返回 `Token` 与 `Expire`(秒);令牌缺失或距离过期不足 5 分钟时自动重新登录。
- 心跳响应返回新 `Token` 时自动更新本地令牌。
- 服务端返回 HTTP 401 或 `code=401` 时,自动重新登录并重试一次。
- 上报间隔由服务端下发的 `Period` 动态决定,修改后下一次等待即生效;`Stop()` 可立即退出等待。
## 配置中心快速开始
```go
package main
import (
"fmt"
"github.com/NewLifeX/Stardust/SDK/Go/stardust"
)
func main() {
config := stardust.NewConfigClient("http://star.example.com:6600", "MyGoApp", "MySecret")
config.Start()
defer config.Stop()
// 获取配置
value := config.Get("database.host")
fmt.Println("Database Host:", value)
// 监听配置变更
config.OnChange(func(configs map[string]string) {
fmt.Println("配置已更新:", configs)
})
select {}
}
```
## 完整文档
详细文档请参考:[/Doc/SDK/stardust-sdk-go.md](/NewLife/Stardust/Blob/master/SDK/Go/stardust/../../../Doc/SDK/stardust-sdk-go.md)
## 框架集成
### Gin 框架
```go
import (
"github.com/gin-gonic/gin"
"github.com/NewLifeX/Stardust/SDK/Go/stardust"
)
var tracer = stardust.NewTracer("http://star.example.com:6600", "MyGinApp", "secret")
func StardustMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
name := c.Request.Method + " " + c.Request.URL.Path
span := tracer.NewSpan(name, "")
span.Tag = c.Request.Method + " " + c.Request.RequestURI
defer span.Finish()
c.Next()
}
}
```
## License
MIT License
|