Skip to content

OpenTelemetry: HTTP server request metrics#757

Open
dahlia wants to merge 5 commits intofedify-dev:mainfrom
dahlia:feature/otel-http-meter
Open

OpenTelemetry: HTTP server request metrics#757
dahlia wants to merge 5 commits intofedify-dev:mainfrom
dahlia:feature/otel-http-meter

Conversation

@dahlia
Copy link
Copy Markdown
Member

@dahlia dahlia commented May 7, 2026

This adds OpenTelemetry metrics for inbound HTTP requests handled by Federation.fetch(), giving operators request counts and latency histograms that still work when tracing is sampled.

Closes #736.

Changes

  • Added two metric instruments in @fedify/fedify:

    • fedify.http.server.request.count (Counter, {request})
    • fedify.http.server.request.duration (Histogram, ms)
  • Both instruments use bounded attributes: http.request.method, fedify.endpoint, optional http.response.status_code, and optional fedify.route.template. Raw URL paths, identifier values, query strings, and full inbox URLs are deliberately excluded.

  • fedify.endpoint is a bounded enum: webfinger, nodeinfo, actor, inbox, shared_inbox, outbox, object, following, followers, liked, featured, featured_tags, collection, not_found, not_acceptable, and error. User-defined collection dispatchers (collection:*, orderedCollection:*) collapse to collection, since their names are application-defined.

  • When a handler throws after route classification, the metric keeps the matched endpoint, such as actor. error is reserved for the case where classification itself fails, so per-endpoint fault attribution stays meaningful.

  • http.request.method is normalized to the OpenTelemetry-standard set (CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE) with _OTHER for anything else, so an arbitrary client cannot inflate metric cardinality with custom methods.

  • Documented the new instruments, attributes, and endpoint enum in docs/manual/opentelemetry.md, and added fedify.endpoint and fedify.route.template to the semantic-attribute reference table.

  • Updated CHANGES.md.

Notes

The instruments are Fedify-prefixed (fedify.http.server.*) rather than reusing the OpenTelemetry HTTP server semantic-convention name (http.server.request.*). Many apps already emit http.server.* metrics for their own traffic. Reusing that name would mix Fedify endpoint labels with application routing labels on the same instrument.

Issue #736 listed actor_key as a candidate endpoint value, but Fedify dispatches key pairs through callbacks rather than URL routes, so there is no separate actor_key endpoint to classify.

Verification

  • mise check && mise test:deno && mise test:node
  • mise check:md && mise docs:build

Sampled traces alone cannot reliably answer aggregate operational
questions like request rate, p95 latency, or status-code error
rate.  Add two always-on instruments around Federation.fetch():

- fedify.http.server.request.count (Counter, {request})
- fedify.http.server.request.duration (Histogram, ms)

Both carry low-cardinality attributes: http.request.method,
fedify.endpoint, optional http.response.status_code, and optional
fedify.route.template.  Raw URL paths, identifier values, query
strings, and full inbox URLs are deliberately excluded so that
operators can rely on the metrics even when traces are sampled.

The fedify.endpoint attribute is drawn from a fixed enumeration
covering the routes Fedify dispatches (webfinger, nodeinfo, actor,
inbox, shared_inbox, outbox, object, the built-in collections,
generic collection for user-defined dispatchers, plus
not_found, not_acceptable, and error).  When a handler throws after
the route was already classified, the metric retains the matched
endpoint; error is reserved for the pre-classification failure
mode so fault-attribution stays per endpoint.

http.request.method is normalized to the OpenTelemetry-standard set
(CONNECT, DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT, TRACE) and
falls back to _OTHER for any other value, so an arbitrary client
cannot inflate metric cardinality with custom methods.

The metric is implemented on the existing FederationMetrics class
in metrics.ts and wired into the existing fetch()/#fetch() pair
via a small mutable HttpMetricState carrier.  Tests cover the
acceptance criteria from the issue (success, 404, 406, thrown
error) plus shared-inbox routing, route-template emission, the
endpoint enum extensions, the global-MeterProvider fallback, and
the method-normalization regression.

fedify-dev#316
fedify-dev#736

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
@dahlia dahlia added this to the Fedify 2.3 milestone May 7, 2026
@dahlia dahlia self-assigned this May 7, 2026
@dahlia dahlia added type/enhancement Improvements to existing features type/feature Requests for new features component/federation Federation object related component/otel OpenTelemetry integration labels May 7, 2026
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 7, 2026

@codex review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e96cf1b-1606-4cfc-a843-84366ccfe540

📥 Commits

Reviewing files that changed from the base of the PR and between ffc7fd3 and 23a6491.

📒 Files selected for processing (1)
  • packages/fedify/src/federation/middleware.test.ts

📝 Walkthrough

Walkthrough

This PR adds OpenTelemetry HTTP server request metrics to Fedify's Federation.fetch(). It introduces a counter and histogram, records method/endpoint (with normalized method), optional status code and route template, classifies endpoints from routes, and updates tests, docs, and the changelog.

