Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

duplicate and wrong counter metrics are generated infinitely #4563

Closed
oliverdding opened this issue Sep 27, 2023 · 6 comments
Closed

duplicate and wrong counter metrics are generated infinitely #4563

oliverdding opened this issue Sep 27, 2023 · 6 comments
Assignees
Labels
bug Something isn't working invalid This doesn't seem right

Comments

@oliverdding
Copy link

oliverdding commented Sep 27, 2023

Description

I wrote a simple http server demo (with fiber) which would receive http request and reporting request count with delta counter to otel collector, where logging exporter would print all the request.

When sending http request one by one, everything worked as expected. But when calling for i in {1..10}; do curl 127.0.0.1:2333/hello/${i}; done, I found that the attributes are wrong (should be ten datapoint with app_id from 1 to 10, but got 7 datapoint with all app_id = 9), and the go SDK kept reporting metrics infinitely.

Logs example:

ResourceMetrics #3
Resource SchemaURL: https://opentelemetry.io/schemas/1.21.0
Resource attributes:
     -> host.name: Str(devcloud)
     -> process.command_args: Slice(["/data/home/oliverdding/Developer/otel-test/cmd/server/__debug_bin308680375"])
     -> process.executable.name: Str(__debug_bin308680375)
     -> process.executable.path: Str(/data/home/oliverdding/Developer/otel-test/cmd/server/__debug_bin308680375)
     -> process.owner: Str(oliverdding)
     -> process.pid: Int(1327097)
     -> process.runtime.description: Str(go version go1.21.0 linux/amd64)
     -> process.runtime.name: Str(go)
     -> process.runtime.version: Str(go1.21.0)
     -> service.name: Str(server)
     -> service.namespace: Str(demo)
     -> service.version: Str(0.1.0)
     -> telemetry.sdk.language: Str(go)
     -> telemetry.sdk.name: Str(opentelemetry)
     -> telemetry.sdk.version: Str(1.18.0)
ScopeMetrics #0
ScopeMetrics SchemaURL: 
InstrumentationScope demo-server 
Metric #0
Descriptor:
     -> Name: demo_server/request_counts
     -> Description: The number of requests received
     -> Unit: 
     -> DataType: Sum
     -> IsMonotonic: true
     -> AggregationTemporality: Delta
NumberDataPoints #0
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #1
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #2
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #3
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #4
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #5
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #6
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
NumberDataPoints #7
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:04.056522685 +0000 UTC
Timestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Value: 1
ResourceMetrics #4
Resource SchemaURL: https://opentelemetry.io/schemas/1.21.0
Resource attributes:
     -> host.name: Str(devcloud)
     -> process.command_args: Slice(["/data/home/oliverdding/Developer/otel-test/cmd/server/__debug_bin308680375"])
     -> process.executable.name: Str(__debug_bin308680375)
     -> process.executable.path: Str(/data/home/oliverdding/Developer/otel-test/cmd/server/__debug_bin308680375)
     -> process.owner: Str(oliverdding)
     -> process.pid: Int(1327097)
     -> process.runtime.description: Str(go version go1.21.0 linux/amd64)
     -> process.runtime.name: Str(go)
     -> process.runtime.version: Str(go1.21.0)
     -> service.name: Str(server)
     -> service.namespace: Str(demo)
     -> service.version: Str(0.1.0)
     -> telemetry.sdk.language: Str(go)
     -> telemetry.sdk.name: Str(opentelemetry)
     -> telemetry.sdk.version: Str(1.18.0)
ScopeMetrics #0
ScopeMetrics SchemaURL: 
InstrumentationScope demo-server 
Metric #0
Descriptor:
     -> Name: demo_server/request_counts
     -> Description: The number of requests received
     -> Unit: 
     -> DataType: Sum
     -> IsMonotonic: true
     -> AggregationTemporality: Delta
NumberDataPoints #0
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #1
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #2
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #3
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #4
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #5
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #6
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
NumberDataPoints #7
Data point attributes:
     -> app_id: Str(9)
     -> interface: Str(hello)
StartTimestamp: 2023-09-27 05:25:06.056762684 +0000 UTC
Timestamp: 2023-09-27 05:25:08.056197636 +0000 UTC
Value: 1
        {"kind": "exporter", "data_type": "metrics", "name": "logging"}

Environment

  • OS: Linux 5.4
  • Architecture: x86
  • Go Version: 1.21.0
  • opentelemetry-go version: v1.18.0

