Repository navigation
feat: API monitoring (M16) - #72
Closed
productdevbook wants to merge 69 commits into
Closed
productdevbook wants to merge 69 commits into
productdevbook wants to merge 69 commits into
Conversation
…shboards, service health and status pages Records the decisions for #63 and its steps #64-#71 in the architecture and the roadmap: an API is an analytics property of kind api, no row per request or address is stored, percentiles come from mergeable sketches, uptime checks and health pulls go through the webhook dialer's address checks, and status pages on custom domains are verified by CNAME and TXT. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…rates and p50, p95 and p99
An analytics property can now be of kind api. Its host sends request records to
POST /v1/analytics/properties/{id}/requests (1-1,000 a call, gzip, analytics:write);
each process adds them up per UTC minute and local hour, with log-bucketed duration
sketches, and writes cumulative rows under its own writer every 10 seconds, so
Postgres holds no row per request and no address. Paths become route patterns with
ids taken out, 404 and 405 probes count as (unmatched), and caps send new values to
(other). The monitor_rollup job folds closed hours and finished local days, and the
query operation answers dataset api from days, hours and minutes.
YUVA_MONITOR, YUVA_MONITOR_MONTHLY_REQUESTS, YUVA_MONITOR_RECORDS_PER_MINUTE,
YUVA_MONITOR_BUFFER_KEYS, YUVA_MONITOR_MINUTE_DAYS and yuva workspace monitor-limit
set it up.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…by the mux's route Middleware reads r.Pattern after the ServeMux ran (method and host left out) or a Route function, and sends route, method, status, duration and sizes in gzip batches of 500 every 5 seconds through the client's queue and retries; SampleRate thins successful requests only. The API client is regenerated for the requests endpoint. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…t records Co-Authored-By: Claude Opus 5.5 <[email protected]>
Writes 30 days of folded rows for one property with 500 routes, drives the records endpoint at 2,000 records a second from 5,000 consumers and times ingest, visibility and the overview queries. Runs with YUVA_TEST_MONITOR_LOAD set. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
Route inference also applies to host-sent patterns, the id rule for long tokens, path_rules as route rules, status class none, 422 property_kind, local minute labels, the 5-second cache on open ranges, the late counter and the monthly limit counting received records. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
… per route An analytics property can now be added as an API. Its overview shows requests, requests per minute, error and client error rates, p95 and p99, the last 60 minutes by minute, requests, error rates and latency over time and the busiest routes; Routes lists every route and method sorted by traffic, errors or latency; a route's page has the same history and its status codes. Ranges include the last 15 minutes, hour, 6 hours and 24 hours. Reached caps and sampled (estimated) counts are shown, and so is the monthly request limit. Until the first request arrives, the property shows the Go middleware and a curl example for the records endpoint. en and tr. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…it, change time zones at midnight
GET /v1/analytics/properties/{id}/routes lists an API property's routes with their
inferred and hidden flags, and PATCH .../routes/{routeId} hides or shows one; hidden
routes are left out of dataset api rows and totals. Request records reaching
YUVA_MONITOR_MONTHLY_REQUESTS now mail the owners and add an operator digest line once
a month, as the analytics event limit does, and usage carries
requests_limit_reached_at. A new time zone on an API property applies from the next
local midnight of the zone before (timezone_applies_at), so hours, days, folds and
queries never cut a local day in two.
Refs #64
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…imit notice and time zone changes Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ice and time zone changes Co-Authored-By: Claude Opus 5.5 <[email protected]>
…tice and time zone changes Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An API property gets a Settings tab: name, host names, inbox, time zone (with the pending change until its next midnight), Apdex threshold, how long hourly counts are kept, path rules, hidden routes and deleting the property. Managers hide a route from the routes table or its page, with undo, and show it again from there or from Settings; inferred routes are marked. The request limit warning uses requests_limit_reached_at. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…s wrap on phones A hidden route's page shows only its notice, since every answer leaves the route out. Chart y-axes are as wide as their longest label, so Turkish latency ticks are no longer cut. Route names in the routes table and on a route's page wrap with the inferred tag after them, and on phones the copy button of a code block sits above the code instead of beside it. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…d hours with their UTC offset A time grain alone answered zeros: the zero-fill looked up buckets by time.Time values, whose location is part of equality, so with a server clock outside UTC no bucket matched and the empty ones replaced the data under the same key. Buckets are now keyed by their instant throughout the monitor package, rows are ordered by their bucket's instant, and minute and hour labels carry the offset (2026-10-12T14:05+03:00), so a repeated DST hour and the hours around a pending zone change stay apart. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…e labels Co-Authored-By: Claude Opus 5.5 <[email protected]>
Minute and hour labels now carry their UTC offset. Axes and tooltips show the property-local wall time of each label, in the order the API returns them, and a tooltip adds the offset only when two buckets share a wall time, as in the repeated hour of a zone change. A pending time zone change names the zone its instant is shown in. Refs #64 Co-Authored-By: Claude Opus 5.5 <[email protected]>
… page The page explains receiving support e-mail through Cloudflare Email Routing and the Yuva Email Worker (flow, fallback forwarding, setup, FAQ) in English, Turkish and German, with an empty-by-default film slot. The home hero lists the frameworks, languages and protocols Yuva has guides or SDK support for, with simple-icons marks and links to their guides. Cloudflare appears as text: its guidelines require written permission for the logo. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The older text row duplicated the new icon strip. Its shipped entries (Amazon SES, Postmark, S3/R2/MinIO, APNs/FCM, Docker) move into the strip and the row and its copy are removed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Taken from the Nitro repository (MIT) since simple-icons has none, drawn in one colour like the other marks. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…scroll The h3 chip showed simple-icons' H3, which is Uber's geospatial index; it now uses the mark from the h3 repository (MIT). S3, R2, MinIO, APNs and FCM get their own chips so a mark never stands for several products. The Cloudflare e-mail setup steps no longer stretch past a phone's width, and the hero's glow no longer washes out the strip's last row. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…3, Nitro, Nuxt, Hono, Express, Fastify and Next.js
useyuva/server gets createMonitor, which sends request records to
POST /v1/analytics/properties/{id}/requests gzip'd in batches of 500 or every
5 s, retrying 429 and 5xx with backoff and Retry-After, sampling only
successful requests, like sdk/go/analytics. Each middleware takes the route
pattern from its router (h3 and Nitro v3 matchedRoute.route, Nitro v2
matchedRoute.path, Hono matchedRoutes, Express baseUrl and req.route.path,
Fastify routeOptions.url, Next.js the wrapper's argument), writes it as
/users/{id}, and flushes through waitUntil or after() and on the server's
close. The Nitro and Nuxt modules record every request when a monitor key
and property id are in the server runtime config. createAnalytics now shares
the sender.
Refs #65
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Their API monitoring middleware shipped in useyuva; srvx's mark comes from its repository (MIT), the others from simple-icons. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…uest records
Any service instrumented with OpenTelemetry now reports to an API property
through POST /otlp/{propertyId}/v1/traces (OTLP/HTTP, binary protobuf or JSON,
gzip, an API key with analytics:write). SERVER spans with an HTTP method become
request records through the same checks, route inference, caps, limits and
ingest stats as the records endpoint, weighed by their tracestate ot=th
threshold; answers are ExportTraceServiceResponse with partial_success, 429 or
503 with Retry-After, and google.rpc.Status errors in the request's encoding.
The few fields used are read with protowire, so no OTLP module is added.
The Swift and Kotlin SDKs send the host app's calls to its own API to
POST /client/v1/analytics/{key}/requests with the API property's public key:
up to 100 records with source app, the platform and app version, no
consumer, under the property's collection rules like app events.
Refs #65
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…s and app-side request records Refs #65 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…uest records Refs #65 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…imes from the rollups A route counts as new only when its first request, by the request times the minute, hour and day rows keep, falls in the range; records that arrive late no longer make an old route new. Rows of routes: new and silent carry first_request_at and last_request_at, so the panel needs no query per listed row. Refs #66 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…est times Refs #66 Co-Authored-By: Claude Opus 5.5 <[email protected]>
… shipped Co-Authored-By: Claude Opus 5.5 <[email protected]>
…he new settings and the monitor.alert webhook, in en, tr and de Refs #67 Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Monitoring section in the rail (g u) lists the workspace's uptime checks. A check is added and changed with every field the server takes, with write-only headers and the server's shortest interval, and refused addresses, too short intervals and the check and host limits are said in words. A check's page shows its state, uptime, downtime, response time p50 and p95 over time, recent runs with their DNS, connect, TLS, first-byte and total times, certificate days left and state history. API properties get an Uptime tab for their checks and an Alerts tab with alert rules of every kind, their recipients and on or off, and the alerts they raised, kept current by the monitor.alert realtime message; their settings choose weekly or monthly reports. New and silent routes show the first and last request times the API now returns. Refs #67 Co-Authored-By: Claude Opus 5.5 <[email protected]>
… test servers Co-Authored-By: Claude Opus 5.5 <[email protected]>
Share links of an API property list api_overview, api_routes and api_consumers and read dataset api through the shared query with the link's limits; consumer names only with api_consumers. Exports view and daily take dataset api and uptime in CSV and NDJSON, with the range fixed as instants when asked for; API properties have no raw export. listAnalyticsConsumers and deleteAnalyticsConsumer erase a consumer's dimension rows and name, keep its id reserved (migration 00060), drop its open rows in every process and sweep again after two and eleven minutes. Refs #68 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ring shares, exports and consumers Refs #68 Co-Authored-By: Claude Opus 5.5 <[email protected]>
The contract says at least one metric, but nothing enforced it, and the uptime dataset and support comparisons index the first metric. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…nd erasure shipped Co-Authored-By: Claude Opus 5.5 <[email protected]>
…g in en, tr and de Refs #68 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…n phones Rule and alert titles that the server named, and why a run failed, are built in the panel from the rule's kind, threshold and subject and the run's error code and status, so they read in Turkish too; a name a member gave stays as it is. A failed run is the only one with a red dot. The check page header stacks its state, address and interval on phones, the property link reads as a secondary link, and tab and range rows scroll with faded edges. The palette finds Monitoring by its name. Refs #67 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fail A minute in which at least 20 of the server's checks ran and more than half failed recorded every run unknown, the answers too, so a check that went down stayed down and kept its alert firing for as long as other checks on the server kept failing. Only the failed runs of such a minute are recorded unknown now, as the architecture says; a run that got its expected answer is believed and brings the check back up. A failed run of a check that was never up or down was recorded up, which drew an up period before the first outage; it is recorded with the check's state after it, unknown. Refs #67 Co-Authored-By: Claude Opus 5.5 <[email protected]>
… state descriptions Refs #67 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…PI properties API property settings get Sharing, with the API overview, routes and consumers views and a note that consumer names are shown only with Consumers, and Data: how long counts per hour and per day are kept, exports of requests or uptime checks as the current view or day by day, and the consumers with search and an Erase action that explains totals stay and older export files keep the name until they are deleted. The export list says what each file holds. A share link of an API property opens its own overview, routes and consumers, read-only. Refs #68 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…nit sits under its title Consumers are counted per hour, so the last 24 hours showed nothing until a viewer picked another range; the view, also on share links, now opens on the last 7 days unless the address names a range. The check page's "per hour, UTC" moves under the chart title so it no longer wraps on phones. Refs #68 Co-Authored-By: Claude Opus 5.5 <[email protected]>
…16.7) Unfinished work on #70 kept from the stopped M16.7 session: services, dependencies, instances, health reports, state rules, incidents and the sdk/go/health package. It has not passed the api checks yet. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_019Gs7kyVD7BGLxEiC3fJoEs
This was referenced Oct 14, 2026
|
Important Review skippedToo many files! This PR contains 348 files, which is 248 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. Usage-priced reviews support at most 300 files. ⚙️ Run configuration
⛔ Files ignored due to path filters: 3
📒 Files selected for processing: 348
You can disable this status message by setting the
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. Comment |
This was referenced Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
API monitoring (M16, #63), moved off
mainso it can be reviewed and merged on its own.What is in it
net/httpmiddleware, the panel overview, routes and route pages.createMonitormiddleware for srvx, h3, Nitro, Nuxt, Hono, Express, Fastify and Next.js, request timing in the Swift and Kotlin modules.sdk/go/health, committed as work in progress in the last commit; it has not passed the api checks.docs/architecture.mdanddocs/roadmap.md.Not started: M16.6 dashboards and the Prometheus endpoint (#69), M16.8 status pages (#71).
Before merging
00056–00061come with it; the dev database already has them applied.Checks
Each step was checked when it was written: the openapi lint,
api/andsdk/go/generate, vet, test and build with Postgres,sdk/jscheck and build, theweb/check, the site check, Swift and Kotlin on the Mac, and the panel screens on the dev server.🤖 Generated with Claude Code
https://claude.ai/code/session_019Gs7kyVD7BGLxEiC3fJoEs