Changes

HTTP Server Request Metrics for Federation.fetch()

Layer / File(s) Summary
Metric Instruments & Contracts
packages/fedify/src/federation/metrics.ts
New httpServerRequestCount (counter) and httpServerRequestDuration (histogram) instruments with fedify.http.server.request.count and fedify.http.server.request.duration names; recordHttpServerRequest() and HTTP method normalization helpers added.
Middleware Integration
packages/fedify/src/federation/middleware.ts
fetch() initializes metricState and captures start time; records metrics on both success and error. #fetch() accepts and mutates metricState to set endpoint and routeTemplate.
Endpoint Classification
packages/fedify/src/federation/middleware.ts
Added FedifyEndpoint taxonomy, HttpMetricState, and getEndpointCategory(routeName) mapping route names to categories like webfinger, nodeinfo, actor, inbox, shared_inbox, outbox, followers, collection, not_found, not_acceptable, and error.
Comprehensive Tests
packages/fedify/src/federation/middleware.test.ts
New test suite asserting emitted fedify.http.server.request.count and fedify.http.server.request.duration with expected attributes across success, 404, 406, exception, collection/followers/shared inbox, and method-normalization cases; asserts noop when no custom meter provider.
OpenTelemetry Documentation
docs/manual/opentelemetry.md
Documents the two new instruments and attribute rules: required http.request.method and fedify.endpoint, bounded method normalization (unknown → _OTHER), conditional http.response.status_code and fedify.route.template, cardinality exclusions (no full URLs/queries/IDs), and enumerates fedify.endpoint values.
Changelog
CHANGES.md
Version 2.3.0 entry describing the new HTTP server metrics, attribute conventions, and updated link references including #736 and #757.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant fetch as Federation.fetch()
  participant router as `#fetch`()
  participant metrics as FederationMetrics.recordHttpServerRequest

  Client->>fetch: HTTP request
  fetch->>fetch: initialize metricState, capture start time
  fetch->>router: route request (mutates metricState)
  router-->>fetch: returns response or throws
  fetch->>metrics: recordHttpServerRequest(metricState, status, duration)
  metrics-->>fetch: metrics emitted
  fetch-->>Client: response or error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

  • fedify-dev/fedify#755: Adds related OpenTelemetry metrics and meterProvider plumbing within FederationMetrics; closely related.

Suggested reviewers

  • 2chanhaeng
🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding OpenTelemetry metrics for HTTP server requests.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining the metrics added, their attributes, and how they integrate into Fedify.
Linked Issues check ✅ Passed The pull request comprehensively addresses all core requirements from #736: implements both metrics instruments with proper attributes, defines endpoint taxonomy with collection collapsing, normalizes HTTP methods, and includes documentation updates.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the scope of #736: HTTP server metrics implementation in @fedify/fedify, documentation updates, changelog updates, and comprehensive tests.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces OpenTelemetry HTTP server metrics for inbound requests handled by Federation.fetch(), adding fedify.http.server.request.count and fedify.http.server.request.duration instruments. These metrics track request volume and latency with attributes for normalized HTTP methods, endpoint categories, status codes, and URI route templates to ensure bounded cardinality. The changes include core metrics implementation, middleware integration, updated documentation, and new test cases. I have no feedback to provide as no review comments were submitted.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b46c62741b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/fedify/src/federation/metrics.ts
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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/fedify/src/federation/metrics.ts`:
- Around line 60-66: The histogram created by meter.createHistogram for
instrument httpServerRequestDuration needs explicit bucket boundaries so P95 can
be accurately computed; update the call that creates
"fedify.http.server.request.duration" (the meter.createHistogram invocation that
assigns httpServerRequestDuration) to include an options.advice object with
explicitBucketBoundaries tuned to millisecond latencies (e.g. a sequence
covering low ms to high ms values around expected P95 — e.g.
[1,2,5,10,20,50,100,200,500,1000,2000]) so the SDK aggregates correctly; keep
description and unit as-is and only add the advice.explicitBucketBoundaries
array.

In `@packages/fedify/src/federation/middleware.ts`:
- Around line 1765-1768: Change HttpMetricState.endpoint from an unconstrained
string to a string-literal union that matches the bounded set of values produced
by getEndpointCategory (e.g. include "error", "not_found", "not_acceptable" and
whatever other categories getEndpointCategory returns), and update
getEndpointCategory's return type to a shared FedifyEndpoint union so both the
function and the interface use the same literal set; this ensures
HttpMetricState.endpoint is typed to the exact allowed values and will catch
mismatches at compile time.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9b203e40-737a-4842-a31b-ff72e00ce77a

📥 Commits

Reviewing files that changed from the base of the PR and between b85b5da and b46c627.

📒 Files selected for processing (5)
  • CHANGES.md
  • docs/manual/opentelemetry.md
  • packages/fedify/src/federation/metrics.ts
  • packages/fedify/src/federation/middleware.test.ts
  • packages/fedify/src/federation/middleware.ts

Comment thread packages/fedify/src/federation/metrics.ts
Comment thread packages/fedify/src/federation/middleware.ts
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 93.84615% with 8 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/fedify/src/federation/middleware.ts 87.50% 0 Missing and 8 partials ⚠️
Files with missing lines Coverage Δ
packages/fedify/src/federation/metrics.ts 100.00% <100.00%> (ø)
packages/fedify/src/federation/middleware.ts 96.02% <87.50%> (-0.06%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dahlia added 3 commits May 7, 2026 15:29
OpenTelemetry's HTTP semantic conventions list QUERY as part of
the default known-method set for http.request.method.  Without
this, valid QUERY requests collapse into the _OTHER bucket and
become indistinguishable from truly custom methods, skewing
method-level request counts and latency.

Add QUERY to KNOWN_HTTP_METHODS, document it in the normalized-
method enumeration, and add a regression test asserting that a
QUERY request is recorded with http.request.method=QUERY.

fedify-dev#757 (comment)

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
Without advice.explicitBucketBoundaries, the SDK falls back to its
default boundary set, which doesn't align with the latency
distribution Fedify expects from inbound HTTP requests and makes
P95 less precise than it could be.

Mirror the OpenTelemetry HTTP server semantic-conventions
recommended buckets, converted from seconds to milliseconds to
match the histogram's unit:

  5, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500,
  10000

The activitypub.delivery.duration and
activitypub.inbox.processing_duration histograms are intentionally
left untouched: they have different latency profiles from inbound
HTTP requests and are out of scope for this PR.

fedify-dev#757 (comment)

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
HttpMetricState.endpoint and getEndpointCategory() previously used
the unconstrained string type, so the type system gave no guidance
when the metric attribute enum drifted from the documented set.

Introduce a FedifyEndpoint string-literal union covering the 16
documented values (webfinger, nodeinfo, actor, inbox, shared_inbox,
outbox, object, following, followers, liked, featured,
featured_tags, collection, not_found, not_acceptable, error) and
use it for both the metric-state field and the helper's return
type.  Future drift between the documented enum and the recorded
attribute will now surface at compile time.

fedify-dev#757 (comment)

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 7, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 7, 2026

/gemini review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces OpenTelemetry HTTP server metrics for inbound requests handled by Federation.fetch(), specifically adding fedify.http.server.request.count and fedify.http.server.request.duration. These metrics track request methods, endpoint categories, status codes, and route templates while maintaining bounded cardinality through method normalization and fixed endpoint enumerations. The changes include the core implementation in the metrics and middleware modules, extensive test coverage, and updated documentation in the manual and changelog. I have no feedback to provide as no review comments were submitted.

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/fedify/src/federation/middleware.test.ts`:
- Around line 1459-1496: Add a user-defined collection dispatcher in
createTestContext to exercise the collection collapse behavior and assert its
metadata maps to the stable "collection" endpoint: call
federation.setCollectionDispatcher("/users/{identifier}/likes", () => ({ items:
[] })) (or the project's collection-registration API) inside createTestContext
alongside the existing setActorDispatcher, setNodeInfoDispatcher,
setFollowersDispatcher and setInboxListeners, then add a test step that invokes
the dispatcher and verifies the resulting fedify.endpoint value equals
"collection".
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 251ff727-d326-4879-936c-f02268288d36

📥 Commits

Reviewing files that changed from the base of the PR and between b46c627 and ffc7fd3.

📒 Files selected for processing (4)
  • docs/manual/opentelemetry.md
  • packages/fedify/src/federation/metrics.ts
  • packages/fedify/src/federation/middleware.test.ts
  • packages/fedify/src/federation/middleware.ts

Comment thread packages/fedify/src/federation/middleware.test.ts
The PR documents that user-defined collection dispatchers
(setCollectionDispatcher) collapse to fedify.endpoint=collection
regardless of the dispatcher's name, but createTestContext() never
registered one, so a regression that emitted the raw dispatcher
name instead of "collection" would slip through.

Register a custom collection dispatcher at
/users/{identifier}/custom/{id} in createTestContext() and add a
test step asserting that a GET to that path records
fedify.endpoint=collection and the parameterized
fedify.route.template.

fedify-dev#757 (comment)

Assisted-by: Claude Code:claude-opus-4-7
Assisted-by: Codex:gpt-5.5
@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 7, 2026

@codex review

@dahlia
Copy link
Copy Markdown
Member Author

dahlia commented May 7, 2026

/gemini review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces OpenTelemetry HTTP server metrics for inbound requests handled by Federation.fetch(). It adds fedify.http.server.request.count and fedify.http.server.request.duration metrics, updates the documentation, and includes comprehensive tests to verify the metric recording across various request scenarios. I have no feedback to provide.

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

Labels

component/federation Federation object related component/otel OpenTelemetry integration type/enhancement Improvements to existing features type/feature Requests for new features

Development

Successfully merging this pull request may close these issues.

OpenTelemetry metrics for Fedify HTTP request performance

1 participant