Steps To Reproduce

  1. otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:

exporters:
  logging:
    verbosity: detailed
    sampling_initial: 5
    sampling_thereafter: 200

processors:
  batch:
    send_batch_size: 100
    send_batch_max_size: 1000
    timeout: 10s

extensions:
  health_check:
    endpoint: 127.0.0.1:13133

service:
  extensions: [ health_check ]
  pipelines:
    metrics:
      receivers:
        - otlp
      processors:
        - batch
      exporters:
        - logging
  1. Run the go server

server.go

package main

import (
	"context"
	"os"
	"time"

	"github.com/gofiber/contrib/fiberzap"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/favicon"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/resource"
	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	logger *zap.Logger
)

func init() {
	var cfg zap.Config
	cfg = zap.NewDevelopmentConfig()

	// set time format
	cfg.EncoderConfig.EncodeTime = zapcore.TimeEncoderOfLayout(time.DateTime)
	// add color for console
	cfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
	logger = zap.New(zapcore.NewCore(zapcore.NewConsoleEncoder(cfg.EncoderConfig), zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), zapcore.DebugLevel))
}

func preferDeltaTemporalitySelector(kind sdkmetric.InstrumentKind) metricdata.Temporality {
	switch kind {
	case sdkmetric.InstrumentKindCounter,
		sdkmetric.InstrumentKindObservableCounter,
		sdkmetric.InstrumentKindHistogram:
		return metricdata.DeltaTemporality
	default:
		return metricdata.CumulativeTemporality
	}
}

func initProvider(ctx context.Context) func() {
	res, err := resource.New(ctx,
		resource.WithFromEnv(),
		resource.WithProcess(),
		resource.WithTelemetrySDK(),
		resource.WithHost(),
		resource.WithAttributes(
			semconv.ServiceName("server"),
			semconv.ServiceNamespace("demo"),
			semconv.ServiceVersion("0.1.0"),
		),
	)
	if err != nil {
		logger.Fatal("failed to create resource", zap.Error(err))
	}

	otelAgentAddr, ok := os.LookupEnv("OTEL_EXPORTER_OTLP_ENDPOINT")
	if !ok {
		otelAgentAddr = "127.0.0.1:4317"
	}
	metricExp, err := otlpmetricgrpc.New(
		ctx,
		otlpmetricgrpc.WithInsecure(),
		otlpmetricgrpc.WithEndpoint(otelAgentAddr),
		otlpmetricgrpc.WithCompressor("none"),
		otlpmetricgrpc.WithTemporalitySelector(preferDeltaTemporalitySelector),
	)

	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(
			sdkmetric.NewPeriodicReader(
				metricExp,
				sdkmetric.WithInterval(2*time.Second),
			),
		),
	)
	otel.SetMeterProvider(meterProvider)

	return func() {
		cxt, cancel := context.WithTimeout(ctx, time.Second)
		defer cancel()
		// pushes any last exports to the receiver
		if err := meterProvider.Shutdown(cxt); err != nil {
			otel.Handle(err)
		}
	}
}

var (
	meter        metric.Meter
	requestCount metric.Int64Counter
)

func main() {
	ctx := context.Background()
	otelShutdownHook := initProvider(ctx)
	defer otelShutdownHook()

	meter = otel.Meter("demo-server")

	app := fiber.New(fiber.Config{
		AppName:           "demo-server",
		CaseSensitive:     true,
		DisableKeepalive:  true,
		EnablePrintRoutes: true,
	})
	app.Use(favicon.New())
	app.Use(fiberzap.New(fiberzap.Config{
		Logger:   logger,
		SkipURIs: []string{"/favicon.ico"},
		Fields:   []string{"time", "method", "path", "status", "respHeader:X-Request-ID", "ip", "port", "ua", "latency"},
		Levels:   []zapcore.Level{zapcore.WarnLevel},
	}))

	requestCount, _ = meter.Int64Counter(
		"demo_server/request_counts",
		metric.WithDescription("The number of requests received"),
	)

	app.Get("/hello/:app_id", helloHandler)

	app.Listen("0.0.0.0:2333")
}

func helloHandler(c *fiber.Ctx) error {
	appID := c.Params("app_id")

	logger.Info("hello", zap.String("app_id", appID))

	requestCount.Add(c.UserContext(), 1, metric.WithAttributes(attribute.String("interface", "hello"), attribute.String("app_id", appID)))

	return nil
}

