Skip to content

Conversation

@krishankumar01
Copy link
Member

πŸ“‘ Description

RelatedTo goravel/goravel#726

βœ… Checks

  • Added test cases for my code

@krishankumar01 krishankumar01 requested a review from a team as a code owner December 29, 2025 20:42
Copilot AI review requested due to automatic review settings December 29, 2025 20:42
@krishankumar01 krishankumar01 marked this pull request as draft December 29, 2025 20:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds HTTP server and client telemetry instrumentation capabilities to the Goravel framework, enabling automatic tracing and metrics collection for HTTP requests and responses. This is part of the broader telemetry instrumentation feature set (issue #726).

Key Changes:

  • Implemented HTTP middleware for server-side request instrumentation with configurable filtering
  • Created HTTP client transport wrapper for outbound request instrumentation
  • Added configuration support for HTTP server instrumentation with exclusion patterns

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
telemetry/setup/stubs.go Adds instrumentation configuration section with http_server settings for enabled state and path/method exclusions
telemetry/instrumentation/http/middleware.go Implements Telemetry middleware that captures traces, metrics, and handles panics for incoming HTTP requests
telemetry/instrumentation/http/middleware_test.go Provides test suite with test helpers for middleware functionality including success cases, exclusions, disabled state, and panic handling
telemetry/instrumentation/http/config.go Defines ServerConfig structure and option functions for customizing HTTP server instrumentation behavior
telemetry/instrumentation/http/transport.go Implements NewTransport function to wrap HTTP clients with telemetry instrumentation
telemetry/instrumentation/http/transport_test.go Tests for transport wrapper covering fallback scenarios and successful wrapping cases
go.mod Adds dependency on otelhttp instrumentation library
go.sum Updates dependency checksums for otelhttp and its transitive dependency httpsnoop

πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

"github.com/goravel/framework/support/color"
"github.com/goravel/framework/telemetry"
)

Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NewTransport function lacks documentation. As a public API function, it should have a comment explaining its purpose, parameters, and return value. This is especially important for exported functions that developers will use to instrument their HTTP clients.

Suggested change
// NewTransport returns an http.RoundTripper instrumented with OpenTelemetry.
// It wraps the provided base RoundTripper with otelhttp using the configured
// telemetry facade's tracer provider, meter provider, and propagator.
//
// If telemetry.TelemetryFacade is nil, a warning is logged and no
// instrumentation is applied. In that case, http.DefaultTransport is returned
// when base is nil; otherwise the provided base RoundTripper is returned.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +150
func (s *MiddlewareTestSuite) TestTelemetry() {
defaultTelemetrySetup := func(mockTelemetry *telemetrymocks.Telemetry) {
mockTelemetry.EXPECT().Tracer(instrumentationName).Return(tracenoop.NewTracerProvider().Tracer("test")).Once()
mockTelemetry.EXPECT().Meter(instrumentationName).Return(metricnoop.NewMeterProvider().Meter("test")).Once()
mockTelemetry.EXPECT().Propagator().Return(propagation.NewCompositeTextMapPropagator()).Once()
}

tests := []struct {
name string
configSetup func(*configmocks.Config)
telemetrySetup func(*telemetrymocks.Telemetry)
handler nethttp.HandlerFunc
requestPath string
expectPanic bool
}{
{
name: "Success: Request is traced and metrics recorded",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
_, _ = w.Write([]byte("OK"))
},
},
{
name: "Ignored: Excluded path is skipped",
requestPath: "/health",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = true
c.ExcludedPaths = []string{"/health"}
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Ignored: Disabled via config",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = false
}).Return(nil).Once()
},
telemetrySetup: func(mockTelemetry *telemetrymocks.Telemetry) {
// If disabled, Tracer/Meter should NOT be initialized
},
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Panic: Metrics recorded as 500 and panic re-thrown",
requestPath: "/crash",
expectPanic: true,
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
panic("server crash")
},
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
mockConfig := configmocks.NewConfig(s.T())
mockTelemetry := telemetrymocks.NewTelemetry(s.T())

telemetry.ConfigFacade = mockConfig
telemetry.TelemetryFacade = mockTelemetry

tt.configSetup(mockConfig)
tt.telemetrySetup(mockTelemetry)

handler := testMiddleware(tt.handler)
server := httptest.NewServer(handler)
defer server.Close()

client := &nethttp.Client{}

action := func() {
_, err := client.Get(server.URL + tt.requestPath)
if !tt.expectPanic {
s.NoError(err)
}
}

