Repository: kubecano/cano-collector
Pull request #54: feat: CU-8697ep13a - Add tracing to Collector with Jaeger and OpenTelemetry
Author: login: tnosadsharkdev
Created: 2025-03-09T01:19:40Z | Merged: 2025-03-10T12:23:39Z

This pull request includes several changes to add tracing capabilities and improve logging in the application. The most important changes include updates to the configuration structure, dependency additions, and modifications to the main application and various modules to support tracing.

### Tracing and Configuration Updates:

* [`config/config.go`](diffhunk://#diff-fe44f09c4d5977b5f5eaea29170b6a0748819c9d02271746a20d81a5f3efca17R13-R14): Added `TracingEnabled` and `TracingEndpoint` fields to the `Config` struct and initialized them in the `LoadConfig` function. [[1]](diffhunk://#diff-fe44f09c4d5977b5f5eaea29170b6a0748819c9d02271746a20d81a5f3efca17R13-R14) [[2]](diffhunk://#diff-fe44f09c4d5977b5f5eaea29170b6a0748819c9d02271746a20d81a5f3efca17R27-R28)
* [`helm/cano-collector/templates/deployment.yaml`](diffhunk://#diff-bbb3e0b78cdb28d784b5c2a8e9e73359be26a944070bdb6da879475e399f6731R73-R76): Added environment variables for tracing configuration.
* [`helm/cano-collector/values.yaml`](diffhunk://#diff-ce6f1917fc0ae8e78410e442fe458b577739522abfee1f045a0a61bc575a5f7fR20-R22): Added default values for the tracing configuration.

### Dependency Additions:

* [`go.mod`](diffhunk://#diff-33ef32bf6c23acb95f5902d7097b7a1d5128ca061167ec0716715b0b9eeaa5f6R15-R69): Added OpenTelemetry dependencies for tracing.

### Main Application Changes:

* [`main.go`](diffhunk://#diff-2873f79a86c0d8b3335cd7731b0ecf7dd4301eb19a82ef7a1cba7589b5252261R4): Integrated tracing initialization and shutdown in the main function. [[1]](diffhunk://#diff-2873f79a86c0d8b3335cd7731b0ecf7dd4301eb19a82ef7a1cba7589b5252261R4) [[2]](diffhunk://#diff-2873f79a86c0d8b3335cd7731b0ecf7dd4301eb19a82ef7a1cba7589b5252261R13-R14) [[3]](diffhunk://#diff-2873f79a86c0d8b3335cd7731b0ecf7dd4301eb19a82ef7a1cba7589b5252261R43-R49)

### Logger and Router Updates:

* [`pkg/logger/logger.go`](diffhunk://#diff-c1dc2eb1e149a1dc7ecd5cb474e167fb456eb7ad0ea0256cf62fba693a89af79R4-R8): Added context-based logging to include trace and span IDs in logs. [[1]](diffhunk://#diff-c1dc2eb1e149a1dc7ecd5cb474e167fb456eb7ad0ea0256cf62fba693a89af79R4-R8) [[2]](diffhunk://#diff-c1dc2eb1e149a1dc7ecd5cb474e167fb456eb7ad0ea0256cf62fba693a89af79R62-R76) [[3]](diffhunk://#diff-c1dc2eb1e149a1dc7ecd5cb474e167fb456eb7ad0ea0256cf62fba693a89af79R118-R122)
* [`pkg/router/router.go`](diffhunk://#diff-06404baf0ccd8477ead1249463f8f394030eb63c6c3bcff3fe1fea2368150673R12-R19): Added middleware for tracing and updated request handling to include tracing information. [[1]](diffhunk://#diff-06404baf0ccd8477ead1249463f8f394030eb63c6c3bcff3fe1fea2368150673R12-R19) [[2]](diffhunk://#diff-06404baf0ccd8477ead1249463f8f394030eb63c6c3bcff3fe1fea2368150673R43) [[3]](diffhunk://#diff-06404baf0ccd8477ead1249463f8f394030eb63c6c3bcff3fe1fea2368150673L43-R71) [[4]](diffhunk://#diff-06404baf0ccd8477ead1249463f8f394030eb63c6c3bcff3fe1fea2368150673L57-R92)

### New Tracer Module:

* [`pkg/tracer/tracer.go`](diffhunk://#diff-daa370b7c53b55b200b08ed318bef1a61e5838f5f5c5ded3442c83caf4e96f54R1-R67): Created a new module to initialize and configure tracing with OpenTelemetry.


Diff:
diff --git a/config/config.go b/config/config.go
index 6080337..c5fbe91 100644
--- a/config/config.go
+++ b/config/config.go
@@ -6,24 +6,28 @@ import (
 )
 
 type Config struct {
-	AppName       string
-	AppVersion    string
-	AppEnv        string
-	LogLevel      string
-	SentryDSN     string
-	SentryEnabled bool
+	AppName         string
+	AppVersion      string
+	AppEnv          string
+	LogLevel        string
+	TracingMode     string
+	TracingEndpoint string
+	SentryDSN       string
+	SentryEnabled   bool
 }
 
 var GlobalConfig Config
 
 func LoadConfig() {
 	GlobalConfig = Config{
-		AppName:       getEnvString("APP_NAME", "cano-collector"),
-		AppVersion:    getEnvString("APP_VERSION", "dev"),
-		AppEnv:        getEnvString("APP_ENV", "production"),
-		LogLevel:      getEnvString("LOG_LEVEL", "info"),
-		SentryDSN:     getEnvString("SENTRY_DSN", ""),
-		SentryEnabled: getEnvBool("ENABLE_TELEMETRY", true),
+		AppName:         getEnvString("APP_NAME", "cano-collector"),
+		AppVersion:      getEnvString("APP_VERSION", "dev"),
+		AppEnv:          getEnvString("APP_ENV", "production"),
+		LogLevel:        getEnvEnum("LOG_LEVEL", []string{"debug", "info", "warn", "error"}, "info"),
+		TracingMode:     getEnvEnum("TRACING_MODE", []string{"disabled", "local", "remote"}, "disabled"),
+		TracingEndpoint: getEnvString("TRACING_ENDPOINT", "http://localhost:4317"),
+		SentryDSN:       getEnvString("SENTRY_DSN", ""),
+		SentryEnabled:   getEnvBool("ENABLE_TELEMETRY", true),
 	}
 }
 
@@ -46,3 +50,13 @@ func getEnvBool(key string, defaultValue bool) bool {
 	}
 	return parsedValue
 }
+
+func getEnvEnum(key string, allowedValues []string, defaultValue string) string {
+	value := getEnvString(key, defaultValue)
+	for _, allowed := range allowedValues {
+		if value == allowed {
+			return value
+		}
+	}
+	return defaultValue
+}
diff --git a/config/config_test.go b/config/config_test.go
index fe8449f..ca981f6 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -3,6 +3,8 @@ package config
 import (
 	"os"
 	"testing"
+
+	"github.com/stretchr/testify/assert"
 )
 
 func TestLoadConfig(t *testing.T) {
@@ -26,3 +28,53 @@ func TestLoadConfig(t *testing.T) {
 		t.Errorf("Expected SENTRY_ENABLED to be true, got false")
 	}
 }
+
+func TestGetEnvString(t *testing.T) {
+	_ = os.Setenv("TEST_STRING", "value1")
+	defer func() {
+		_ = os.Unsetenv("TEST_STRING")
+	}()
+
+	assert.Equal(t, "value1", getEnvString("TEST_STRING", "default"))
+	assert.Equal(t, "default", getEnvString("NON_EXISTENT_STRING", "default"))
+}
+
+func TestGetEnvBool(t *testing.T) {
+	_ = os.Setenv("TEST_BOOL_TRUE", "true")
+	_ = os.Setenv("TEST_BOOL_FALSE", "false")
+	_ = os.Setenv("TEST_BOOL_INVALID", "invalid")
+	defer func() {
+		_ = os.Unsetenv("TEST_BOOL_TRUE")
+	}()
+	defer func() {
+		_ = os.Unsetenv("TEST_BOOL_FALSE")
+	}()
+	defer func() {
+		_ = os.Unsetenv("TEST_BOOL_INVALID")
+	}()
+
+	assert.True(t, getEnvBool("TEST_BOOL_TRUE", false))
+	assert.False(t, getEnvBool("TEST_BOOL_FALSE", true))
+
+	assert.True(t, getEnvBool("NON_EXISTENT_BOOL", true))
+	assert.False(t, getEnvBool("NON_EXISTENT_BOOL", false))
+
+	assert.False(t, getEnvBool("TEST_BOOL_INVALID", false))
+}
+
+func TestGetEnvEnum(t *testing.T) {
+	allowedValues := []string{"disabled", "local", "remote"}
+
+	_ = os.Setenv("TEST_ENUM_VALID", "local")
+	_ = os.Setenv("TEST_ENUM_INVALID", "invalid")
+	defer func() {
+		_ = os.Unsetenv("TEST_ENUM_VALID")
+	}()
+	defer func() {
+		_ = os.Unsetenv("TEST_ENUM_INVALID")
+	}()
+
+	assert.Equal(t, "local", getEnvEnum("TEST_ENUM_VALID", allowedValues, "disabled"))
+	assert.Equal(t, "disabled", getEnvEnum("TEST_ENUM_INVALID", allowedValues, "disabled"))
+	assert.Equal(t, "disabled", getEnvEnum("NON_EXISTENT_ENUM", allowedValues, "disabled"))
+}
diff --git a/go.mod b/go.mod
index d89e1b0..983df26 100644
--- a/go.mod
+++ b/go.mod
@@ -12,46 +12,60 @@ require (
 	github.com/hellofresh/health-go/v5 v5.5.3
 	github.com/prometheus/client_golang v1.21.1
 	github.com/stretchr/testify v1.10.0
+	go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0
+	go.opentelemetry.io/otel v1.35.0
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
+	go.opentelemetry.io/otel/sdk v1.35.0
+	go.opentelemetry.io/otel/trace v1.35.0
 	go.uber.org/zap v1.27.0
 )
 
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/bytedance/sonic v1.12.1 // indirect
-	github.com/bytedance/sonic/loader v0.2.0 // indirect
+	github.com/bytedance/sonic v1.12.10 // indirect
+	github.com/bytedance/sonic/loader v0.2.3 // indirect
+	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
-	github.com/cloudwego/base64x v0.1.4 // indirect
-	github.com/cloudwego/iasm v0.2.0 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
-	github.com/gabriel-vasile/mimetype v1.4.5 // indirect
-	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+	github.com/gin-contrib/sse v1.0.0 // indirect
+	github.com/go-logr/logr v1.4.2 // indirect
+	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.22.0 // indirect
-	github.com/goccy/go-json v0.10.3 // indirect
+	github.com/go-playground/validator/v10 v10.25.0 // indirect
+	github.com/goccy/go-json v0.10.5 // indirect
+	github.com/google/uuid v1.6.0 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/klauspost/compress v1.17.11 // indirect
-	github.com/klauspost/cpuid/v2 v2.2.8 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/common v0.62.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
-	go.opentelemetry.io/otel v1.24.0 // indirect
-	go.opentelemetry.io/otel/trace v1.24.0 // indirect
+	go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
+	go.opentelemetry.io/otel/metric v1.35.0 // indirect
+	go.opentelemetry.io/proto/otlp v1.5.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
-	golang.org/x/arch v0.9.0 // indirect
-	golang.org/x/crypto v0.31.0 // indirect
-	golang.org/x/net v0.33.0 // indirect
-	golang.org/x/sys v0.28.0 // indirect
-	golang.org/x/text v0.21.0 // indirect
-	google.golang.org/protobuf v1.36.1 // indirect
+	golang.org/x/arch v0.14.0 // indirect
+	golang.org/x/crypto v0.33.0 // indirect
+	golang.org/x/net v0.35.0 // indirect
+	golang.org/x/sys v0.30.0 // indirect
+	golang.org/x/text v0.22.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
+	google.golang.org/grpc v1.71.0 // indirect
+	google.golang.org/protobuf v1.36.5 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index 1066dbe..8244e7c 100644
--- a/go.sum
+++ b/go.sum
@@ -1,47 +1,59 @@
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
-github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
+github.com/bytedance/sonic v1.12.10 h1:uVCQr6oS5669E9ZVW0HyksTLfNS7Q/9hV6IVS4nEMsI=
+github.com/bytedance/sonic v1.12.10/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
-github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
-github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
+github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
-github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
-github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
 github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
-github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
 github.com/getsentry/sentry-go v0.31.1 h1:ELVc0h7gwyhnXHDouXkhqTFSO5oslsRDk0++eyE0KJ4=
 github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
 github.com/getsentry/sentry-go/gin v0.31.1 h1:lvOOO5j0o0IhYIXoHCmQ+D4ExhXWRCnDusV176dXWDA=
 github.com/getsentry/sentry-go/gin v0.31.1/go.mod h1:iMF6gA5uO2t3KVMj4QpjLi9B0U+oMidAiHAdPcJMMdQ=
-github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
-github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
+github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
 github.com/gin-contrib/zap v1.1.4 h1:xvxTybg6XBdNtcQLH3Tf0lFr4vhDkwzgLLrIGlNTqIo=
 github.com/gin-contrib/zap v1.1.4/go.mod h1:7lgEpe91kLbeJkwBTPgtVBy4zMa6oSBEcvj662diqKQ=
 github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
 github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
 github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
 github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
-github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
-github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
-github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8=
+github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
 github.com/hellofresh/health-go/v5 v5.5.3 h1:i+mfJcA8te/QhBzrBZxOw344XgIvHrc9IQzrEyn3OUQ=
 github.com/hellofresh/health-go/v5 v5.5.3/go.mod h1:maWprKoK7N9zno7l2ubFEGVF2SDmTHq5D9sV+lCFmGs=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -49,8 +61,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
 github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
 github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
-github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -69,8 +81,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
-github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
 github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
 github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -85,8 +97,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
 github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
 github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
 github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -98,37 +110,57 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
 github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
-go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
-go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
-go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0 h1:jj/B7eX95/mOxim9g9laNZkOHKz/XCHG0G410SntRy4=
+go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin v0.60.0/go.mod h1:ZvRTVaYYGypytG0zRp2A60lpj//cMq3ZnxYdZaljVBM=
+go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
+go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
+go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
+go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
+go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
+go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
+go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
+go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
+go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
+go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
+go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
+go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
 go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
-golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
-golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
-golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
-golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
+golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
+golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
+golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
+golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
-golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
-google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
-google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
+golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
+golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
+golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
+google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
+google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
+google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
+google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
+google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
+google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
diff --git a/helm/cano-collector/templates/deployment.yaml b/helm/cano-collector/templates/deployment.yaml
index f1217cb..627ec23 100644
--- a/helm/cano-collector/templates/deployment.yaml
+++ b/helm/cano-collector/templates/deployment.yaml
@@ -70,6 +70,10 @@ spec:
               value: {{ .Values.appEnv | quote }}
             - name: "LOG_LEVEL"
               value: {{ .Values.logLevel | quote }}
+            - name: "TRACING_MODE"
+              value: {{ .Values.tracing.mode | quote }}
+            - name: "TRACING_ENDPOINT"
+              value: {{ .Values.tracing.endpoint | quote }}
             - name: "ENABLE_TELEMETRY"
               value: {{ .Values.enableTelemetry | quote }}
             - name: "GIN_MODE"
diff --git a/helm/cano-collector/values.yaml b/helm/cano-collector/values.yaml
index c494b86..9aa0065 100644
--- a/helm/cano-collector/values.yaml
+++ b/helm/cano-collector/values.yaml
@@ -17,6 +17,9 @@ sentry_dsn: https://4f1a66f025c60830fec303a094dcdf94@o1120648.ingest.sentry.io/6
 enableTelemetry: true
 appEnv: production
 logLevel: info
+tracing:
+  mode: "disabled" # disabled | local | remote
+#  endpoint: "http://alloy:4317"
 ginMode: release
 
 # This is for the secretes for pulling an image from a private repository more information can be found here: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/
diff --git a/main.go b/main.go
index 1b67aff..17c9c79 100644
--- a/main.go
+++ b/main.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+	"context"
 	"time"
 
 	"github.com/kubecano/cano-collector/pkg/router"
@@ -9,6 +10,8 @@ import (
 
 	"github.com/kubecano/cano-collector/pkg/health"
 
+	"github.com/kubecano/cano-collector/pkg/tracer"
+
 	"github.com/getsentry/sentry-go"
 
 	"github.com/kubecano/cano-collector/config"
@@ -37,6 +40,13 @@ func main() {
 	}
 	logger.Debug("Health checks registered")
 
+	ctx := context.Background()
+	tp, err := tracer.InitTracer(ctx)
+	if err != nil {
+		logger.Fatalf("Failed to initialize tracing: %v", err)
+	}
+	defer func() { _ = tp.Shutdown(ctx) }()
+
 	r := router.SetupRouter(h)
 	logger.Debug("Router setup complete")
 
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index 721e310..24b2c17 100644
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -1,8 +1,11 @@
 package logger
 
 import (
+	"context"
 	"sync"
 
+	"go.opentelemetry.io/otel/trace"
+
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 
@@ -56,6 +59,21 @@ func InitLogger(level string) {
 	})
 }
 
+// WithContext
+func _(ctx context.Context) *zap.Logger {
+	span := trace.SpanFromContext(ctx)
+	spanCtx := span.SpanContext()
+
+	if spanCtx.HasTraceID() {
+		return logger.With(
+			zap.String("trace_id", spanCtx.TraceID().String()),
+			zap.String("span_id", spanCtx.SpanID().String()),
+		)
+	}
+
+	return logger
+}
+
 func SetLogger(customLogger *zap.Logger) {
 	logger = customLogger
 }
@@ -82,6 +100,11 @@ func Info(args ...interface{}) {
 	GetLogger().Sugar().Info(args...)
 }
 
+// Infof logs a formatted message at InfoLevel. The message includes any fields passed at the log site.
+func Infof(template string, args ...interface{}) {
+	GetLogger().Sugar().Infof(template, args...)
+}
+
 // Warn logs a message at WarnLevel. The message includes any fields passed at the log site.
 func Warn(args ...interface{}) {
 	GetLogger().Sugar().Warn(args...)
@@ -97,6 +120,11 @@ func Errorf(template string, args ...interface{}) {
 	GetLogger().Sugar().Errorf(template, args...)
 }
 
+// Error logs a message at ErrorLevel. The message includes any fields passed at the log site.
+func Error(args ...interface{}) {
+	GetLogger().Sugar().Error(args...)
+}
+
 // Fatalf logs a message at FatalLevel and calls os.Exit. The message includes any fields passed at the log site.
 func Fatalf(template string, args ...interface{}) {
 	GetLogger().Sugar().Fatalf(template, args...)
diff --git a/pkg/router/router.go b/pkg/router/router.go
index 93dade7..c3e506c 100644
--- a/pkg/router/router.go
+++ b/pkg/router/router.go
@@ -9,6 +9,14 @@ import (
 	"syscall"
 	"time"
 
+	"github.com/kubecano/cano-collector/pkg/tracer"
+
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+
+	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
+	"go.opentelemetry.io/otel"
+
 	"github.com/kubecano/cano-collector/pkg/logger"
 
 	"github.com/hellofresh/health-go/v5"
@@ -23,7 +31,7 @@ import (
 )
 
 func SetupRouter(health *health.Health) *gin.Engine {
-	r := gin.Default()
+	r := gin.New()
 
 	logger.Debug("Setting up router")
 
@@ -32,6 +40,7 @@ func SetupRouter(health *health.Health) *gin.Engine {
 		WaitForDelivery: false,
 		Timeout:         2 * time.Second,
 	}))
+
 	r.Use(func(ctx *gin.Context) {
 		if hub := sentrygin.GetHubFromContext(ctx); hub != nil {
 			hub.Scope().SetTag("endpoint", ctx.FullPath())
@@ -40,11 +49,29 @@ func SetupRouter(health *health.Health) *gin.Engine {
 		ctx.Next()
 	})
 
-	// Add a ginzap middleware, which:
-	//   - Logs all requests, like a combined access and error log.
-	//   - Logs to stdout.
-	//   - RFC3339 with UTC time format.
-	r.Use(ginzap.Ginzap(logger.GetLogger(), time.RFC3339, true))
+	if config.GlobalConfig.TracingMode != "disabled" {
+		r.Use(otelgin.Middleware(config.GlobalConfig.AppName))
+		r.Use(tracer.TraceLoggerMiddleware())
+	} else {
+		logger.Debug("otelgin middleware is disabled to prevent trace generation.")
+	}
+
+	r.Use(ginzap.GinzapWithConfig(logger.GetLogger(), &ginzap.Config{
+		TimeFormat: time.RFC3339,
+		UTC:        true,
+		Context: func(c *gin.Context) []zapcore.Field {
+			var fields []zapcore.Field
+
+			if traceID, exists := c.Get("trace_id"); exists {
+				fields = append(fields, zap.String("trace_id", traceID.(string)))
+			}
+			if spanID, exists := c.Get("span_id"); exists {
+				fields = append(fields, zap.String("span_id", spanID.(string)))
+			}
+
+			return fields
+		},
+	}))
 
 	// Logs all panic to error log
 	//   - stack means whether output the stack info.
@@ -54,14 +81,18 @@ func SetupRouter(health *health.Health) *gin.Engine {
 	metrics.RegisterMetrics()
 	r.Use(metrics.PrometheusMiddleware())
 
-	r.GET("/", func(ctx *gin.Context) {
-		ctx.String(http.StatusOK, "Hello world!")
+	r.GET("/", func(c *gin.Context) {
+		tr := otel.Tracer(config.GlobalConfig.AppName)
+		_, span := tr.Start(c.Request.Context(), "root-handler")
+		defer span.End()
+
+		c.String(http.StatusOK, "Hello world!")
 	})
 
 	r.GET("/metrics", gin.WrapH(promhttp.Handler()))
 
-	r.GET("/livez", func(c *gin.Context) {
-		c.JSON(http.StatusOK, gin.H{"status": "ok"})
+	r.GET("/livez", func(ctx *gin.Context) {
+		ctx.JSON(http.StatusOK, gin.H{"status": "ok"})
 	})
 	r.GET("/readyz", gin.WrapH(health.Handler()))
 	r.GET("/healthz", gin.WrapH(health.Handler()))
@@ -80,7 +111,7 @@ func StartServer(router *gin.Engine) {
 	go func() {
 		// service connections
 		if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
-			logger.Fatalf("listen: %s\n", err)
+			logger.Fatalf("Failed to start server: %v", err)
 		}
 	}()
 
@@ -97,7 +128,7 @@ func StartServer(router *gin.Engine) {
 	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 	defer cancel()
 	if err := srv.Shutdown(ctx); err != nil {
-		logger.Fatal("Cano-collector server shutdown:", err)
+		logger.Fatalf("Cano-collector server shutdown: %v", err)
 	}
 	// catching ctx.Done(). timeout of 5 seconds.
 	<-ctx.Done()
diff --git a/pkg/tracer/tracer.go b/pkg/tracer/tracer.go
new file mode 100644
index 0000000..84da157
--- /dev/null
+++ b/pkg/tracer/tracer.go
@@ -0,0 +1,101 @@
+package tracer
+
+import (
+	"context"
+
+	"github.com/gin-gonic/gin"
+
+	"github.com/kubecano/cano-collector/config"
+	"github.com/kubecano/cano-collector/pkg/logger"
+
+	"go.opentelemetry.io/otel"
+	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
+	"go.opentelemetry.io/otel/propagation"
+	sdkresource "go.opentelemetry.io/otel/sdk/resource"
+	sdktrace "go.opentelemetry.io/otel/sdk/trace"
+	semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
+	"go.opentelemetry.io/otel/trace"
+)
+
+func InitTracer(ctx context.Context) (*sdktrace.TracerProvider, error) {
+	logger.Debug("Initializing Tracer...")
+
+	otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.NeverSample())))
+
+	var tp *sdktrace.TracerProvider
+
+	switch config.GlobalConfig.TracingMode {
+	case "disabled":
+		logger.Info("Tracing is disabled. No traces will be collected.")
+		tp = sdktrace.NewTracerProvider(
+			sdktrace.WithSampler(sdktrace.NeverSample()),
+		)
+
+	case "local":
+		logger.Info("Tracing is enabled in local mode. Traces will not be exported.")
+		tp = sdktrace.NewTracerProvider(
+			sdktrace.WithSampler(sdktrace.AlwaysSample()),
+			sdktrace.WithResource(sdkresource.NewWithAttributes(
+				semconv.SchemaURL,
+				semconv.ServiceNameKey.String(config.GlobalConfig.AppName),
+			)),
+		)
+
+	case "remote":
+		endpoint := config.GlobalConfig.TracingEndpoint
+		logger.Infof("Tracing is enabled in remote mode. Exporting traces to: %s", endpoint)
+
+		exporter, err := otlptracegrpc.New(
+			ctx,
+			otlptracegrpc.WithInsecure(),
+			otlptracegrpc.WithEndpointURL(endpoint),
+		)
+		if err != nil {
+			logger.Errorf("Failed to initialize OTLP exporter: %v", err)
+			return nil, err
+		}
+
+		tp = sdktrace.NewTracerProvider(
+			sdktrace.WithBatcher(exporter),
+			sdktrace.WithSampler(sdktrace.AlwaysSample()),
+			sdktrace.WithResource(sdkresource.NewWithAttributes(
+				semconv.SchemaURL,
+				semconv.ServiceNameKey.String(config.GlobalConfig.AppName),
+			)),
+		)
+	}
+
+	if tp == nil {
+		logger.Warn("TracerProvider was nil. Creating a new default TracerProvider.")
+		tp = sdktrace.NewTracerProvider(
+			sdktrace.WithSampler(sdktrace.NeverSample()),
+		)
+	}
+
+	otel.SetTracerProvider(tp)
+	otel.SetTextMapPropagator(propagation.TraceContext{})
+
+	logger.Debug("Tracer initialized successfully.")
+	return tp, nil
+}
+
+func TraceLoggerMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		span := trace.SpanFromContext(c.Request.Context())
+		spanCtx := span.SpanContext()
+
+		if spanCtx.HasTraceID() {
+			traceID := spanCtx.TraceID().String()
+			spanID := spanCtx.SpanID().String()
+
+			c.Set("trace_id", traceID)
+			c.Set("span_id", spanID)
+
+			logger.Debug("Request Trace Info:", "trace_id:", traceID, "span_id:", spanID)
+		} else {
+			logger.Debug("No active trace found for request.")
+		}
+
+		c.Next()
+	}
+}
diff --git a/pkg/tracer/tracer_test.go b/pkg/tracer/tracer_test.go
new file mode 100644
index 0000000..1850f66
--- /dev/null
+++ b/pkg/tracer/tracer_test.go
@@ -0,0 +1,106 @@
+package tracer
+
+import (
+	"context"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	sdktrace "go.opentelemetry.io/otel/sdk/trace"
+	"go.uber.org/zap"
+
+	"github.com/kubecano/cano-collector/pkg/logger"
+
+	"github.com/gin-gonic/gin"
+	"github.com/stretchr/testify/require"
+	"go.opentelemetry.io/otel/trace"
+
+	"github.com/kubecano/cano-collector/config"
+
+	"github.com/stretchr/testify/assert"
+	"go.opentelemetry.io/otel"
+)
+
+func TestInitTracer_Disabled(t *testing.T) {
+	l, _ := zap.NewDevelopment()
+	logger.SetLogger(l)
+
+	otel.SetTracerProvider(sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.NeverSample())))
+
+	config.GlobalConfig.TracingMode = "disabled"
+
+	tp, err := InitTracer(context.Background())
+
+	require.NoError(t, err)
+	assert.NotNil(t, tp, "TraceProvider should not be nil")
+
+	assert.Equal(t, tp, otel.GetTracerProvider(), "TracerProvider should be globally set")
+
+	tracer := otel.Tracer("test-tracer")
+	assert.NotNil(t, tracer, "Tracer should not be nil")
+
+	_, span := tracer.Start(context.Background(), "test-span")
+	defer span.End()
+
+	assert.False(t, span.IsRecording(), "Span should not be recording for mode=disabled")
+	assert.Equal(t, trace.TraceFlags(0x0), span.SpanContext().TraceFlags(), "TraceFlags should be 0x0 for mode=disabled")
+}
+
+func TestInitTracer_Local(t *testing.T) {
+	config.GlobalConfig.TracingMode = "local"
+
+	tp, err := InitTracer(context.Background())
+
+	require.NoError(t, err)
+	assert.NotNil(t, tp)
+
+	assert.Equal(t, tp, otel.GetTracerProvider())
+
+	tracer := otel.Tracer("test-tracer")
+	_, span := tracer.Start(context.Background(), "test-span")
+	defer span.End()
+
+	assert.True(t, span.SpanContext().HasTraceID(), "TraceID should be generated for mode=local")
+	assert.True(t, span.SpanContext().HasSpanID(), "SpanID should be generated for mode=local")
+}
+
+func TestInitTracer_Remote(t *testing.T) {
+	config.GlobalConfig.TracingMode = "remote"
+	config.GlobalConfig.TracingEndpoint = "localhost:4317"
+
+	tp, err := InitTracer(context.Background())
+
+	require.NoError(t, err)
+	assert.NotNil(t, tp)
+
+	assert.Equal(t, tp, otel.GetTracerProvider())
+
+	tracer := otel.Tracer("test-tracer")
+	_, span := tracer.Start(context.Background(), "test-span")
+	defer span.End()
+
+	assert.True(t, span.SpanContext().HasTraceID(), "TraceID should be generated for mode=remote")
+	assert.True(t, span.SpanContext().HasSpanID(), "SpanID should be generated for mode=remote")
+}
+
+func TestTraceLoggerMiddleware(t *testing.T) {
+	gin.SetMode(gin.TestMode)
+
+	router := gin.New()
+	router.Use(TraceLoggerMiddleware())
+	router.GET("/test", func(c *gin.Context) {
+		traceID, _ := c.Get("trace_id")
+		spanID, _ := c.Get("span_id")
+		c.JSON(http.StatusOK, gin.H{"trace_id": traceID, "span_id": spanID})
+	})
+
+	req, _ := http.NewRequest(http.MethodGet, "/test", nil)
+	w := httptest.NewRecorder()
+	router.ServeHTTP(w, req)
+
+	assert.Equal(t, http.StatusOK, w.Code)
+
+	body := w.Body.String()
+	assert.Contains(t, body, "trace_id")
+	assert.Contains(t, body, "span_id")
+}
diff --git a/release-please-config.json b/release-please-config.json
index 5a4d017..53c7920 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -18,11 +18,6 @@
           "type": "yaml",
           "path": "helm/cano-collector/Chart.yaml",
           "jsonpath": "$.appVersion"
-        },
-        {
-          "type": "yaml",
-          "path": "helm/cano-collector/values.yaml",
-          "jsonpath": "$.image.tag"
         }
       ]
     },


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## PR Overview

This pull request adds tracing support using Jaeger and OpenTelemetry while improving logging and configuration across the Collector application.
- Introduces a new tracer module for initializing tracing and providing a Gin middleware.
- Updates configuration, logger, and router components to propagate trace and span IDs.
- Enhances Helm templates and configuration defaults to include tracing capabilities.

### Reviewed Changes

| File                                   | Description                                                                   |
| -------------------------------------- | ----------------------------------------------------------------------------- |
| pkg/tracer/tracer.go                   | New module to initialize and configure tracing and add a trace logger middleware |
| pkg/logger/logger.go                   | Added a context-aware logger function to attach trace and span IDs              |
| pkg/router/router.go                   | Updated router middleware to include tracing and context propagation            |
| helm/cano-collector/values.yaml        | Added default tracing configuration                                           |
| config/config.go                       | Introduced tracing fields into the application configuration                  |
| main.go                                | Integrated tracer initialization and graceful shutdown of tracing             |
| helm/cano-collector/templates/deployment.yaml | Added environment variables for tracing configuration                |

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**pkg/logger/logger.go:63**
* [nitpick] The function name '_' is non-descriptive, which could reduce code clarity. Consider renaming it to 'LoggerWithContext' or a similarly descriptive name.
```
func _(ctx context.Context) *zap.Logger {
```
**pkg/router/router.go:91**
* [nitpick] The parameter name in this route handler ('ctx') is inconsistent with other handlers that use 'c'. Consider using a consistent name (e.g., 'c') for clarity.
```
r.GET("/livez", func(ctx *gin.Context) {
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T01:20:08Z
user: login: copilot-pull-request-reviewer[bot]
body: ## PR Overview

This PR adds tracing capabilities to the Collector by introducing a new tracer module, updating configuration to support tracing modes and endpoints, and integrating tracing information into logging and routing.  
- Introduces a new tracer module (pkg/tracer/tracer.go) with initialization and middleware functions.  
- Updates configuration (config/config.go, helm values) to include tracing mode and endpoint.  
- Integrates tracing support in the main application, logger, and router with corresponding tests.

### Reviewed Changes

| File                                    | Description                                                      |
| --------------------------------------- | ---------------------------------------------------------------- |
| pkg/tracer/tracer.go                     | Adds tracer initialization and middleware for tracing            |
| pkg/tracer/tracer_test.go                | Provides tests for tracer initialization and middleware            |
| config/config.go                         | Updates config structure to include tracing settings              |
| pkg/logger/logger.go                     | Adds a context-based logger function to include trace IDs          |
| pkg/router/router.go                     | Integrates tracing middleware into router setup                    |
| helm/cano-collector/values.yaml & deployment.yaml | Adds helm values and environment variable configuration for tracing |
| main.go                                  | Integrates tracing initialization into the main function           |

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**pkg/logger/logger.go:63**
* [nitpick] The function name '_' is non-descriptive and may cause confusion; consider renaming it to 'LoggerWithContext' to clearly indicate its purpose.
```
func _(ctx context.Context) *zap.Logger {
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T09:04:26Z
user: login: copilot-pull-request-reviewer[bot]
body: ## PR Overview

This PR adds tracing capabilities to the collector application using Jaeger and OpenTelemetry, along with associated logging and configuration updates.  
- Introduces a new tracer module including initialization functions and middleware for tracing requests.  
- Updates configuration to support tracing mode and endpoint settings, along with new environment variable mappings in Helm charts.  
- Integrates the tracing initialization and middleware into the main application and router setup.

### Reviewed Changes

| File                                   | Description                                                  |
| -------------------------------------- | ------------------------------------------------------------ |
| pkg/tracer/tracer.go                   | Implements tracer initialization and middleware functionality |
| pkg/tracer/tracer_test.go              | Adds tests covering disabled, local, and remote tracing modes |
| config/config.go                       | Extends configuration structure with tracing fields          |
| config/config_test.go                  | Adds tests for new environment variable loading functions      |
| pkg/router/router.go                   | Updates router to conditionally include tracing middleware       |
| pkg/logger/logger.go                   | Enhances logging with context injection; adds a new context-based function |
| main.go                                | Integrates tracing initialization and graceful shutdown of tracer |
| helm/cano-collector/values.yaml        | Adds default tracing configuration values                       |
| helm/cano-collector/templates/deployment.yaml | Propagates tracing configuration variables to deployment         |

Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**pkg/logger/logger.go:63**
* The function name '_' is ambiguous and non-descriptive. Consider renaming it to 'WithContext' to improve readability.
```
func _(ctx context.Context) *zap.Logger {
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T12:20:50Z

Review comments:
diff_hunk: @@ -70,6 +70,10 @@ spec:
               value: {{ .Values.appEnv | quote }}
             - name: "LOG_LEVEL"
               value: {{ .Values.logLevel | quote }}
+            - name: "TRACING_ENABLED"
path: helm/cano-collector/templates/deployment.yaml
user: login: Copilot
body: The deployment sets the environment variable 'TRACING_ENABLED', but the configuration expects 'TRACING_MODE'. This mismatch might cause tracing not to be configured correctly.
```suggestion
            - name: "TRACING_MODE"
```
created_at: 2025-03-10T09:04:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -17,6 +17,9 @@ sentry_dsn: https://4f1a66f025c60830fec303a094dcdf94@o1120648.ingest.sentry.io/6
 enableTelemetry: true
 appEnv: production
 logLevel: info
+tracing:
+  mode: "off" # off | local | remote
path: helm/cano-collector/values.yaml
user: login: Copilot
body: The allowed values in the configuration for tracing mode are 'disabled', 'local', or 'remote', so using 'off' in the values file may lead to unexpected behavior. Consider changing it to 'disabled' for consistency.
```suggestion
  mode: "disabled" # disabled | local | remote
```
created_at: 2025-03-10T09:04:26Z
author_association: CONTRIBUTOR

PR comments:
user: login: tnosadsharkdev
created_at: 2025-03-09T01:19:44Z
body: Task linked: [CU-8697ep13a Add tracing to Collector with Jaeger and OpenTelemetry](https://app.clickup.com/t/8697ep13a)
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-03-10T11:47:54Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=kubecano_cano-collector&pullRequest=54) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [73.7% Coverage on New Code](https://sonarcloud.io/component_measures?id=kubecano_cano-collector&pullRequest=54&metric=new_coverage&view=list) (required ≥ 80%)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=kubecano_cano-collector&pullRequest=54)


author_association: NONE