go.mod

module github.com/oliverdding/otel-test

go 1.21.0

require (
	github.com/gofiber/contrib/fiberzap v1.0.2
	github.com/gofiber/fiber/v2 v2.49.2
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.44.0
	go.opentelemetry.io/otel v1.18.0
	go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.41.0
	go.opentelemetry.io/otel/metric v1.18.0
	go.opentelemetry.io/otel/sdk v1.18.0
	go.opentelemetry.io/otel/sdk/metric v0.41.0
	go.uber.org/zap v1.26.0
)

require (
	github.com/andybalholm/brotli v1.0.5 // indirect
	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
	github.com/felixge/httpsnoop v1.0.3 // indirect
	github.com/go-logr/logr v1.2.4 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/golang/protobuf v1.5.3 // indirect
	github.com/google/uuid v1.3.1 // indirect
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
	github.com/klauspost/compress v1.17.0 // indirect
	github.com/mattn/go-colorable v0.1.13 // indirect
	github.com/mattn/go-isatty v0.0.19 // indirect
	github.com/mattn/go-runewidth v0.0.15 // indirect
	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
	github.com/rivo/uniseg v0.4.4 // indirect
	github.com/valyala/bytebufferpool v1.0.0 // indirect
	github.com/valyala/fasthttp v1.50.0 // indirect
	github.com/valyala/tcplisten v1.0.0 // indirect
	go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.41.0 // indirect
	go.opentelemetry.io/otel/trace v1.18.0 // indirect
	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
	go.uber.org/goleak v1.2.1 // indirect
	go.uber.org/multierr v1.11.0 // indirect
	golang.org/x/net v0.15.0 // indirect
	golang.org/x/sys v0.12.0 // indirect
	golang.org/x/text v0.13.0 // indirect
	google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 // indirect
	google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 // indirect
	google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 // indirect
	google.golang.org/grpc v1.58.2 // indirect
	google.golang.org/protobuf v1.31.0 // indirect
)
  1. Send http request with
for i in {1..10}; do curl 127.0.0.1:2333/hello/${i}; done

Expected behavior

There should be ten datapoints, with app_id from 1 to 10.

@pellared
Copy link
Member

pellared commented Sep 28, 2023

@oliverdding I made a shortcut in your repro steps

Fiber

I simplified the code and replaced the usage of otlpmetricgrpc to stdoutmetric

package main

import (
	"context"
	"time"

	"github.com/gofiber/fiber/v2"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/resource"
)

func preferDeltaTemporalitySelector(kind sdkmetric.InstrumentKind) metricdata.Temporality {
	switch kind {
	case sdkmetric.InstrumentKindCounter,
		sdkmetric.InstrumentKindObservableCounter,
		sdkmetric.InstrumentKindHistogram:
		return metricdata.DeltaTemporality
	default:
		return metricdata.CumulativeTemporality
	}
}

func initProvider(ctx context.Context) func() {
	res, err := resource.New(ctx)
	if err != nil {
		panic(err)
	}

	metricExp, err := stdoutmetric.New(
		stdoutmetric.WithTemporalitySelector(preferDeltaTemporalitySelector),
	)
	if err != nil {
		panic(err)
	}

	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(
			sdkmetric.NewPeriodicReader(
				metricExp,
				sdkmetric.WithInterval(2*time.Second),
			),
		),
	)
	otel.SetMeterProvider(meterProvider)

	return func() {
		cxt, cancel := context.WithTimeout(ctx, time.Second)
		defer cancel()
		// pushes any last exports to the receiver
		if err := meterProvider.Shutdown(cxt); err != nil {
			otel.Handle(err)
		}
	}
}

var (
	meter        metric.Meter
	requestCount metric.Int64Counter
)

func main() {
	ctx := context.Background()
	otelShutdownHook := initProvider(ctx)
	defer otelShutdownHook()

	meter = otel.Meter("demo-server")

	app := fiber.New(fiber.Config{
		AppName:           "demo-server",
		CaseSensitive:     true,
		DisableKeepalive:  true,
		EnablePrintRoutes: true,
	})

	requestCount, _ = meter.Int64Counter(
		"demo_server/request_counts",
		metric.WithDescription("The number of requests received"),
	)

	app.Get("/hello/:app_id", helloHandler)

	app.Listen("0.0.0.0:2333")
}

