Skip to content

feat(httpserver): split OTLP debug attributes into structured sub-attributes#579

Merged
flemzord merged 7 commits into
mainfrom
feat/otlp-middleware-structured-debug-attributes
Mar 23, 2026
Merged

feat(httpserver): split OTLP debug attributes into structured sub-attributes#579
flemzord merged 7 commits into
mainfrom
feat/otlp-middleware-structured-debug-attributes

Conversation

@flemzord
Copy link
Copy Markdown
Member

Summary

  • Replace the monolithic http.request / http.response span attributes with structured sub-attributes in debug mode
  • New attributes: http.request.method, http.request.url, http.request.host, http.request.proto, http.request.headers, http.request.body, http.response.headers, http.response.body
  • Enables filtering and searching on individual fields in observability tools (Signoz, Jaeger, etc.)

Test plan

  • Package compiles successfully (go build ./pkg/transport/httpserver/...)
  • Deploy a service with debug=true and verify structured attributes appear in traces

…ributes

Replace the single monolithic `http.request` and `http.response` span
attributes with structured sub-attributes for better observability:

- http.request.method, http.request.url, http.request.host, http.request.proto
- http.request.headers, http.request.body
- http.response.headers, http.response.body

This makes it possible to filter and search on individual attributes
in observability tools (Signoz, Jaeger, etc.) instead of parsing a
raw dump.
@flemzord flemzord requested review from a team as code owners March 23, 2026 16:09
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 23, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • .github/workflows/main.yml is excluded by !**/*.yml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c62f089a-628b-4294-b872-ac3dae1ee17b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Middleware replaces raw request/response dumps with structured span attributes (method, url, host, proto, headers, conditional JSON body capture), restores request body for handlers, and buffers responses for status/body attributes; tests add a logger Enabled method; LocalStack test harness now starts a shared server with adjusted container env/port binding.

Changes

Cohort / File(s) Summary
OTLP middleware (debug tracing)
pkg/transport/httpserver/otlp_middleware.go
Replaced raw request/response dump with structured span attributes: always set request metadata (http.request.method, http.request.url, http.request.host, http.request.proto); when debug + JSON content-type, read and set http.request.body and restore r.Body; responseWriter wrapper records status and, when capturing, buffers response bytes and sets http.response.status_code, http.response.headers, and http.response.body (JSON-only body capture in debug).
Test logger tweak
pkg/authn/licence/licence_test.go
Extended mockLogger test double with Enabled(level logging.Level) bool returning true to satisfy logger interface calls in tests.
LocalStack test integration
pkg/messaging/queue/localstack_integration_test.go, pkg/testing/platform/localstacktesting/localstack.go
Moved LocalStack lifecycle to a shared process-wide server (new TestMain) and updated client init to use srv.GetAWSConfig(ctx) / srv.Endpoint(); adjusted container config to add LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1 and changed Docker port binding to let Docker pick host port (keep container port exposed).

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant MW as Middleware
    participant Handler as Handler
    participant RW as BufferedResponseWriter
    participant Tracer as Tracer

    Client->>MW: HTTP request
    MW->>MW: Set span attrs: method, url, host, proto, headers
    alt debug && content-type contains "application/json"
        MW->>MW: Read request body\nSet span attr: http.request.body\nRestore r.Body
    end
    MW->>Handler: Forward request
    Handler->>RW: Write status, headers, body
    RW->>MW: Buffered response captured
    MW->>MW: Set span attrs: http.response.status_code, http.response.headers
    alt debug && captured JSON body
        MW->>MW: Set span attr: http.response.body
    end
    MW->>Tracer: Record/end span
    MW->>Client: Flush buffered response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hop through traces, soft and spry,

I peek at JSON, then say hi.
I fix the stream, then send you on,
I buffer answers till they're gone.
Cheerful hops — the tests pass by!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: refactoring OTLP middleware to use structured sub-attributes instead of monolithic request/response attributes for debug instrumentation.
Description check ✅ Passed The description is directly related to the changeset, clearly explaining the structured attributes introduced, their purpose for observability tools, and test verification steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/otlp-middleware-structured-debug-attributes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
pkg/transport/httpserver/otlp_middleware.go (2)

88-88: Reuse the span variable from line 45.

span is already captured in the enclosing scope; calling trace.SpanFromContext again is unnecessary.

♻️ Proposed fix
-				trace.SpanFromContext(r.Context()).SetAttributes(respAttrs...)
+				span.SetAttributes(respAttrs...)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` at line 88, The code redundantly
calls trace.SpanFromContext(r.Context()) to set attributes even though the
enclosing scope already captures the span in the variable named span; replace
trace.SpanFromContext(r.Context()).SetAttributes(respAttrs...) with
span.SetAttributes(respAttrs...) (or guard with a nil/IsRecording() check if
needed) to reuse the existing span variable in otlp_middleware.go.

62-69: Consider limiting body size and using bytes.NewReader.

Two concerns with the current body handling:

  1. Unbounded memory usage: io.ReadAll consumes the entire request body. In debug mode on high-traffic services, large request bodies (file uploads, batch payloads) could cause memory pressure.

  2. Inefficient conversion: strings.NewReader(string(body)) performs an unnecessary []bytestring → reader conversion.

♻️ Proposed fix

Add bytes import and apply:

+import "bytes"
...
 			// Body
 			if r.Body != nil {
-				body, err := io.ReadAll(r.Body)
+				body, err := io.ReadAll(io.LimitReader(r.Body, 64*1024)) // 64KB limit
 				if err == nil {
 					attrs = append(attrs, attribute.String("http.request.body", string(body)))
-					r.Body = io.NopCloser(strings.NewReader(string(body)))
+					r.Body = io.NopCloser(bytes.NewReader(body))
 				}
 			}

Note: With io.LimitReader, truncated bodies won't match actual request content. You may want to append a marker (e.g., "[truncated]") or set a separate attribute indicating truncation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` around lines 62 - 69, The
current request-body handling uses io.ReadAll and strings.NewReader which can
allocate unbounded memory and double-convert bytes→string; change it to read a
bounded amount using io.LimitReader (e.g., wrap r.Body with io.LimitReader using
a defined maxBodySize constant), read into a []byte buffer, and replace r.Body
with bytes.NewReader(buf) (or io.NopCloser(bytes.NewReader(buf))) to avoid the
string conversion; also detect if the original body was larger than the limit
and append a truncation marker or separate attribute (e.g.,
"http.request.body_truncated") when adding attribute.String("http.request.body",
...) so callers know the payload was truncated — update imports to include
"bytes" and remove the unnecessary "strings" usage and adjust error handling
around the read.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/transport/httpserver/otlp_middleware.go`:
- Line 88: The code redundantly calls trace.SpanFromContext(r.Context()) to set
attributes even though the enclosing scope already captures the span in the
variable named span; replace
trace.SpanFromContext(r.Context()).SetAttributes(respAttrs...) with
span.SetAttributes(respAttrs...) (or guard with a nil/IsRecording() check if
needed) to reuse the existing span variable in otlp_middleware.go.
- Around line 62-69: The current request-body handling uses io.ReadAll and
strings.NewReader which can allocate unbounded memory and double-convert
bytes→string; change it to read a bounded amount using io.LimitReader (e.g.,
wrap r.Body with io.LimitReader using a defined maxBodySize constant), read into
a []byte buffer, and replace r.Body with bytes.NewReader(buf) (or
io.NopCloser(bytes.NewReader(buf))) to avoid the string conversion; also detect
if the original body was larger than the limit and append a truncation marker or
separate attribute (e.g., "http.request.body_truncated") when adding
attribute.String("http.request.body", ...) so callers know the payload was
truncated — update imports to include "bytes" and remove the unnecessary
"strings" usage and adjust error handling around the read.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef201f9a-cfde-44e6-b9f3-03e1d058ce15

📥 Commits

Reviewing files that changed from the base of the PR and between 21e6135 and b676eab.

📒 Files selected for processing (1)
  • pkg/transport/httpserver/otlp_middleware.go

The Logger interface was updated with an Enabled(level) method in
21e6135 but the mock in licence_test.go was not updated, breaking
compilation.
fguery
fguery previously approved these changes Mar 23, 2026
Comment thread pkg/transport/httpserver/otlp_middleware.go Outdated
func (m *mockLogger) Warnf(format string, args ...any) {}
func (m *mockLogger) Debug(args ...any) {}
func (m *mockLogger) Debugf(format string, args ...any) {}
func (m *mockLogger) Enabled(level logging.Level) bool { return true }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Geoffrey's PR was merged?

…equests

- Always: method, url, host, proto, response status_code
- Debug only: request/response headers and body
- Limit debug body capture to 64KB (io.LimitReader)
- Use bytes.NewReader instead of strings.NewReader for body restore
- Reuse span variable in defer (CodeRabbit nitpick)
- Capture response status_code via responseWriter.WriteHeader

Addresses review feedback from @fguery and CodeRabbit.
- Remove arbitrary 64KB body size limit
- Only capture request/response body when Content-Type is application/json
- Skip body buffering entirely for non-JSON responses (no perf overhead)
- Keep always-on: method, url, host, proto, status_code
- Keep debug-only: headers, body (JSON only)
LocalStack now requires LOCALSTACK_AUTH_TOKEN or acknowledgement env var.

- Add LOCALSTACK_ACKNOWLEDGE_ACCOUNT_REQUIREMENT=1 to localstacktesting wrapper
- Migrate queue integration tests from elgohr/go-localstack to our own
  localstacktesting wrapper (elgohr lib doesn't support custom env vars)
- Remove unused elgohr/go-localstack dependency
The host port was hardcoded to 4566, causing "port already allocated"
errors when multiple localstack test suites run in parallel.
Let Docker allocate a random available port instead.
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 4.25532% with 45 lines in your changes missing coverage. Please review.
✅ Project coverage is 25.65%. Comparing base (af785cc) to head (575e0b7).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
pkg/transport/httpserver/otlp_middleware.go 0.00% 45 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #579      +/-   ##
==========================================
- Coverage   25.92%   25.65%   -0.27%     
==========================================
  Files         180      180              
  Lines        7098     7163      +65     
==========================================
- Hits         1840     1838       -2     
- Misses       5146     5214      +68     
+ Partials      112      111       -1     

☔ 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
pkg/transport/httpserver/otlp_middleware.go (1)

82-88: ⚠️ Potential issue | 🟡 Minor

Request body not restored if io.ReadAll fails.

If io.ReadAll returns an error (e.g., client disconnects mid-send), the original r.Body is already consumed, but the code doesn't restore it. Downstream handlers will receive an empty or broken body.

🛡️ Proposed fix: always attempt to restore body
 				// Debug: request body (JSON only)
 				if captureBody && r.Body != nil {
 					body, err := io.ReadAll(r.Body)
+					// Always restore body with whatever was read
+					r.Body = io.NopCloser(bytes.NewReader(body))
 					if err == nil {
 						span.SetAttributes(attribute.String("http.request.body", string(body)))
-						r.Body = io.NopCloser(bytes.NewReader(body))
 					}
 				}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` around lines 82 - 88, When
capturing the request body in the otlp middleware (see captureBody, r.Body and
io.ReadAll usage), ensure the request body is always restored for downstream
handlers even if io.ReadAll returns an error: read r.Body into a byte slice, and
regardless of whether ReadAll returned an error, reconstruct r.Body as
io.NopCloser(bytes.NewReader(body)) so downstream handlers get the (possibly
partial) bytes; still set span attributes only on successful full reads
(span.SetAttributes), and consider logging the read error, but do not leave
r.Body consumed when io.ReadAll fails.
🧹 Nitpick comments (1)
pkg/transport/httpserver/otlp_middleware.go (1)

47-50: Consider logging instead of panicking on write failure.

If the client disconnects mid-response, Write will fail and panic will crash the goroutine. While recovery middleware may catch this, logging the error would be more graceful.

♻️ Suggested improvement
-	_, err := w.ResponseWriter.Write(w.data)
-	if err != nil {
-		panic(err)
-	}
+	if _, err := w.ResponseWriter.Write(w.data); err != nil {
+		// Client may have disconnected; log and continue
+		// Consider injecting a logger if needed
+		return
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` around lines 47 - 50, The write
error handling in the OTLP middleware currently panics on failure (the block
calling w.ResponseWriter.Write(w.data) and checking err), which is harsh for
client disconnects; replace the panic with a graceful log of the error using the
repository's logger (or the standard logger if none exists) and return
immediately from the handler so the request finishes cleanly. Locate the write
in otlp_middleware.go where w.ResponseWriter.Write(w.data) is used and change
the panic(err) to a logger call (including context like the request path/remote
addr if available) and ensure the function exits after logging instead of
panicking.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/transport/httpserver/otlp_middleware.go`:
- Line 71: The current logic uses captureBody (set from r.Header via
isJSONContent) for both request and response capture; change this to two
separate booleans—captureRequestBody (based on r.Header and isJSONContent) and
captureResponseBody (determined from the response Content-Type after the handler
runs, using the wrapped response headers via the response recorder/writer) and
only capture/append the response body when captureResponseBody is true; update
the middleware around the handler invocation (the response buffering/wrapping
code and the section referencing captureBody, e.g., the variable captureBody and
the response-handling block) to buffer responses in debug mode so you can
inspect the response Content-Type, then decide to log the response body if
captureResponseBody is true.
- Around line 15-27: The responseWriter wrapper currently only implements
WriteHeader and doesn't delegate optional interfaces (http.Flusher,
http.Hijacker, http.CloseNotifier and http.Pusher) so callers lose access to
streaming, hijack, push and close-notify behavior; add methods on type
responseWriter: Flush() to forward to the embedded ResponseWriter if it
implements http.Flusher, Hijack() (returning net.Conn, *bufio.ReadWriter, error)
to forward to http.Hijacker on the underlying writer, CloseNotify() to forward
to http.CloseNotifier (or an equivalent channel) and Push(target string, opts
*http.PushOptions) to forward to http.Pusher, each performing a type assertion
and delegating, and add the required imports ("bufio" and "net").

---

Duplicate comments:
In `@pkg/transport/httpserver/otlp_middleware.go`:
- Around line 82-88: When capturing the request body in the otlp middleware (see
captureBody, r.Body and io.ReadAll usage), ensure the request body is always
restored for downstream handlers even if io.ReadAll returns an error: read
r.Body into a byte slice, and regardless of whether ReadAll returned an error,
reconstruct r.Body as io.NopCloser(bytes.NewReader(body)) so downstream handlers
get the (possibly partial) bytes; still set span attributes only on successful
full reads (span.SetAttributes), and consider logging the read error, but do not
leave r.Body consumed when io.ReadAll fails.

---

Nitpick comments:
In `@pkg/transport/httpserver/otlp_middleware.go`:
- Around line 47-50: The write error handling in the OTLP middleware currently
panics on failure (the block calling w.ResponseWriter.Write(w.data) and checking
err), which is harsh for client disconnects; replace the panic with a graceful
log of the error using the repository's logger (or the standard logger if none
exists) and return immediately from the handler so the request finishes cleanly.
Locate the write in otlp_middleware.go where w.ResponseWriter.Write(w.data) is
used and change the panic(err) to a logger call (including context like the
request path/remote addr if available) and ensure the function exits after
logging instead of panicking.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1773d778-e382-4b11-a839-2677bbcfddf9

📥 Commits

Reviewing files that changed from the base of the PR and between f2a36cf and 960e0e2.

⛔ Files ignored due to path filters (2)
  • go.mod is excluded by !**/*.mod
  • go.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (3)
  • pkg/messaging/queue/localstack_integration_test.go
  • pkg/testing/platform/localstacktesting/localstack.go
  • pkg/transport/httpserver/otlp_middleware.go

Comment on lines 15 to 27
type responseWriter struct {
http.ResponseWriter
data []byte
data []byte
statusCode int
captureBody bool
}

func (w *responseWriter) WriteHeader(code int) {
w.statusCode = code
if !w.captureBody {
w.ResponseWriter.WriteHeader(code)
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing http.Flusher, http.Hijacker, and http.CloseNotifier interface implementations.

The responseWriter wrapper embeds http.ResponseWriter but does not expose the optional interfaces that the underlying writer may implement. This will break:

  • SSE / streaming: Handlers calling w.(http.Flusher).Flush() will panic or get a false type assertion.
  • WebSocket upgrades: w.(http.Hijacker).Hijack() will fail, breaking upgrade handshakes.
  • HTTP/2 Server Push: w.(http.Pusher) won't work.
🔧 Proposed fix: delegate optional interfaces to underlying writer
+func (w *responseWriter) Flush() {
+	if f, ok := w.ResponseWriter.(http.Flusher); ok {
+		f.Flush()
+	}
+}
+
+func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
+	if h, ok := w.ResponseWriter.(http.Hijacker); ok {
+		return h.Hijack()
+	}
+	return nil, nil, fmt.Errorf("hijacking not supported")
+}

You'll also need to add "bufio" and "net" imports.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` around lines 15 - 27, The
responseWriter wrapper currently only implements WriteHeader and doesn't
delegate optional interfaces (http.Flusher, http.Hijacker, http.CloseNotifier
and http.Pusher) so callers lose access to streaming, hijack, push and
close-notify behavior; add methods on type responseWriter: Flush() to forward to
the embedded ResponseWriter if it implements http.Flusher, Hijack() (returning
net.Conn, *bufio.ReadWriter, error) to forward to http.Hijacker on the
underlying writer, CloseNotify() to forward to http.CloseNotifier (or an
equivalent channel) and Push(target string, opts *http.PushOptions) to forward
to http.Pusher, each performing a type assertion and delegating, and add the
required imports ("bufio" and "net").

attribute.String("http.request.proto", r.Proto),
)