action()
})
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SpanNameFormatter option is not covered by tests. While the middleware uses defaultSpanNameFormatter internally, there are no tests validating that custom SpanNameFormatter functions can be provided through the WithSpanNameFormatter option.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +150
func (s *MiddlewareTestSuite) TestTelemetry() {
defaultTelemetrySetup := func(mockTelemetry *telemetrymocks.Telemetry) {
mockTelemetry.EXPECT().Tracer(instrumentationName).Return(tracenoop.NewTracerProvider().Tracer("test")).Once()
mockTelemetry.EXPECT().Meter(instrumentationName).Return(metricnoop.NewMeterProvider().Meter("test")).Once()
mockTelemetry.EXPECT().Propagator().Return(propagation.NewCompositeTextMapPropagator()).Once()
}

tests := []struct {
name string
configSetup func(*configmocks.Config)
telemetrySetup func(*telemetrymocks.Telemetry)
handler nethttp.HandlerFunc
requestPath string
expectPanic bool
}{
{
name: "Success: Request is traced and metrics recorded",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
_, _ = w.Write([]byte("OK"))
},
},
{
name: "Ignored: Excluded path is skipped",
requestPath: "/health",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = true
c.ExcludedPaths = []string{"/health"}
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Ignored: Disabled via config",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = false
}).Return(nil).Once()
},
telemetrySetup: func(mockTelemetry *telemetrymocks.Telemetry) {
// If disabled, Tracer/Meter should NOT be initialized
},
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Panic: Metrics recorded as 500 and panic re-thrown",
requestPath: "/crash",
expectPanic: true,
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
panic("server crash")
},
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
mockConfig := configmocks.NewConfig(s.T())
mockTelemetry := telemetrymocks.NewTelemetry(s.T())

telemetry.ConfigFacade = mockConfig
telemetry.TelemetryFacade = mockTelemetry

tt.configSetup(mockConfig)
tt.telemetrySetup(mockTelemetry)

handler := testMiddleware(tt.handler)
server := httptest.NewServer(handler)
defer server.Close()

client := &nethttp.Client{}

action := func() {
_, err := client.Get(server.URL + tt.requestPath)
if !tt.expectPanic {
s.NoError(err)
}
}

action()
})
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MetricAttributes option is not covered by tests. While the middleware supports custom metric attributes through the WithMetricAttributes option, there are no tests validating this functionality works correctly.

Copilot uses AI. Check for mistakes.
color.Warningln("[Telemetry] Facade not initialized. HTTP middleware disabled.")
return func(ctx http.Context) { ctx.Request().Next() }
}

Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code does not check if ConfigFacade is nil before calling UnmarshalKey, which could cause a panic. Based on the pattern used in the log instrumentation (telemetry/instrumentation/log/channel.go), ConfigFacade should be validated before use. Consider adding a nil check similar to the TelemetryFacade check.

Suggested change
if telemetry.ConfigFacade == nil {
color.Warningln("[Telemetry] Config facade not initialized. HTTP middleware disabled.")
return func(ctx http.Context) { ctx.Request().Next() }
}

Copilot uses AI. Check for mistakes.
Comment on lines +26 to +34
originalTelemetry contractstelemetry.Telemetry
}

func (s *MiddlewareTestSuite) SetupTest() {
s.originalTelemetry = telemetry.TelemetryFacade
}