func helloHandler(c *fiber.Ctx) error {
	appID := c.Params("app_id")
	requestCount.Add(c.UserContext(), 1, metric.WithAttributes(attribute.String("interface", "hello"), attribute.String("app_id", appID)))

	return nil
}

Then I executed

for i in {1..10}; do curl 127.0.0.1:2333/hello?id=${i}; done

I got the similar (incorrect) result (just the output is formatted differently):


 ┌───────────────────────────────────────────────────┐ 
 │                    demo-server                    │ 
 │                   Fiber v2.49.2                   │ 
 │               http://127.0.0.1:2333               │ 
 │       (bound on host 0.0.0.0 and port 2333)       │ 
 │                                                   │ 
 │ Handlers ............. 2  Processes ........... 1 │ 
 │ Prefork ....... Disabled  PID ............. 32237 │ 
 └───────────────────────────────────────────────────┘ 

method | path           | name | handlers 
------ | ----           | ---- | -------- 
GET    | /hello/:app_id |      | main.helloHandler 
HEAD   | /hello/:app_id |      | main.helloHandler 
{
	"Resource": null,
	"ScopeMetrics": []
}
{
	"Resource": null,
	"ScopeMetrics": [
		{
			"Scope": {
				"Name": "demo-server",
				"Version": "",
				"SchemaURL": ""
			},
			"Metrics": [
				{
					"Name": "demo_server/request_counts",
					"Description": "The number of requests received",
					"Unit": "",
					"Data": {
						"DataPoints": [
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "10"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 2
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "1"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:11.969268779+02:00",
								"Time": "2023-09-28T20:12:13.969413354+02:00",
								"Value": 1
							},
							{
								"Attributes": null,
								"StartTime": "0001-01-01T00:00:00Z",
								"Time": "0001-01-01T00:00:00Z",
								"Value": 0
							}
						],
						"Temporality": "DeltaTemporality",
						"IsMonotonic": true
					}
				}
			]
		}
	]
}
{
	"Resource": null,
	"ScopeMetrics": [
		{
			"Scope": {
				"Name": "demo-server",
				"Version": "",
				"SchemaURL": ""
			},
			"Metrics": [
				{
					"Name": "demo_server/request_counts",
					"Description": "The number of requests received",
					"Unit": "",
					"Data": {
						"DataPoints": [
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 2
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T20:12:13.969413354+02:00",
								"Time": "2023-09-28T20:12:15.969344522+02:00",
								"Value": 1
							}
						],
						"Temporality": "DeltaTemporality",
						"IsMonotonic": true
					}
				}
			]
		}
	]
}

net/http

I decided to simplify and rewrite server.go to use net/http (as I have no experience with fiber):

package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
	"go.opentelemetry.io/otel/metric"
	sdkmetric "go.opentelemetry.io/otel/sdk/metric"
	"go.opentelemetry.io/otel/sdk/metric/metricdata"
	"go.opentelemetry.io/otel/sdk/resource"
)

func initProvider(ctx context.Context) func() {
	res, err := resource.New(ctx)
	if err != nil {
		panic(err)
	}

	metricExp, err := stdoutmetric.New(stdoutmetric.WithTemporalitySelector(preferDeltaTemporalitySelector))
	if err != nil {
		panic(err)
	}

	meterProvider := sdkmetric.NewMeterProvider(
		sdkmetric.WithResource(res),
		sdkmetric.WithReader(
			sdkmetric.NewPeriodicReader(
				metricExp,
				sdkmetric.WithInterval(2*time.Second),
			),
		),
	)
	otel.SetMeterProvider(meterProvider)

	return func() {
		cxt, cancel := context.WithTimeout(ctx, time.Second)
		defer cancel()
		// pushes any last exports to the receiver
		if err := meterProvider.Shutdown(cxt); err != nil {
			otel.Handle(err)
		}
	}
}

var (
	meter        metric.Meter
	requestCount metric.Int64Counter
)

func preferDeltaTemporalitySelector(kind sdkmetric.InstrumentKind) metricdata.Temporality {
	switch kind {
	case sdkmetric.InstrumentKindCounter,
		sdkmetric.InstrumentKindObservableCounter,
		sdkmetric.InstrumentKindHistogram:
		return metricdata.DeltaTemporality
	default:
		return metricdata.CumulativeTemporality
	}
}