captureBody := debug && isJSONContent(r.Header.Get("Content-Type"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Response body capture is conditional on request Content-Type, not response Content-Type.

captureBody is determined by the request's Content-Type (line 71), but it's also used to decide whether to capture the response body (line 115). This means:

  • A POST application/x-www-form-urlencoded request to a JSON API won't have its JSON response captured.
  • Conversely, a JSON request returning an HTML error page would attempt capture (though this may be acceptable).

Consider checking the response's Content-Type header for response body capture instead.

🔧 Proposed fix: separate capture decisions for request and response
 			captureBody := debug && isJSONContent(r.Header.Get("Content-Type"))
+			captureResponseBody := false

 			// ... existing code ...

 			rw := &responseWriter{
 				ResponseWriter: w,
 				data:           make([]byte, 0, 1024),
-				captureBody:    captureBody,
+				captureBody:    debug, // Capture if debug, check content-type later
 			}
 			defer func() {
 				rw.finalize()

 				// ... status code logic ...

 				if debug {
 					// Debug: response headers
 					// ... existing header code ...

 					// Debug: response body (only if JSON content)
-					if captureBody && len(rw.data) > 0 {
+					if isJSONContent(rw.Header().Get("Content-Type")) && len(rw.data) > 0 {
 						span.SetAttributes(attribute.String("http.response.body", string(rw.data)))
 					}
 				}
 			}()

Note: This would also require buffering all debug responses, which may have performance implications. An alternative is to accept the current behavior as a known limitation and document it.

Also applies to: 114-117

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/transport/httpserver/otlp_middleware.go` at line 71, The current logic
uses captureBody (set from r.Header via isJSONContent) for both request and
response capture; change this to two separate booleans—captureRequestBody (based
on r.Header and isJSONContent) and captureResponseBody (determined from the
response Content-Type after the handler runs, using the wrapped response headers
via the response recorder/writer) and only capture/append the response body when
captureResponseBody is true; update the middleware around the handler invocation
(the response buffering/wrapping code and the section referencing captureBody,
e.g., the variable captureBody and the response-handling block) to buffer
responses in debug mode so you can inspect the response Content-Type, then
decide to log the response body if captureResponseBody is true.

@flemzord flemzord enabled auto-merge March 23, 2026 17:14
- Dirty: runs pre-commit (tidy, generate, lint) and checks for uncommitted changes
- Tests: runs test suite and uploads coverage
- Both jobs run in parallel for faster feedback
- Remove Dependabot auto-merge (no longer needed)
@flemzord flemzord added this pull request to the merge queue Mar 23, 2026
Merged via the queue into main with commit 823e476 Mar 23, 2026
6 of 8 checks passed
@flemzord flemzord deleted the feat/otlp-middleware-structured-debug-attributes branch March 23, 2026 19:47
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