通过OpenTelemetry Go SDK自定义指标

ARMS提供了常见的应用监控指标,如果您需要自定义指标,可以引入OpenTelemetry Go SDK实现。本文介绍如何自定义指标并在Grafana中查询自定义指标。

前提条件

  • 已经成功接入ARMS应用监控,具体操作,请参见应用接入

  • Go版本为1.21或以上。

  • OpenTelemetry Go SDK 使用1.27.0或以上版本。

步骤一:添加自定义指标

展开查看完整的示例Demo

package main

import (
	"context"
	"fmt"
	"go.opentelemetry.io/otel/attribute"
	"log"
	"math/rand"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/metric"
)

func main() {
	// 设置随机种子
	rand.Seed(time.Now().UnixNano())
	// 创建 Meter
	meter := otel.GetMeterProvider().Meter("demo-meter")

	// 创建 IntCounter
	requestCounter, err := meter.Int64Counter(
		"request_counter",
		metric.WithDescription("请求计数器"),
		metric.WithUnit("1"),
	)
	if err != nil {
		log.Fatalf("创建 IntCounter 失败: %v", err)
	}

	// 创建带标签的 IntCounter
	errorCounter, err := meter.Int64Counter(
		"error_counter",
		metric.WithDescription("错误计数器"),
		metric.WithUnit("1"),
	)
	if err != nil {
		log.Fatalf("创建错误计数器失败: %v", err)
	}

	// 创建业务计数器
	orderCounter, err := meter.Int64Counter(
		"order_counter",
		metric.WithDescription("订单计数器"),
		metric.WithUnit("1"),
	)
	if err != nil {
		log.Fatalf("创建订单计数器失败: %v", err)
	}

	// 创建用户注册计数器
	userRegistrationCounter, err := meter.Int64Counter(
		"user_registration_counter",
		metric.WithDescription("用户注册计数器"),
		metric.WithUnit("1"),
	)
	if err != nil {
		log.Fatalf("创建用户注册计数器失败: %v", err)
	}

	intUpDown, err := meter.Int64UpDownCounter("demo_int64_updown", metric.WithDescription("Int64UpDownCounter 示例"))
	if err != nil {
		log.Fatalf("failed to create int64 updown counter: %v", err)
	}
	intHist, err := meter.Int64Histogram("demo_int64_histogram", metric.WithDescription("Int64Histogram 示例"))
	if err != nil {
		log.Fatalf("failed to create int64 histogram: %v", err)
	}
	floatCnt, err := meter.Float64Counter("demo_float64_counter", metric.WithDescription("Float64Counter 示例"))
	if err != nil {
		log.Fatalf("failed to create float64 counter: %v", err)
	}
	floatHist, err := meter.Float64Histogram("demo_float64_histogram", metric.WithDescription("Float64Histogram 示例"))
	if err != nil {
		log.Fatalf("failed to create float64 histogram: %v", err)
	}

	floatUpDown, err := meter.Float64UpDownCounter("demo_float64_updown", metric.WithDescription("Float64UpDownCounter 示例"))
	if err != nil {
		log.Fatalf("failed to create float64 updown counter: %v", err)
	}

	fmt.Println("=== OpenTelemetry Metrics IntCounter Demo ===")
	fmt.Println("开始模拟业务操作...")
	fmt.Println("Prometheus metrics 将在 http://localhost:8080/metrics 上可用")

	// 模拟业务操作
	go simulateBusinessOperations(
		requestCounter,
		errorCounter,
		orderCounter,
		userRegistrationCounter,
		intUpDown,
		intHist,
		floatCnt,
		floatHist,
		floatUpDown,
	)

	// 启动 HTTP 服务器来暴露 metrics

	// 保持程序运行
	select {}
}

func simulateBusinessOperations(
	requestCounter,
	errorCounter,
	orderCounter,
	userRegistrationCounter metric.Int64Counter,
	intUpDown metric.Int64UpDownCounter,
	intHist metric.Int64Histogram,
	floatCnt metric.Float64Counter,
	floatHist metric.Float64Histogram,
	floatUpDown metric.Float64UpDownCounter,
) {
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()

	requestTypes := []string{"GET", "POST", "PUT", "DELETE"}
	services := []string{"user", "order", "payment", "inventory"}
	statusCodes := []int{200, 400, 401, 403, 500}
	paymentMethods := []string{"credit_card", "debit_card", "paypal", "alipay"}
	ctx := context.Background()
	i := 10
	for range ticker.C {
		// 模拟请求
		requestType := requestTypes[rand.Intn(len(requestTypes))]
		service := services[rand.Intn(len(services))]
		statusCode := statusCodes[rand.Intn(len(statusCodes))]

		// 增加请求计数器
		requestCounter.Add(context.Background(), 1,
			metric.WithAttributes(
				attribute.String("request_type", requestType),
				attribute.String("service", service),
				attribute.Int("code", statusCode),
			),
		)

		// 如果是错误状态码,增加错误计数器
		if statusCode >= 400 {
			errorCounter.Add(context.Background(), 1,
				metric.WithAttributes(
					attribute.String("service", service),
					attribute.Int("code", statusCode),
				),
			)
		}

		// 模拟订单创建
		if requestType == "POST" && service == "order" && statusCode == 200 {
			paymentMethod := paymentMethods[rand.Intn(len(paymentMethods))]
			orderCounter.Add(context.Background(), 1,
				metric.WithAttributes(
					attribute.String("order_type", "new"),
					attribute.String("payment_method", paymentMethod),
					attribute.String("service", service),
				),
			)
		}

		// 模拟用户注册
		if requestType == "POST" && service == "user" && statusCode == 200 {
			userRegistrationCounter.Add(context.Background(), 1,
				metric.WithAttributes(
					attribute.String("registration_source", "web"),
					attribute.String("service", service),
				),
			)
		}
		// Int64UpDownCounter:可增可减
		intUpDown.Add(ctx, 1) // -1,0,1 循环
		// Int64Histogram:记录分布(整数)
		intHist.Record(ctx, int64(100+i))

		// Float64Counter:累加(只能加)
		floatCnt.Add(ctx, 1.5)

		// Float64Histogram:记录浮点分布
		floatHist.Record(ctx, 3.14)

		// Float64UpDownCounter:浮点可增可减
		floatUpDown.Add(ctx, 0.2)

		i++
		fmt.Printf("模拟请求: %s %s (状态码: %d)\n", requestType, service, statusCode)
	}
}
  1. 创建一个Meter。

    meter := otel.GetMeterProvider().Meter("demo-meter")
  2. 通过Meter创建需要的Counter。

    requestCounter, err := meter.Int64Counter(
    		"request_counter",
    		metric.WithDescription("请求计数器"),
    		metric.WithUnit("1"),
    	)

    目前支持的Counter类型如下:

    • Int64Counter

    • Int64UpDownCounter

    • Int64Histogram

    • Float64Counter

    • Float64Histogram

    • Float64UpDownCounter

步骤二:编译部署应用

根据不同的接入环境编译应用,具体操作,请参见开始监控 Golang 应用

步骤三:查看指标并配置告警

  1. ARMS控制台Prometheus监控 > 实例列表页面顶部菜单栏选择应用接入的地域。

    实例类型为Prometheus for 应用监控的实例即为默认集成的应用监控实例。

    2024-10-29_11-56-55

  2. 单击右侧共享版进入 Grafana 页面,然后单击 Explore,选择数据源为上一步对应的 Prometheus 实例名称。

    2025-07-31_16-26-27

  3. 您可以通过 PromQL 简单查询在代码中定义的指标,如下图所示,也可以在 Grafana 中自定义展示大盘

    image

注意事项

  • 目前 ARMS 指标上报间隔为 15 秒一次。

  • 对于 Counter 类指标,ARMS 目前上报的不是累计值,而是一个上报周期内的增加值,即每 15 秒该指标的增加值。