func main() {
	ctx := context.Background()
	otelShutdownHook := initProvider(ctx)
	defer otelShutdownHook()

	meter = otel.Meter("demo-server")
	requestCount, _ = meter.Int64Counter(
		"demo_server/request_counts",
		metric.WithDescription("The number of requests received"),
	)

	http.HandleFunc("/hello", hello)
	log.Fatal(http.ListenAndServe(":2333", nil))
}

func hello(w http.ResponseWriter, r *http.Request) {
	appID := r.URL.Query().Get("id")

	requestCount.Add(r.Context(), 1, metric.WithAttributes(attribute.String("interface", "hello"), attribute.String("app_id", appID)))

	if _, err := io.WriteString(w, appID+"\n"); err != nil {
		log.Printf("Write failed: %v\n", err)
	}
}

Then I executed

for i in {1..10}; do curl 127.0.0.1:2333/hello?id=${i}; done

And I got the following output which IMO is correct:

{
	"Resource": null,
	"ScopeMetrics": []
}
{
	"Resource": null,
	"ScopeMetrics": [
		{
			"Scope": {
				"Name": "demo-server",
				"Version": "",
				"SchemaURL": ""
			},
			"Metrics": [
				{
					"Name": "demo_server/request_counts",
					"Description": "The number of requests received",
					"Unit": "",
					"Data": {
						"DataPoints": [
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "5"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "6"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "8"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "10"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "1"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "2"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "4"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "3"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "7"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							}
						],
						"Temporality": "DeltaTemporality",
						"IsMonotonic": true
					}
				}
			]
		}
	]
}
{
	"Resource": null,
	"ScopeMetrics": []
}

Right now, I have no clue what is wrong, but maybe you will find something before I get time to look at it further.

@MrAlias
Copy link
Contributor

MrAlias commented Sep 28, 2023

{
	"Resource": null,
	"ScopeMetrics": []
}
{
	"Resource": null,
	"ScopeMetrics": [
		{
			"Scope": {
				"Name": "demo-server",
				"Version": "",
				"SchemaURL": ""
			},
			"Metrics": [
				{
					"Name": "demo_server/request_counts",
					"Description": "The number of requests received",
					"Unit": "",
					"Data": {
						"DataPoints": [
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "5"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "6"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "8"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "10"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "1"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "2"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "4"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "3"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "7"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							},
							{
								"Attributes": [
									{
										"Key": "app_id",
										"Value": {
											"Type": "STRING",
											"Value": "9"
										}
									},
									{
										"Key": "interface",
										"Value": {
											"Type": "STRING",
											"Value": "hello"
										}
									}
								],
								"StartTime": "2023-09-28T19:59:11.6206094+02:00",
								"Time": "2023-09-28T19:59:13.619949141+02:00",
								"Value": 1
							}
						],
						"Temporality": "DeltaTemporality",
						"IsMonotonic": true
					}
				}
			]
		}
	]
}
{
	"Resource": null,
	"ScopeMetrics": []
}

This did not reproduce the issue. All expected attributes (1 through 10) are present.

@MrAlias
Copy link
Contributor

MrAlias commented Sep 28, 2023

Is this an issue with the collector?

@pellared
Copy link
Member

pellared commented Sep 28, 2023

@oliverdding I found the root cause.

From https://pkg.go.dev/github.com/gofiber/fiber/v2#Ctx.Params

Returned value is only valid within the handler. Do not store any references. Make copies or use the Immutable setting to use the value outside the Handler.

You should make a copy of appID in the handler as the metrics are exported asynchronously.

appID = strings.Clone(appID)
requestCount.Add(c.UserContext(), 1, metric.WithAttributes(attribute.String("interface", "hello"), attribute.String("app_id", appID)))

Please close the issue if you agree that this a proper fix.

@MrAlias MrAlias added invalid This doesn't seem right response needed Waiting on user input before progress can be made labels Sep 28, 2023
@oliverdding
Copy link
Author

That is the point! It's my misusing of fiber...

Thanks for your patience @pellared , you are so kind!

@pellared
Copy link
Member

pellared commented Sep 29, 2023

That is the point! It's my misusing of fiber...

Thanks for your patience @pellared , you are so kind!

Thank you. I am happy that I was able to help you. I would also not expect such behavior from fiber.

@pellared pellared removed the response needed Waiting on user input before progress can be made label Sep 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

3 participants