func (s *MiddlewareTestSuite) TearDownTest() {
telemetry.TelemetryFacade = s.originalTelemetry
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test suite modifies the global ConfigFacade in tests (line 128) but does not restore it in TearDownTest. This could cause test pollution and affect other tests. Consider storing the original ConfigFacade in SetupTest and restoring it in TearDownTest, similar to how TelemetryFacade is handled.

Suggested change
originalTelemetry contractstelemetry.Telemetry
}
func (s *MiddlewareTestSuite) SetupTest() {
s.originalTelemetry = telemetry.TelemetryFacade
}
func (s *MiddlewareTestSuite) TearDownTest() {
telemetry.TelemetryFacade = s.originalTelemetry
originalTelemetry contractstelemetry.Telemetry
originalConfig interface{}
}
func (s *MiddlewareTestSuite) SetupTest() {
s.originalTelemetry = telemetry.TelemetryFacade
s.originalConfig = telemetry.ConfigFacade
}
func (s *MiddlewareTestSuite) TearDownTest() {
telemetry.TelemetryFacade = s.originalTelemetry
telemetry.ConfigFacade = s.originalConfig

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +150
func (s *MiddlewareTestSuite) TestTelemetry() {
defaultTelemetrySetup := func(mockTelemetry *telemetrymocks.Telemetry) {
mockTelemetry.EXPECT().Tracer(instrumentationName).Return(tracenoop.NewTracerProvider().Tracer("test")).Once()
mockTelemetry.EXPECT().Meter(instrumentationName).Return(metricnoop.NewMeterProvider().Meter("test")).Once()
mockTelemetry.EXPECT().Propagator().Return(propagation.NewCompositeTextMapPropagator()).Once()
}

tests := []struct {
name string
configSetup func(*configmocks.Config)
telemetrySetup func(*telemetrymocks.Telemetry)
handler nethttp.HandlerFunc
requestPath string
expectPanic bool
}{
{
name: "Success: Request is traced and metrics recorded",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
_, _ = w.Write([]byte("OK"))
},
},
{
name: "Ignored: Excluded path is skipped",
requestPath: "/health",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = true
c.ExcludedPaths = []string{"/health"}
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Ignored: Disabled via config",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = false
}).Return(nil).Once()
},
telemetrySetup: func(mockTelemetry *telemetrymocks.Telemetry) {
// If disabled, Tracer/Meter should NOT be initialized
},
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Panic: Metrics recorded as 500 and panic re-thrown",
requestPath: "/crash",
expectPanic: true,
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
panic("server crash")
},
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
mockConfig := configmocks.NewConfig(s.T())
mockTelemetry := telemetrymocks.NewTelemetry(s.T())

telemetry.ConfigFacade = mockConfig
telemetry.TelemetryFacade = mockTelemetry

tt.configSetup(mockConfig)
tt.telemetrySetup(mockTelemetry)

handler := testMiddleware(tt.handler)
server := httptest.NewServer(handler)
defer server.Close()

client := &nethttp.Client{}

action := func() {
_, err := client.Get(server.URL + tt.requestPath)
if !tt.expectPanic {
s.NoError(err)
}
}

action()
})
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ExcludedMethods configuration feature is not covered by tests. While ExcludedPaths has test coverage, ExcludedMethods should also be tested to ensure HTTP methods can be properly filtered.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +150
func (s *MiddlewareTestSuite) TestTelemetry() {
defaultTelemetrySetup := func(mockTelemetry *telemetrymocks.Telemetry) {
mockTelemetry.EXPECT().Tracer(instrumentationName).Return(tracenoop.NewTracerProvider().Tracer("test")).Once()
mockTelemetry.EXPECT().Meter(instrumentationName).Return(metricnoop.NewMeterProvider().Meter("test")).Once()
mockTelemetry.EXPECT().Propagator().Return(propagation.NewCompositeTextMapPropagator()).Once()
}

tests := []struct {
name string
configSetup func(*configmocks.Config)
telemetrySetup func(*telemetrymocks.Telemetry)
handler nethttp.HandlerFunc
requestPath string
expectPanic bool
}{
{
name: "Success: Request is traced and metrics recorded",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
_, _ = w.Write([]byte("OK"))
},
},
{
name: "Ignored: Excluded path is skipped",
requestPath: "/health",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = true
c.ExcludedPaths = []string{"/health"}
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Ignored: Disabled via config",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = false
}).Return(nil).Once()
},
telemetrySetup: func(mockTelemetry *telemetrymocks.Telemetry) {
// If disabled, Tracer/Meter should NOT be initialized
},
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Panic: Metrics recorded as 500 and panic re-thrown",
requestPath: "/crash",
expectPanic: true,
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
panic("server crash")
},
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
mockConfig := configmocks.NewConfig(s.T())
mockTelemetry := telemetrymocks.NewTelemetry(s.T())

telemetry.ConfigFacade = mockConfig
telemetry.TelemetryFacade = mockTelemetry

tt.configSetup(mockConfig)
tt.telemetrySetup(mockTelemetry)

handler := testMiddleware(tt.handler)
server := httptest.NewServer(handler)
defer server.Close()

client := &nethttp.Client{}

action := func() {
_, err := client.Get(server.URL + tt.requestPath)
if !tt.expectPanic {
s.NoError(err)
}
}

