From 4b8cd0dd05663d62c3f7f1bef1982707c3ab82e3 Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Mon, 8 Jun 2026 23:47:52 +0530 Subject: [PATCH 1/7] feat: expose OTLP exporter TLS config in telemetry Add tls ca/cert/key knobs to the trace, metric, and log OTLP exporters, mirroring the framework stub, so the example can target a TLS-enabled collector (https:// endpoint with insecure=false). --- config/telemetry.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/config/telemetry.go b/config/telemetry.go index 97bdd6c..97c89e9 100755 --- a/config/telemetry.go +++ b/config/telemetry.go @@ -194,6 +194,14 @@ func init() { // Timeout: Max time to wait for the backend to acknowledge. // Format: Duration string (e.g., "10s", "500ms"). "timeout": config.Env("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT", "10s"), + + // TLS certificate file paths. Set "ca" and use an https:// endpoint + // with insecure=false to reach a TLS-enabled collector. + "tls": map[string]any{ + "ca": config.Env("OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE", ""), + "cert": config.Env("OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE", ""), + "key": config.Env("OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY", ""), + }, }, // OTLP Metric Exporter @@ -215,6 +223,14 @@ func init() { // - "cumulative": Standard for Prometheus (counts never reset). // - "delta": Standard for Datadog/StatsD (counts per interval). "metric_temporality": config.Env("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY", "cumulative"), + + // TLS certificate file paths. Set "ca" and use an https:// endpoint + // with insecure=false to reach a TLS-enabled collector. + "tls": map[string]any{ + "ca": config.Env("OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE", ""), + "cert": config.Env("OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE", ""), + "key": config.Env("OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY", ""), + }, }, // OTLP Log Exporter @@ -231,6 +247,14 @@ func init() { // Timeout: Max time to wait for the backend to acknowledge. // Format: Duration string (e.g., "10s", "500ms"). "timeout": config.Env("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT", "10s"), + + // TLS certificate file paths. Set "ca" and use an https:// endpoint + // with insecure=false to reach a TLS-enabled collector. + "tls": map[string]any{ + "ca": config.Env("OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE", ""), + "cert": config.Env("OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE", ""), + "key": config.Env("OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY", ""), + }, }, // Console Exporter (Debugging) From c684e33449dfc851fe5b1d9ad7e84dbe8f14b7f6 Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Wed, 10 Jun 2026 01:57:07 +0530 Subject: [PATCH 2/7] test: cover OTLP exporter TLS end to end and sync telemetry config with framework master --- .gitignore | 1 + config/telemetry.go | 100 ++++++++++++--- docker-compose.yml | 10 ++ go.mod | 72 +++++------ go.sum | 158 +++++++++++------------ otel-config-tls.yaml | 42 ++++++ tests/feature/telemetry_tls_test.go | 190 ++++++++++++++++++++++++++++ 7 files changed, 441 insertions(+), 132 deletions(-) create mode 100644 otel-config-tls.yaml create mode 100644 tests/feature/telemetry_tls_test.go diff --git a/.gitignore b/.gitignore index 53fa944..63758fe 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ !.env.example storage tmp +tests/testdata/otel-tls/ diff --git a/config/telemetry.go b/config/telemetry.go index 97c89e9..c42c21c 100755 --- a/config/telemetry.go +++ b/config/telemetry.go @@ -40,6 +40,12 @@ func init() { // - "b3multi": B3 Multi Header "propagators": config.Env("OTEL_PROPAGATORS", "tracecontext"), + // Shutdown Timeout + // + // Max time to wait for flushing buffered telemetry when the application stops. + // Format: Duration string (e.g., "15s", "30s"). + "shutdown_timeout": config.Env("OTEL_SHUTDOWN_TIMEOUT", "15s"), + // Traces Configuration // // Configures the distributed tracing signal. Traces record the path of @@ -69,6 +75,23 @@ func init() { // e.g., 0.1 records ~10% of traces. "ratio": config.Env("OTEL_TRACES_SAMPLER_RATIO", 1.0), }, + + // Processor Configuration + // + // Controls how finished spans are handed to the exporter. + "processor": map[string]any{ + // Type: "batch" buffers spans for performance (recommended); + // "simple" exports each span synchronously (debugging). + "type": config.Env("OTEL_TRACE_PROCESSOR_TYPE", "batch"), + + // Interval: How often buffered spans are pushed. + // Format: Duration string (e.g., "5s", "1m", "500ms"). + "interval": config.Env("OTEL_TRACE_EXPORT_INTERVAL", "5s"), + + // Timeout: Max time allowed for export before cancelling. + // Format: Duration string (e.g., "30s", "10s"). + "timeout": config.Env("OTEL_TRACE_EXPORT_TIMEOUT", "30s"), + }, }, // Metrics Configuration @@ -110,14 +133,18 @@ func init() { // Processor Configuration // - // Configures the BatchLogProcessor, which batches logs before export. + // Controls how log records are handed to the exporter. "processor": map[string]any{ - // Interval: How often logs are flushed. - // Format: Duration string (e.g., "1s", "500ms"). + // Type: "batch" buffers records for performance (recommended); + // "simple" exports each record synchronously (debugging). + "type": config.Env("OTEL_LOG_PROCESSOR_TYPE", "batch"), + + // Interval: How often buffered records are pushed. + // Format: Duration string (e.g., "1s", "1m", "500ms"). "interval": config.Env("OTEL_LOG_EXPORT_INTERVAL", "10s"), // Timeout: Max time allowed for export before cancelling. - // Format: Duration string (e.g., "30s"). + // Format: Duration string (e.g., "30s", "10s"). "timeout": config.Env("OTEL_LOG_EXPORT_TIMEOUT", "10s"), }, }, @@ -183,9 +210,9 @@ func init() { // Reference: https://opentelemetry.io/docs/specs/otel/protocol/ "otlptrace": map[string]any{ "driver": "otlp", - "endpoint": config.Env("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "http://localhost:4318"), + "endpoint": config.Env("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", "localhost:4318"), - // Protocol: "http/protobuf", "http/json" or "grpc". + // Protocol: "http/protobuf" or "grpc". "protocol": config.Env("OTEL_EXPORTER_OTLP_TRACES_PROTOCOL", "http/protobuf"), // Set to false to require TLS/SSL. @@ -195,21 +222,33 @@ func init() { // Format: Duration string (e.g., "10s", "500ms"). "timeout": config.Env("OTEL_EXPORTER_OTLP_TRACES_TIMEOUT", "10s"), - // TLS certificate file paths. Set "ca" and use an https:// endpoint - // with insecure=false to reach a TLS-enabled collector. + // Compression: "gzip" or "" (none). + "compression": config.Env("OTEL_EXPORTER_OTLP_TRACES_COMPRESSION", ""), + + // TLS certificate file paths. Leave empty to use system roots. + // Conflicts with "insecure". "tls": map[string]any{ "ca": config.Env("OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE", ""), "cert": config.Env("OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE", ""), "key": config.Env("OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY", ""), }, + + // Retry with exponential backoff on export failure. + // Zero durations use the SDK defaults shown below. + "retry": map[string]any{ + "enabled": true, + "initial_interval": "5s", + "max_interval": "30s", + "max_elapsed_time": "1m", + }, }, // OTLP Metric Exporter "otlpmetric": map[string]any{ "driver": "otlp", - "endpoint": config.Env("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "http://localhost:4318"), + "endpoint": config.Env("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", "localhost:4318"), - // Protocol: "http/protobuf", "http/json" or "grpc". + // Protocol: "http/protobuf" or "grpc". "protocol": config.Env("OTEL_EXPORTER_OTLP_METRICS_PROTOCOL", "http/protobuf"), // Set to false to require TLS/SSL. @@ -219,26 +258,39 @@ func init() { // Format: Duration string (e.g., "10s", "500ms"). "timeout": config.Env("OTEL_EXPORTER_OTLP_METRICS_TIMEOUT", "10s"), - // Metric Temporality: "cumulative" or "delta". + // Metric Temporality: "cumulative", "delta" or "lowmemory". // - "cumulative": Standard for Prometheus (counts never reset). - // - "delta": Standard for Datadog/StatsD (counts per interval). + // - "delta": Standard for Datadog/StatsD. UpDownCounters stay cumulative per the OTLP spec. + // - "lowmemory": Delta for synchronous Counter/Histogram only. "metric_temporality": config.Env("OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY", "cumulative"), - // TLS certificate file paths. Set "ca" and use an https:// endpoint - // with insecure=false to reach a TLS-enabled collector. + // Compression: "gzip" or "" (none). + "compression": config.Env("OTEL_EXPORTER_OTLP_METRICS_COMPRESSION", ""), + + // TLS certificate file paths. Leave empty to use system roots. + // Conflicts with "insecure". "tls": map[string]any{ "ca": config.Env("OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE", ""), "cert": config.Env("OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE", ""), "key": config.Env("OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY", ""), }, + + // Retry with exponential backoff on export failure. + // Zero durations use the SDK defaults shown below. + "retry": map[string]any{ + "enabled": true, + "initial_interval": "5s", + "max_interval": "30s", + "max_elapsed_time": "1m", + }, }, // OTLP Log Exporter "otlplog": map[string]any{ "driver": "otlp", - "endpoint": config.Env("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", "http://localhost:4318"), + "endpoint": config.Env("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", "localhost:4318"), - // Protocol: "http/protobuf", "http/json" or "grpc". + // Protocol: "http/protobuf" or "grpc". "protocol": config.Env("OTEL_EXPORTER_OTLP_LOGS_PROTOCOL", "http/protobuf"), // Set to false to require TLS/SSL. @@ -248,13 +300,25 @@ func init() { // Format: Duration string (e.g., "10s", "500ms"). "timeout": config.Env("OTEL_EXPORTER_OTLP_LOGS_TIMEOUT", "10s"), - // TLS certificate file paths. Set "ca" and use an https:// endpoint - // with insecure=false to reach a TLS-enabled collector. + // Compression: "gzip" or "" (none). + "compression": config.Env("OTEL_EXPORTER_OTLP_LOGS_COMPRESSION", ""), + + // TLS certificate file paths. Leave empty to use system roots. + // Conflicts with "insecure". "tls": map[string]any{ "ca": config.Env("OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE", ""), "cert": config.Env("OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE", ""), "key": config.Env("OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY", ""), }, + + // Retry with exponential backoff on export failure. + // Zero durations use the SDK defaults shown below. + "retry": map[string]any{ + "enabled": true, + "initial_interval": "5s", + "max_interval": "30s", + "max_elapsed_time": "1m", + }, }, // Console Exporter (Debugging) diff --git a/docker-compose.yml b/docker-compose.yml index a388e24..bf033d8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,16 @@ services: - "4317:4317" - "4318:4318" + otel-collector-tls: + image: otel/opentelemetry-collector-contrib:latest + restart: always + command: ["--config=/etc/otel-config-tls.yaml"] + volumes: + - ./otel-config-tls.yaml:/etc/otel-config-tls.yaml + - ./tests/testdata/otel-tls:/etc/otel-tls:ro + ports: + - "4319:4319" + jaeger: image: jaegertracing/all-in-one:latest restart: always diff --git a/go.mod b/go.mod index ba126d0..a44f094 100644 --- a/go.mod +++ b/go.mod @@ -29,9 +29,9 @@ require ( github.com/swaggo/swag v1.16.2 github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/vektah/gqlparser/v2 v2.5.19 - go.opentelemetry.io/otel v1.43.0 - go.opentelemetry.io/otel/metric v1.43.0 - google.golang.org/grpc v1.80.0 + go.opentelemetry.io/otel v1.44.0 + go.opentelemetry.io/otel/metric v1.44.0 + google.golang.org/grpc v1.81.1 ) require ( @@ -82,7 +82,7 @@ require ( github.com/clipperhouse/uax29/v2 v2.7.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/containerd/console v1.0.5 // indirect - github.com/dave/dst v0.27.3 // indirect + github.com/dave/dst v0.27.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dromara/carbon/v2 v2.6.11 // indirect @@ -118,7 +118,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gookit/color v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -141,7 +141,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.21 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.22 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/microsoft/go-mssqldb v1.9.1 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -187,47 +187,47 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible // indirect github.com/ugorji/go/codec v1.3.1 // indirect github.com/unrolled/secure v1.17.0 // indirect - github.com/urfave/cli/v3 v3.8.0 // indirect + github.com/urfave/cli/v3 v3.9.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.71.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.42.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect - go.opentelemetry.io/otel/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 // indirect + go.opentelemetry.io/otel/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.22.0 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect - golang.org/x/mod v0.34.0 // indirect - golang.org/x/net v0.53.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect + golang.org/x/mod v0.36.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/term v0.42.0 // indirect - golang.org/x/text v0.36.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/term v0.43.0 // indirect + golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.43.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + golang.org/x/tools v0.45.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.6.0 // indirect @@ -237,4 +237,4 @@ require ( gorm.io/plugin/dbresolver v1.6.2 // indirect ) -replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260408110243-4838a69e4bdd +replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2 diff --git a/go.sum b/go.sum index 1d37e7b..1afcb6e 100644 --- a/go.sum +++ b/go.sum @@ -101,8 +101,8 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= -github.com/brianvoe/gofakeit/v7 v7.14.1 h1:a7fe3fonbj0cW3wgl5VwIKfZtiH9C3cLnwcIXWT7sow= -github.com/brianvoe/gofakeit/v7 v7.14.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= +github.com/brianvoe/gofakeit/v7 v7.15.0 h1:kGLYAWN8tnmxq2PelKVK6zwpM7kMxdz9SGPH31mFkNs= +github.com/brianvoe/gofakeit/v7 v7.15.0/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= @@ -165,8 +165,8 @@ github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= -github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= -github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= +github.com/dave/dst v0.27.4 h1:d+EVnOZmphH+lUEXq9rit4GjsFSKJ3AhfRWf7eobTps= +github.com/dave/dst v0.27.4/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg= github.com/dave/jennifer v1.5.0/go.mod h1:4MnyiFIlZS3l5tSDn8VnzE6ffAhYBMB2SZntBsZGUok= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -285,8 +285,8 @@ github.com/goravel/example-proto v0.0.1 h1:ZxETeKREQWjuJ49bX/Hqj1NLR5Vyj489Ks6dR github.com/goravel/example-proto v0.0.1/go.mod h1:I8IPsHr4Ndf7KxmdsRpBR2LQ0Geo48+pjv9IIWf3mZg= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c h1:OV2obbs8BQRHU8n0oT2M//eruXRzljIaVlH1SBpjtv0= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c/go.mod h1:JkUiNH4SDXncjfMn70YqfVxJGpR7WJu64T4jyTYe8RI= -github.com/goravel/framework v1.17.2-0.20260408110243-4838a69e4bdd h1:/SosPdp3n/B59wiRzRUjAkscQtoXg4emy9O3hspdJ1E= -github.com/goravel/framework v1.17.2-0.20260408110243-4838a69e4bdd/go.mod h1:j75T9n1majM+KYFJPP8xEeZZBrQn4aFVJQc3sl5ZBfQ= +github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2 h1:GUUg787KtbCpKFoPx33o+Ogv9B3A3tPTh7SWOIAC6LQ= +github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3 h1:Zy4XPyzWYUKOXQFlUnLtNXY39reh+LCucTAc40TykaQ= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3/go.mod h1:92/izGQ6edpy5p314VjaiDy4NrD37k6bvegGCnTzlEo= github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac h1:umDvd9hciZVHvlmRVAioXpT+kUojwc2MO30wcnshHh0= @@ -309,8 +309,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= @@ -381,8 +381,8 @@ github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJ github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.22 h1:76lXsPn6FyHtTY+jt2fTTvsMUCZq1k0qwRsAMuxzKAk= -github.com/mattn/go-runewidth v0.0.22/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo= @@ -536,8 +536,8 @@ github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU= github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= -github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI= -github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= +github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c= +github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.71.0 h1:tepR7H+Guh9VUqxxcPggYi8R3lGUu2Rsdh+z7/FCY3k= @@ -560,50 +560,52 @@ go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= -go.opentelemetry.io/contrib/propagators/b3 v1.42.0 h1:B2Pew5ufEtgkjLF+tSkXjgYZXQr9m7aCm1wLKB0URbU= -go.opentelemetry.io/contrib/propagators/b3 v1.42.0/go.mod h1:iPgUcSEF5DORW6+yNbdw/YevUy+QqJ508ncjhrRSCjc= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= -go.opentelemetry.io/otel/log/logtest v0.19.0 h1:HdSsl4ndTK15LtJGLWBfMsSlLrCgSeE3VMzwOrLYiYs= -go.opentelemetry.io/otel/log/logtest v0.19.0/go.mod h1:c1sH1nOHTwfMCWhhQTdWGqxgDjZhtkbkzAqGGyj0Ijs= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI= +go.opentelemetry.io/contrib/propagators/b3 v1.44.0 h1:1IFH4oFKK8KupzIelCl3u+bkxpGRps1oWRjQI2+TTWs= +go.opentelemetry.io/contrib/propagators/b3 v1.44.0/go.mod h1:JqWFXsc7VDaqIyubFhEd2cPHqsrzqP0Lvn783SUwyro= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 h1:rydZ9sxbcFdm/oWrVyfLTjHIygMgv0bEeMd+3B/BvoM= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0/go.mod h1:earQ25dooT0Hhspq59DZ8YCC50jWfOlFEeWoxy/P444= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 h1:SUplec5dp06reu1zaXmOXdvqH398taqrDXqUl99jxSc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0/go.mod h1:ho2g4N+ane+swq5I/VBkKWnRDY4kUINH3FuqyZqX/Ug= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 h1:RuynHbfU8JUEw7DyONgkVYg2SVtsoF28y0LGIr69jgA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0/go.mod h1:qZF+/lBs71APw8mlnEZcqZHMzqrYrsFiJOv83lX1OGo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 h1:aZfdmtI6QU/DAPD4b7YZ5zuJgewxO1EW9miOZklqleU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0/go.mod h1:isNl10/Om5CBWu9jj8WOb2+tJLbCVXDgqwzCaJMnJ6w= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 h1:bl2S7Ubua0Nms+D/gAmznQTd4dxxMA93aKbcpKqiTCs= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0/go.mod h1:L0hRV50XdVIODHUfWEqGRCXQvj2rV82STVo12FMFBU0= +go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs= +go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E= +go.opentelemetry.io/otel/log/logtest v0.20.0 h1:+tsZVE15N+RWyN9lUzsRyw7hMZXNMepGu105Eim82/k= +go.opentelemetry.io/otel/log/logtest v0.20.0/go.mod h1:zS9Ryx9RrEAG2tgapMBSvacwhVSSOGSaSiWWgW3NPlQ= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA= +go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY= +go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg= +go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -629,15 +631,15 @@ golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOM golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= -golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= +golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q= +golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -649,8 +651,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -669,8 +671,8 @@ golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -706,8 +708,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -725,8 +727,8 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -740,8 +742,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= -golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -753,8 +755,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -764,12 +766,12 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= -google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/otel-config-tls.yaml b/otel-config-tls.yaml new file mode 100644 index 0000000..979f5e6 --- /dev/null +++ b/otel-config-tls.yaml @@ -0,0 +1,42 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4319 + tls: + cert_file: /etc/otel-tls/server.crt + key_file: /etc/otel-tls/server.key + +processors: + batch: + +exporters: + otlp/jaeger: + endpoint: "jaeger:4317" + tls: + insecure: true + + prometheusremotewrite: + endpoint: "http://prometheus:9090/api/v1/write" + resource_to_telemetry_conversion: + enabled: true + + otlphttp/loki: + endpoint: "http://loki:3100/otlp" + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [prometheusremotewrite] + logs: + receivers: [otlp] + processors: [batch] + exporters: [otlphttp/loki] diff --git a/tests/feature/telemetry_tls_test.go b/tests/feature/telemetry_tls_test.go new file mode 100644 index 0000000..28b5321 --- /dev/null +++ b/tests/feature/telemetry_tls_test.go @@ -0,0 +1,190 @@ +package feature + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "net" + "net/url" + "os" + "path/filepath" + "testing" + "time" + + "github.com/goravel/framework/facades" + "github.com/stretchr/testify/suite" + + "goravel/tests" +) + +// tlsServiceName isolates this suite's telemetry from TelemetryTestSuite, +// which exports through the plaintext collector under the app name. +const tlsServiceName = "goravel-tls" + +type TelemetryTLSTestSuite struct { + suite.Suite + tests.TestCase + originalConfig map[string]any +} + +func TestTelemetryTLSTestSuite(t *testing.T) { + suite.Run(t, &TelemetryTLSTestSuite{}) +} + +func (s *TelemetryTLSTestSuite) SetupSuite() { + certsDir, err := filepath.Abs("../../tests/testdata/otel-tls") + s.Require().NoError(err) + + caPath, err := generateCollectorCerts(certsDir) + s.Require().NoError(err) + + s.False(facades.Process().Path("../../").Run("docker compose up -d prometheus jaeger loki otel-collector-tls").Failed()) + time.Sleep(5 * time.Second) + + s.originalConfig = map[string]any{ + "telemetry.service.name": facades.Config().GetString("telemetry.service.name"), + } + // Signal-specific OTLP endpoints carry the full URL: the SDK uses them + // as-is, without appending the default /v1/ path. + endpoints := map[string]string{ + "otlptrace": "https://localhost:4319/v1/traces", + "otlpmetric": "https://localhost:4319/v1/metrics", + "otlplog": "https://localhost:4319/v1/logs", + } + for exporter, endpoint := range endpoints { + prefix := "telemetry.exporters." + exporter + s.originalConfig[prefix+".endpoint"] = facades.Config().GetString(prefix + ".endpoint") + s.originalConfig[prefix+".insecure"] = facades.Config().GetBool(prefix + ".insecure") + s.originalConfig[prefix+".tls.ca"] = facades.Config().GetString(prefix + ".tls.ca") + + facades.Config().Add(prefix+".endpoint", endpoint) + facades.Config().Add(prefix+".insecure", false) + facades.Config().Add(prefix+".tls.ca", caPath) + } + facades.Config().Add("telemetry.service.name", tlsServiceName) + + s.Require().NoError(facades.App().Restart()) + + resp, err := s.Http(s.T()).Get("/telemetry") + s.Require().NoError(err) + resp.AssertSuccessful() + + // Wait for telemetry data to be exported + time.Sleep(11 * time.Second) +} + +func (s *TelemetryTLSTestSuite) TearDownSuite() { + for key, value := range s.originalConfig { + facades.Config().Add(key, value) + } + s.NoError(facades.App().Restart()) + + s.False(facades.Process().Path("../../").Run("docker compose down").Failed()) +} + +func (s *TelemetryTLSTestSuite) TestTraces() { + resp, err := facades.Http().Get("http://localhost:16686/api/traces?service=" + tlsServiceName) + s.NoError(err) + + body, err := resp.Body() + s.NoError(err) + + s.Contains(body, "GET /telemetry") + s.Contains(body, "user.UserService/GetUser") +} + +func (s *TelemetryTLSTestSuite) TestMetrics() { + query := url.QueryEscape(`grpc_controller_total{service_name="` + tlsServiceName + `"}`) + resp, err := facades.Http().Get("http://localhost:9090/api/v1/query?query=" + query) + s.NoError(err) + + body, err := resp.Body() + s.NoError(err) + + s.Contains(body, "grpc_controller_total") + s.Contains(body, tlsServiceName) +} + +// Logs are not asserted here: the otel log channel caches its handler +// process-wide, so after TelemetryTestSuite has used it once, records +// emitted after the restart in SetupSuite no longer reach the exporter. +// The TLS transport itself is shared by all three signals and is covered +// by the trace and metric assertions above. + +// generateCollectorCerts writes a throwaway CA and a localhost server +// certificate for the TLS collector into dir and returns the CA path. +// The app trusts the CA via "tls.ca"; the collector serves with the +// server pair mounted by docker compose. +func generateCollectorCerts(dir string) (string, error) { + if err := os.MkdirAll(dir, 0o755); err != nil { + return "", err + } + + caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", err + } + caTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{CommonName: "goravel example test CA"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + IsCA: true, + KeyUsage: x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey) + if err != nil { + return "", err + } + caCert, err := x509.ParseCertificate(caDER) + if err != nil { + return "", err + } + + serverKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return "", err + } + serverTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(2), + Subject: pkix.Name{CommonName: "localhost"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(24 * time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + DNSNames: []string{"localhost"}, + IPAddresses: []net.IP{net.ParseIP("127.0.0.1")}, + } + serverDER, err := x509.CreateCertificate(rand.Reader, serverTemplate, caCert, &serverKey.PublicKey, caKey) + if err != nil { + return "", err + } + serverKeyDER, err := x509.MarshalECPrivateKey(serverKey) + if err != nil { + return "", err + } + + caPath := filepath.Join(dir, "ca.crt") + if err := writePEM(caPath, "CERTIFICATE", caDER); err != nil { + return "", err + } + if err := writePEM(filepath.Join(dir, "server.crt"), "CERTIFICATE", serverDER); err != nil { + return "", err + } + if err := writePEM(filepath.Join(dir, "server.key"), "EC PRIVATE KEY", serverKeyDER); err != nil { + return "", err + } + + return caPath, nil +} + +// writePEM uses 0644 so the non-root collector container can read the +// mounted files; these are throwaway test credentials. +func writePEM(path, blockType string, der []byte) error { + return os.WriteFile(path, pem.EncodeToMemory(&pem.Block{Type: blockType, Bytes: der}), 0o644) +} From 5bb73cff34da06ca49ff0aac86217c50d3858dfd Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Wed, 10 Jun 2026 02:30:00 +0530 Subject: [PATCH 3/7] test: assert logs over TLS and pin framework with the log handler cache fix --- go.mod | 2 +- go.sum | 4 ++-- tests/feature/telemetry_tls_test.go | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index a44f094..485cefb 100644 --- a/go.mod +++ b/go.mod @@ -237,4 +237,4 @@ require ( gorm.io/plugin/dbresolver v1.6.2 // indirect ) -replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2 +replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446 diff --git a/go.sum b/go.sum index 1afcb6e..62e3aae 100644 --- a/go.sum +++ b/go.sum @@ -285,8 +285,8 @@ github.com/goravel/example-proto v0.0.1 h1:ZxETeKREQWjuJ49bX/Hqj1NLR5Vyj489Ks6dR github.com/goravel/example-proto v0.0.1/go.mod h1:I8IPsHr4Ndf7KxmdsRpBR2LQ0Geo48+pjv9IIWf3mZg= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c h1:OV2obbs8BQRHU8n0oT2M//eruXRzljIaVlH1SBpjtv0= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c/go.mod h1:JkUiNH4SDXncjfMn70YqfVxJGpR7WJu64T4jyTYe8RI= -github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2 h1:GUUg787KtbCpKFoPx33o+Ogv9B3A3tPTh7SWOIAC6LQ= -github.com/goravel/framework v1.17.2-0.20260609091043-9e0d1e9279b2/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= +github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446 h1:km5MzhimD2sim6XgkwZuTfwp+CCkrKTZDFX4tk15OPI= +github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3 h1:Zy4XPyzWYUKOXQFlUnLtNXY39reh+LCucTAc40TykaQ= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3/go.mod h1:92/izGQ6edpy5p314VjaiDy4NrD37k6bvegGCnTzlEo= github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac h1:umDvd9hciZVHvlmRVAioXpT+kUojwc2MO30wcnshHh0= diff --git a/tests/feature/telemetry_tls_test.go b/tests/feature/telemetry_tls_test.go index 28b5321..0565f10 100644 --- a/tests/feature/telemetry_tls_test.go +++ b/tests/feature/telemetry_tls_test.go @@ -7,6 +7,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "math/big" "net" "net/url" @@ -109,11 +110,20 @@ func (s *TelemetryTLSTestSuite) TestMetrics() { s.Contains(body, tlsServiceName) } -// Logs are not asserted here: the otel log channel caches its handler -// process-wide, so after TelemetryTestSuite has used it once, records -// emitted after the restart in SetupSuite no longer reach the exporter. -// The TLS transport itself is shared by all three signals and is covered -// by the trace and metric assertions above. +func (s *TelemetryTLSTestSuite) TestLogs() { + end := time.Now().UnixNano() + start := time.Now().Add(-5 * time.Minute).UnixNano() + query := url.QueryEscape(`{service_name="` + tlsServiceName + `"}`) + + resp, err := facades.Http().Get(fmt.Sprintf( + "http://localhost:3100/loki/api/v1/query_range?query=%s&start=%d&end=%d", query, start, end)) + s.NoError(err) + + body, err := resp.Body() + s.NoError(err) + + s.Contains(body, "test telemetry log") +} // generateCollectorCerts writes a throwaway CA and a localhost server // certificate for the TLS collector into dir and returns the CA path. From b6ef06c3f83e46dd13015567a1bb784bd13aa2dc Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Wed, 10 Jun 2026 13:40:39 +0530 Subject: [PATCH 4/7] chore: pin framework to master with the log handler cache fix --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 485cefb..aefe876 100644 --- a/go.mod +++ b/go.mod @@ -237,4 +237,4 @@ require ( gorm.io/plugin/dbresolver v1.6.2 // indirect ) -replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446 +replace github.com/goravel/framework => github.com/goravel/framework v1.17.2-0.20260610071514-89408709dd39 diff --git a/go.sum b/go.sum index 62e3aae..508f83e 100644 --- a/go.sum +++ b/go.sum @@ -285,8 +285,8 @@ github.com/goravel/example-proto v0.0.1 h1:ZxETeKREQWjuJ49bX/Hqj1NLR5Vyj489Ks6dR github.com/goravel/example-proto v0.0.1/go.mod h1:I8IPsHr4Ndf7KxmdsRpBR2LQ0Geo48+pjv9IIWf3mZg= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c h1:OV2obbs8BQRHU8n0oT2M//eruXRzljIaVlH1SBpjtv0= github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c/go.mod h1:JkUiNH4SDXncjfMn70YqfVxJGpR7WJu64T4jyTYe8RI= -github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446 h1:km5MzhimD2sim6XgkwZuTfwp+CCkrKTZDFX4tk15OPI= -github.com/goravel/framework v1.17.2-0.20260609205211-4ef1a48af446/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= +github.com/goravel/framework v1.17.2-0.20260610071514-89408709dd39 h1:LRpcRTvnBhRWtWdB9lQK6hvfcE6u5XNlE+If08WlwI8= +github.com/goravel/framework v1.17.2-0.20260610071514-89408709dd39/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3 h1:Zy4XPyzWYUKOXQFlUnLtNXY39reh+LCucTAc40TykaQ= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3/go.mod h1:92/izGQ6edpy5p314VjaiDy4NrD37k6bvegGCnTzlEo= github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac h1:umDvd9hciZVHvlmRVAioXpT+kUojwc2MO30wcnshHh0= From 8ae0c8fd823c1e2421f8976da6b876a2ebb84401 Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Wed, 10 Jun 2026 14:36:16 +0530 Subject: [PATCH 5/7] test: keep the last session cookie after login rotation --- tests/feature/auth_test.go | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/feature/auth_test.go b/tests/feature/auth_test.go index d47db99..23762a0 100644 --- a/tests/feature/auth_test.go +++ b/tests/feature/auth_test.go @@ -1,6 +1,7 @@ package feature import ( + nethttp "net/http" "strconv" "testing" @@ -245,7 +246,7 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods resp.AssertSuccessful() s.NoError(resp.Bind(&login)) - cookies := resp.Cookies() + cookies := latestCookies(resp.Cookies()) s.Require().NotEmpty(cookies) // Step 5: Verify the authenticated session status and user info. @@ -296,7 +297,7 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods s.Equal(login.User.ID, loginByID.ID) s.Equal(login.User.Name, loginByID.User.Name) - loginByIDCookies := resp.Cookies() + loginByIDCookies := latestCookies(resp.Cookies()) s.Require().NotEmpty(loginByIDCookies) resp, err = s.Http(s.T()).WithCookies(loginByIDCookies).Get("session/status") @@ -316,6 +317,26 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods s.Equal(login.User.Name, info.User.Name) } +// latestCookies keeps the last cookie per name, as a browser does when a +// response sets the same cookie twice (the session ID rotates on login). +func latestCookies(cookies []*nethttp.Cookie) []*nethttp.Cookie { + byName := make(map[string]*nethttp.Cookie, len(cookies)) + var names []string + for _, cookie := range cookies { + if _, ok := byName[cookie.Name]; !ok { + names = append(names, cookie.Name) + } + byName[cookie.Name] = cookie + } + + result := make([]*nethttp.Cookie, 0, len(names)) + for _, name := range names { + result = append(result, byName[name]) + } + + return result +} + func uintFromString(value string) uint { parsed, err := strconv.ParseUint(value, 10, 64) if err != nil { From 8090eef38fe75e0df0b6c0ed6b62b62dfee83d88 Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Thu, 11 Jun 2026 20:39:59 +0530 Subject: [PATCH 6/7] fix: bump goravel/gin and drop duplicate-cookie workaround --- go.mod | 2 +- go.sum | 2 ++ tests/feature/auth_test.go | 25 ++----------------------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index aefe876..01029a4 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/goravel/example-proto v0.0.1 github.com/goravel/fiber v1.17.1-0.20260524040519-047523ffa71c github.com/goravel/framework v1.17.2 - github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3 + github.com/goravel/gin v1.17.1-0.20260611134216-db683f379979 github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac github.com/goravel/mysql v1.17.2-0.20260328140940-c52993f25694 github.com/goravel/oss v1.17.1-0.20260316041853-7cb11e498cb9 diff --git a/go.sum b/go.sum index 508f83e..c68c495 100644 --- a/go.sum +++ b/go.sum @@ -289,6 +289,8 @@ github.com/goravel/framework v1.17.2-0.20260610071514-89408709dd39 h1:LRpcRTvnBh github.com/goravel/framework v1.17.2-0.20260610071514-89408709dd39/go.mod h1:PC9D6+MoC3af0oKo2NBh61pyTFoNqopHEq0OLpMU0Ko= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3 h1:Zy4XPyzWYUKOXQFlUnLtNXY39reh+LCucTAc40TykaQ= github.com/goravel/gin v1.17.1-0.20260524040513-e1372c698db3/go.mod h1:92/izGQ6edpy5p314VjaiDy4NrD37k6bvegGCnTzlEo= +github.com/goravel/gin v1.17.1-0.20260611134216-db683f379979 h1:Qom1i02QYlRUgu95eMxaNY+JRPTJxB+7JTDKp8G6YXM= +github.com/goravel/gin v1.17.1-0.20260611134216-db683f379979/go.mod h1:mQkTyL0F4caujuZriXHqvjiwNiKIfh9YLkvXnwy1U2k= github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac h1:umDvd9hciZVHvlmRVAioXpT+kUojwc2MO30wcnshHh0= github.com/goravel/minio v1.17.1-0.20260406054609-93d9a56045ac/go.mod h1:kcnWmIaUPCNU4cGNY3AvW9kYToTPGdhQk232k1o+lTg= github.com/goravel/mysql v1.17.2-0.20260328140940-c52993f25694 h1:+bAo7xKhITp0hqZWHaNqRt6wN50jxhktBHNhCWjAedI= diff --git a/tests/feature/auth_test.go b/tests/feature/auth_test.go index 23762a0..d47db99 100644 --- a/tests/feature/auth_test.go +++ b/tests/feature/auth_test.go @@ -1,7 +1,6 @@ package feature import ( - nethttp "net/http" "strconv" "testing" @@ -246,7 +245,7 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods resp.AssertSuccessful() s.NoError(resp.Bind(&login)) - cookies := latestCookies(resp.Cookies()) + cookies := resp.Cookies() s.Require().NotEmpty(cookies) // Step 5: Verify the authenticated session status and user info. @@ -297,7 +296,7 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods s.Equal(login.User.ID, loginByID.ID) s.Equal(login.User.Name, loginByID.User.Name) - loginByIDCookies := latestCookies(resp.Cookies()) + loginByIDCookies := resp.Cookies() s.Require().NotEmpty(loginByIDCookies) resp, err = s.Http(s.T()).WithCookies(loginByIDCookies).Get("session/status") @@ -317,26 +316,6 @@ func (s *AuthTestSuite) TestSessionStatusLoginUsingIDLogoutAndUnsupportedMethods s.Equal(login.User.Name, info.User.Name) } -// latestCookies keeps the last cookie per name, as a browser does when a -// response sets the same cookie twice (the session ID rotates on login). -func latestCookies(cookies []*nethttp.Cookie) []*nethttp.Cookie { - byName := make(map[string]*nethttp.Cookie, len(cookies)) - var names []string - for _, cookie := range cookies { - if _, ok := byName[cookie.Name]; !ok { - names = append(names, cookie.Name) - } - byName[cookie.Name] = cookie - } - - result := make([]*nethttp.Cookie, 0, len(names)) - for _, name := range names { - result = append(result, byName[name]) - } - - return result -} - func uintFromString(value string) uint { parsed, err := strconv.ParseUint(value, 10, 64) if err != nil { From 883f1fca4afdb320deee1caed306f218362f478b Mon Sep 17 00:00:00 2001 From: kkumar-gcc Date: Thu, 11 Jun 2026 21:32:50 +0530 Subject: [PATCH 7/7] fix: gate TLS collector behind a compose profile and clean up test certs --- docker-compose.yml | 3 +++ tests/feature/telemetry_tls_test.go | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index bf033d8..932bf3d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,8 +11,11 @@ services: - "4317:4317" - "4318:4318" + # Started by the TLS test suite, which generates the mounted certificates. otel-collector-tls: image: otel/opentelemetry-collector-contrib:latest + profiles: + - tls restart: always command: ["--config=/etc/otel-config-tls.yaml"] volumes: diff --git a/tests/feature/telemetry_tls_test.go b/tests/feature/telemetry_tls_test.go index 0565f10..641e97f 100644 --- a/tests/feature/telemetry_tls_test.go +++ b/tests/feature/telemetry_tls_test.go @@ -30,6 +30,7 @@ type TelemetryTLSTestSuite struct { suite.Suite tests.TestCase originalConfig map[string]any + certsDir string } func TestTelemetryTLSTestSuite(t *testing.T) { @@ -39,6 +40,7 @@ func TestTelemetryTLSTestSuite(t *testing.T) { func (s *TelemetryTLSTestSuite) SetupSuite() { certsDir, err := filepath.Abs("../../tests/testdata/otel-tls") s.Require().NoError(err) + s.certsDir = certsDir caPath, err := generateCollectorCerts(certsDir) s.Require().NoError(err) @@ -84,7 +86,8 @@ func (s *TelemetryTLSTestSuite) TearDownSuite() { } s.NoError(facades.App().Restart()) - s.False(facades.Process().Path("../../").Run("docker compose down").Failed()) + s.False(facades.Process().Path("../../").Run("docker compose --profile tls down").Failed()) + s.NoError(os.RemoveAll(s.certsDir)) } func (s *TelemetryTLSTestSuite) TestTraces() {