如何在Go SDK的OTEL实现中采集应用CPU信息?
获取OTEL runtime扩展的CPU使用率指标
你当前使用的OTEL runtime包仅聚焦Go运行时的内存、GC等专属指标,CPU使用率属于进程级系统指标,需要通过以下两种方式扩展采集:
方法一:引入Prometheus进程指标收集器(推荐)
Go OTEL生态中,进程级CPU指标可借助Prometheus官方的进程收集器实现,无需手动计算:
- 引入依赖
import ( "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/semconv/v1.21.0" )
- 整合到你的Meter Provider
在初始化meterProvider时,将进程收集器注册到OTEL的Prometheus导出器中:
// 初始化Prometheus导出器 exporter, err := prometheus.New() if err != nil { // 处理初始化错误 } // 创建带进程指标的Meter Provider meterProvider := metric.NewMeterProvider( metric.WithReader(exporter), metric.WithResource(resource.NewWithAttributes( semconv.SchemaURL, semconv.ServiceNameKey.String("your-service-name"), )), ) // 注册进程收集器,自动采集CPU、内存等系统级指标 exporter.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})) // 启动原有的runtime指标采集 runtime.Start(runtime.WithMeterProvider(meterProvider), runtime.WithMinimumReadMemStatsInterval(time.Second))
完成后会自动生成process_cpu_seconds_total等CPU相关指标,通过监控系统计算该指标的增长率即可得到实时CPU使用率。
方法二:手动采集CPU时间(轻量方案)
如果不想引入额外依赖,可通过Go标准库手动计算CPU使用率:
- 定义OTEL仪表
import ( "context" "go.opentelemetry.io/otel/metric" "runtime" "syscall" "time" ) var cpuUsage metric.Float64Gauge func initCPUMetric(meter metric.Meter) error { var err error cpuUsage, err = meter.Float64Gauge( "runtime.go.cpu.usage", metric.WithDescription("当前进程CPU使用率(0-1)"), ) return err }
- 定时采集并计算
func startCPUCollection(ctx context.Context) { var prevUser, prevSys uint64 // 获取初始CPU时间 user, sys, err := getProcessCPUTime() if err == nil { prevUser, prevSys = user, sys } ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-ctx.Done(): return case <-ticker.C: user, sys, err := getProcessCPUTime() if err != nil { continue } // 计算时间差并转换为秒 userDiff := float64(user - prevUser) / 1e9 sysDiff := float64(sys - prevSys) / 1e9 totalDiff := userDiff + sysDiff // 结合CPU核心数计算使用率 cpuCount := float64(runtime.NumCPU()) usage := totalDiff / cpuCount cpuUsage.Record(ctx, usage) prevUser, prevSys = user, sys } } } // 获取进程用户态/内核态CPU时间(纳秒) func getProcessCPUTime() (user, sys uint64, err error) { var rusage syscall.Rusage err = syscall.Getrusage(syscall.RUSAGE_SELF, &rusage) if err != nil { return 0, 0, err } user = uint64(rusage.Utime.Nano()) sys = uint64(rusage.Stime.Nano()) return }
- 在应用初始化时启动采集
// 初始化MeterProvider后 meter := meterProvider.Meter("your-meter-name") if err := initCPUMetric(meter); err == nil { go startCPUCollection(context.Background()) } // 启动原runtime指标采集 runtime.Start(runtime.WithMeterProvider(meterProvider), runtime.WithMinimumReadMemStatsInterval(time.Second))
注意事项
- 方法一的进程收集器会自动处理跨平台兼容、多核心计算等细节,稳定性更高,优先推荐。
- 手动采集方案需注意跨平台适配(如Windows系统需替换
syscall相关逻辑)。
内容的提问来源于stack exchange,提问作者Wiki
相关产品推荐
相关产品推荐