action()
})
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Filters configuration feature is not covered by tests. The middleware supports custom filters through the ServerConfig.Filters field, but there are no tests validating this functionality works correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +150
func (s *MiddlewareTestSuite) TestTelemetry() {
defaultTelemetrySetup := func(mockTelemetry *telemetrymocks.Telemetry) {
mockTelemetry.EXPECT().Tracer(instrumentationName).Return(tracenoop.NewTracerProvider().Tracer("test")).Once()
mockTelemetry.EXPECT().Meter(instrumentationName).Return(metricnoop.NewMeterProvider().Meter("test")).Once()
mockTelemetry.EXPECT().Propagator().Return(propagation.NewCompositeTextMapPropagator()).Once()
}

tests := []struct {
name string
configSetup func(*configmocks.Config)
telemetrySetup func(*telemetrymocks.Telemetry)
handler nethttp.HandlerFunc
requestPath string
expectPanic bool
}{
{
name: "Success: Request is traced and metrics recorded",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
_, _ = w.Write([]byte("OK"))
},
},
{
name: "Ignored: Excluded path is skipped",
requestPath: "/health",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = true
c.ExcludedPaths = []string{"/health"}
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Ignored: Disabled via config",
requestPath: "/users",
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg interface{}) {
c := cfg.(*ServerConfig)
c.Enabled = false
}).Return(nil).Once()
},
telemetrySetup: func(mockTelemetry *telemetrymocks.Telemetry) {
// If disabled, Tracer/Meter should NOT be initialized
},
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
w.WriteHeader(nethttp.StatusOK)
},
},
{
name: "Panic: Metrics recorded as 500 and panic re-thrown",
requestPath: "/crash",
expectPanic: true,
configSetup: func(mockConfig *configmocks.Config) {
mockConfig.EXPECT().UnmarshalKey("telemetry.instrumentation.http_server", mock.Anything).
Run(func(_ string, cfg any) {
c := cfg.(*ServerConfig)
c.Enabled = true
}).Return(nil).Once()
},
telemetrySetup: defaultTelemetrySetup,
handler: func(w nethttp.ResponseWriter, r *nethttp.Request) {
panic("server crash")
},
},
}

for _, tt := range tests {
s.Run(tt.name, func() {
mockConfig := configmocks.NewConfig(s.T())
mockTelemetry := telemetrymocks.NewTelemetry(s.T())

telemetry.ConfigFacade = mockConfig
telemetry.TelemetryFacade = mockTelemetry

tt.configSetup(mockConfig)
tt.telemetrySetup(mockTelemetry)

handler := testMiddleware(tt.handler)
server := httptest.NewServer(handler)
defer server.Close()

client := &nethttp.Client{}

action := func() {
_, err := client.Get(server.URL + tt.requestPath)
if !tt.expectPanic {
s.NoError(err)
}
}

action()
})
}
}
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no test coverage for the case when TelemetryFacade is nil. The middleware includes a nil check that returns early and logs a warning, but this code path is not tested.

Copilot uses AI. Check for mistakes.
unitSeconds = "s"
unitBytes = "By"
)

Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Telemetry function lacks documentation. As a public API function that creates middleware, it should have a comment explaining its purpose, parameters, and return value. This is especially important for exported functions that will be used by developers.

Suggested change
// Telemetry creates HTTP server telemetry middleware that instruments incoming
// requests with tracing and metrics. The optional opts parameters allow
// customizing the server configuration (such as span naming and enabling or
// disabling instrumentation). It returns an http.Middleware that propagates
// context, records spans and metrics when telemetry is enabled, and otherwise
// transparently passes requests through when telemetry is disabled or not
// initialized.

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Dec 29, 2025

Codecov Report

❌ Patch coverage is 67.24138% with 38 lines in your changes missing coverage. Please review.
βœ… Project coverage is 70.05%. Comparing base (c4009cf) to head (0d8fda5).
⚠️ Report is 9 commits behind head on master.

Files with missing lines Patch % Lines
telemetry/instrumentation/http/middleware.go 74.41% 12 Missing and 10 partials ⚠️
telemetry/instrumentation/http/config.go 18.18% 9 Missing ⚠️
telemetry/setup/stubs.go 0.00% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1326      +/-   ##
==========================================
+ Coverage   69.41%   70.05%   +0.64%     
==========================================
  Files         282      285       +3     
  Lines       16630    16868     +238     
==========================================
+ Hits        11543    11817     +274     
+ Misses       4596     4550      -46     
- Partials      491      501      +10     

β˜” View full report in Codecov by Sentry.
πŸ“’ Have feedback on the report? Share it here.

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants