2023年4月26日 星期三

基本的 OpenTelemetry Metrics 設定:讓 Counter 只紀錄差異

上一篇文章中,我用 LongCounter 來計算收到的 event 數量,並且讓 OTEL 每一秒輸出一次資料到 log 裡。不過其實這個作法跟我本來想像的結果不太相同,因為我想像的是例如第一秒收到 5 個 event、第二秒收到 7 個 event,OTEL 輸出的 Metrics 應該要是:

  • 第一個 data point 是 5
  • 第二個 data point 是 7
  • 第三個 data point 是 0

但事實上當時寫出來的範例程式會輸出的結果會是:

  • 第一個 data point 是 5
  • 第二個 data point 是 12
  • 第三個 data point 是 12

也就是說,範例程式紀錄下來的是累計值,是從 application 啟動開始累計到現在的所有數值的加總。但我希望紀錄下來的其實是差異值,每次 Exporter 輸出完 Metrics 後,我希望它重置 counter。

設定 Exporter 輸出差異值

要達成紀錄的是差異值,要做的事情是去設定 Exporter 的 AggregationTemporality。具體設定的方法其實每個 Exporter 都有點不一樣,不過都是在 ...Builder 的階段控制的。這裡繼續以 LoggingMetricExporter 為例,LoggingMetricExporter 設定 temporality 的方法如下:

LoggingMetricExporter.create(AggregationTemporality.DELTA);

測試方法

設定了 AggregationTemporality 之後,把測試的腳本稍微弄複雜一點,大體來說就是改成用 2 個 thread 模擬送出 1M 個 event,然後觀察 OTEL 紀錄到什麼東西。為了簡化測試,我把一些本來隨機的東西都拿掉,會比較方便觀察數字的正確性 XD。

@Override
public void run(String... args) throws Exception {
    log.info("Run");

    produceEvents(1_000_000, 10);

    log.info("Done");
}

private void produceEvents(int numOfEvents, int numOfThreads) {
    var executor = Executors.newFixedThreadPool(numOfThreads);

    for (int i = 0; i < numOfEvents; i++) {
        executor.submit(() -> {
            if (random.nextBoolean()) {
                processor1.receiveEvent(generateRandomEvent());
            } else {
                processor2.receiveEvent(generateRandomEvent());
            }
        });
    }
}

private Event generateRandomEvent() {
    var eventType = "create";

    return Event.builder()
            .eventType(eventType)
            .owner("owner-1")
            .build();
}

完整的範例程式可以參考這裡

執行結果

以下是執行時,OTEL 輸出的結果。

INFO  i.o.e.l.LoggingMetricExporter [PeriodicMetricReader-1] Received a collection of 1 metrics for export.
INFO  i.o.e.l.LoggingMetricExporter [PeriodicMetricReader-1] metric: ImmutableMetricData{resource=Resource{schemaUrl=null, attributes={service.name="otel-example", telemetry.sdk.language="java", telemetry.sdk.name="opentelemetry", telemetry.sdk.version="1.22.0"}}, instrumentationScopeInfo=InstrumentationScopeInfo{name=event-consumer, version=1.0.0, schemaUrl=null, attributes={}}, name=eventType, description=Metrics for the event consuming., unit=1, type=LONG_SUM, data=ImmutableSumData{points=[ImmutableLongPointData{startEpochNanos=1682522136538247300, epochNanos=1682522137553247500, attributes={eventType="create", owner="owner-1"}, value=584412, exemplars=[]}], monotonic=true, aggregationTemporality=DELTA}}
INFO  i.o.e.l.LoggingMetricExporter [PeriodicMetricReader-1] Received a collection of 1 metrics for export.
INFO  i.o.e.l.LoggingMetricExporter [PeriodicMetricReader-1] metric: ImmutableMetricData{resource=Resource{schemaUrl=null, attributes={service.name="otel-example", telemetry.sdk.language="java", telemetry.sdk.name="opentelemetry", telemetry.sdk.version="1.22.0"}}, instrumentationScopeInfo=InstrumentationScopeInfo{name=event-consumer, version=1.0.0, schemaUrl=null, attributes={}}, name=eventType, description=Metrics for the event consuming., unit=1, type=LONG_SUM, data=ImmutableSumData{points=[ImmutableLongPointData{startEpochNanos=1682522137553247500, epochNanos=1682522138548844500, attributes={eventType="create", owner="owner-1"}, value=415588, exemplars=[]}], monotonic=true, aggregationTemporality=DELTA}}
DEBUG i.o.s.m.e.PeriodicMetricReader [PeriodicMetricReader-1] No metric data to export - skipping export.
DEBUG i.o.s.m.e.PeriodicMetricReader [PeriodicMetricReader-1] No metric data to export - skipping export.

文字太長了,稍微摘要一下:

....attributes={eventType="create", owner="owner-1"}, value=584412, exemplars=[]....
....attributes={eventType="create", owner="owner-1"}, value=415588, exemplars=[]....
....No metric data to export - skipping export.
....No metric data to export - skipping export.

可以看到它第一秒紀錄到的 count 是 584,412、第二秒紀錄到的是 415,588,兩秒合計就是 1M,正好是我的測試程式送出的數目。兩秒過去後,因為後續沒有再呼叫 method,所以 OTEL 沒有繼續收到新的 metric data,就會看到 skipping export 的訊息。

沒有留言: