Skip to content

Massive clean up#354

Open
izadoesdev wants to merge 12 commits intomainfrom
staging
Open

Massive clean up#354
izadoesdev wants to merge 12 commits intomainfrom
staging

Conversation

@izadoesdev
Copy link
Member

Description

Please include a summary of the change and which issue is fixed. Also include relevant motivation and context.

Checklist
  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Wire evlog/elysia into API, basket, links, and uptime with initLogger.
Replace the old @databuddy/shared pino logger with evlog (including an
RPC-compatible shim). Basket uses wide-event context via useLogger,
moves producer/geo/schema enrichment off OTel span attributes, and drops
setAttributes from basket tracing. Update lockfile and dependencies.
…xiom

Remove OTLP trace SDK; wire createAxiomDrain in initLogger and simplify
tracing helpers to evlog-only captureError/record.
Remove OTLP Node SDK, HTTP/pg/ORPC instrumentations, and request span
plumbing. Wire createAxiomDrain on initLogger; map setAttributes/captureError
to evlog; simplify agent query execution without active spans.
- Add structured-errors with basketErrors factories, ingest helpers, and
  buildBasketErrorPayload for consistent success/error/message responses.
- Throw EvlogErrors from track, llm, request-validation (non-billing), and
  basket ingest routes; keep billing Response and pixel/bot ignore behavior.
- Centralize onError JSON via buildBasketErrorPayload; fix invalid Biome
  nursery key useMaxParams so biome check runs.
@cursor
Copy link

cursor bot commented Mar 21, 2026

You have used all of your free Bugbot PR reviews.

To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 21, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8a57c7dd-30f4-4dff-8c98-b586bc4a3b97

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch staging

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.

@vercel
Copy link

vercel bot commented Mar 21, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard (staging) Ready Ready Preview, Comment Mar 21, 2026 8:44pm
databuddy-links Ready Ready Preview, Comment Mar 21, 2026 8:44pm
documentation (staging) Ready Ready Preview, Comment Mar 21, 2026 8:44pm

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 21, 2026

Greptile Summary

This PR is a large-scale observability infrastructure swap: OpenTelemetry (SDK, exporters, span processors) is removed from api and basket, and replaced throughout with evlog — a structured wide-event logger that integrates with the Elysia request lifecycle. links and uptime keep their existing OTel tracing but also gain evlog for log draining. Alongside the logging migration, basket introduces a typed EvlogError hierarchy (structured-errors.ts) that replaces ad-hoc new Response(JSON.stringify(...)) returns with throw basketErrors.*(), and the RPC package moves from pino/@axiomhq/pino to a log-based shim.

Key changes:

  • All setAttributes() / span lifecycle calls replaced with useLogger().set({})/log.info()/log.error() request-scoped calls
  • validateRequest() in basket now throws EvlogError on validation failures instead of returning { error: Response } (billing-exceeded still returns the response object for differentiation)
  • packages/shared/src/utils/logger.ts (Pino) and @axiomhq/pino dependency removed; pino/pino-pretty also dropped from shared
  • biome.jsonc re-enables the useMaxParams lint rule

Issues found:

  • The new Pino-compat shim in packages/rpc/src/lib/logger.ts silently drops logger.method({ fields }) calls that don't include a message string (missing else-branch in emit).
  • apps/api/src/routes/public/flags.ts parseProperties swallows JSON parse errors with no log output, removing a useful observability signal.
  • apps/links/src/index.ts and apps/uptime/src/index.ts initialise evlog without an Axiom drain, unlike api and basket, so structured logs from those services won't be shipped.

Confidence Score: 4/5

  • Safe to merge after fixing the silent log-drop in the RPC logger shim; other findings are observability gaps that don't affect correctness.
  • The core migration is well-executed: OTel removed cleanly, EvlogError hierarchy is solid, error handling improved. The P1 finding (silent drop in emit) is a real correctness gap in the logger shim but doesn't affect runtime request behaviour since all current call-sites pass a message string. The P2 findings are observability regressions (missing drain in links/uptime, silent parseProperties failure) but do not break functionality.
  • packages/rpc/src/lib/logger.ts needs the missing else-branch; apps/links/src/index.ts and apps/uptime/src/index.ts need createAxiomDrain() added to their initLogger calls.

Important Files Changed

Filename Overview
apps/basket/src/lib/structured-errors.ts New file introducing a typed error hierarchy via evlog's createError/EvlogError. Well-structured with clear why/fix messages and a buildBasketErrorPayload helper that centralises error serialisation. No issues found.
packages/rpc/src/lib/logger.ts New Pino-compatible shim over evlog. The emit helper has a missing else-branch: calls of the form logger.info({ fields }) (object, no message string) silently emit nothing. All current call sites happen to pass a message, but the gap is a latent trap.
apps/api/src/routes/public/flags.ts Migrated from logger to useLogger(). The parseProperties error handler no longer logs the parse error, silently swallowing invalid JSON with no observability signal.
apps/basket/src/lib/request-validation.ts Significant refactor replacing error-return pattern with EvlogError throws. Body/query typed from any to unknown with explicit narrowing. Billing-block still returns { error: Response } for differentiation. Clean and correct.
apps/api/src/index.ts Removes OpenTelemetry span-lifecycle hooks; swaps in evlog middleware. Error handler extended with structured why/fix/link fields via parseError. SIGINT/SIGTERM handlers simplified (shutdown no longer awaited).
apps/basket/src/lib/tracing.ts OTel SDK fully removed. record() is a thin pass-through, captureError delegates to useLogger() with a global log fallback. setAttributes and span lifecycle functions deleted.
apps/basket/src/index.ts OTel middleware and span-state removed; replaced with evlog() plugin. Global error handler now calls buildBasketErrorPayload. unhandledRejection/uncaughtException handlers log via evlog global log.
apps/links/src/index.ts Adds evlog middleware alongside retained OpenTelemetry. initLogger is called without an Axiom drain (unlike api and basket), so structured logs will not be shipped to the backend.
apps/uptime/src/index.ts Adds evlog middleware alongside retained OpenTelemetry. initLogger is also missing an Axiom drain, matching the links service inconsistency.
apps/basket/src/utils/parsing-helpers.ts createSchemaErrorResponse/createBotDetectedResponse refactored to return plain objects (batch-safe). validateEventSchema logs via global log.info instead of the request-scoped useLogger(), so the context won't be merged into the wide request event.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Elysia as Elysia Middleware
    participant evlog as evlog Plugin<br/>(Wide Event)
    participant Handler as Route Handler
    participant ValidateReq as validateRequest()
    participant ErrorHandler as Global onError

    Client->>Elysia: HTTP Request
    Elysia->>evlog: Initialise wide event per request
    evlog->>Handler: request context available

    Handler->>Handler: useLogger().set({ route })
    Handler->>ValidateReq: validateRequest(body, query, request)

    alt Validation error (payload, clientId, origin, IP)
        ValidateReq-->>Handler: throw EvlogError (status 4xx)
        Handler-->>ErrorHandler: EvlogError re-thrown
        ErrorHandler->>ErrorHandler: buildBasketErrorPayload()
        ErrorHandler-->>Client: { status, error, why, fix }
    else Billing exceeded
        ValidateReq-->>Handler: return { error: Response }
        Handler-->>Client: billing.response (429)
    else Success
        ValidateReq-->>Handler: return ValidatedRequest
        Handler->>Handler: useLogger().set({ clientId, eventType, … })
        Handler-->>Client: 200 OK
    end

    evlog->>evlog: Flush wide event to Axiom drain
Loading

Comments Outside Diff (2)

  1. apps/api/src/routes/public/flags.ts, line 831-836 (link)

    P2 Silent JSON parse failure — no observability signal

    The previous code logged a warn with the raw propertiesJson string, which made debugging malformed payloads possible. The PR removes the log entirely, leaving a silent {} fallback that gives no indication of the failure in production.

  2. apps/basket/src/utils/parsing-helpers.ts, line 47 (link)

    P2 Validation context logged to global log, not the request-scoped wide event

    validateEventSchema is always called from within an Elysia request handler, so useLogger() is available. Using the global log.info here means the validation fields won't be merged into the per-request wide event that evlog's middleware accumulates.

Last reviewed commit: "feat(basket): unify ..."

Comment on lines +13 to +27
log[level]({ ...base, message: fieldsOrMessage });
} else if (message !== undefined) {
log[level]({ ...base, ...fieldsOrMessage, message });
}
}

/**
* Pino-compatible (obj, msg) or (msg) logging via evlog global `log`.
*/
export const logger = {
error: (fieldsOrMessage: Fields | string, message?: string) =>
emit("error", fieldsOrMessage, message),
info: (fieldsOrMessage: Fields | string, message?: string) =>
emit("info", fieldsOrMessage, message),
warn: (fieldsOrMessage: Fields | string, message?: string) =>
Copy link
Contributor

Choose a reason for hiding this comment

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

P1 Silent log drop when called with only an object

The emit helper has no else branch. When it is called as logger.info({ someField }) — an object with no message string — neither condition matches and the call is silently discarded. Pino (which this shim is replacing) would emit the entry with an empty msg field.

All current call-sites happen to pass a message, but the contract mismatch is a latent trap for any future caller.

Suggested change
log[level]({ ...base, message: fieldsOrMessage });
} else if (message !== undefined) {
log[level]({ ...base, ...fieldsOrMessage, message });
}
}
/**
* Pino-compatible (obj, msg) or (msg) logging via evlog global `log`.
*/
export const logger = {
error: (fieldsOrMessage: Fields | string, message?: string) =>
emit("error", fieldsOrMessage, message),
info: (fieldsOrMessage: Fields | string, message?: string) =>
emit("info", fieldsOrMessage, message),
warn: (fieldsOrMessage: Fields | string, message?: string) =>
function emit(
level: "error" | "info" | "warn",
fieldsOrMessage: Fields | string,
message?: string
): void {
if (typeof fieldsOrMessage === "string") {
log[level]({ ...base, message: fieldsOrMessage });
} else if (message !== undefined) {
log[level]({ ...base, ...fieldsOrMessage, message });
} else {
// Object-only call (no message string) – emit as-is
log[level]({ ...base, ...fieldsOrMessage });
}
}

Comment on lines 25 to +28
});

initLogger({
env: { service: "links" },
Copy link
Contributor

Choose a reason for hiding this comment

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

P2 Missing Axiom drain — logs stay local

api and basket both configure drain: createAxiomDrain() in their initLogger calls. links (and uptime — same pattern at apps/uptime/src/index.ts:9-11) omit it, so structured logs from these services will never reach Axiom.

Suggested change
});
initLogger({
env: { service: "links" },
initLogger({
env: { service: "links" },
drain: createAxiomDrain(),
});

Wire api and basket through createDrainPipeline, initLogger tail sampling,
middleware enrich (UA, request size, trace context), and optional NDJSON to
apps/*/.evlog/logs in development. Basket: structured errors in auth, geo,
producer, and request validation wide events. Ignore **/.evlog/ at repo root.
Biome: disable useHookAtTopLevel for evlog useLogger in api and basket.
Replace z.record output schemas with explicit Zod models for getAnalytics
and getAnalyticsByReferrer so handlers match processFunnelAnalytics return types.
@railway-app railway-app bot temporarily deployed to Databuddy / production March 21, 2026 19:56 Inactive
@vercel vercel bot temporarily deployed to Preview – databuddy-links March 21, 2026 19:56 Inactive
@vercel vercel bot temporarily deployed to staging – documentation March 21, 2026 19:56 Inactive
- Replace OpenTelemetry span helpers in @databuddy/rpc with evlog wide-event helpers (rpc-log-context.ts); remove @opentelemetry/api and @orpc/otel.

- API: mergeWideEvent/captureError patterns, drop no-op record() wrapper, basket-style uncaught handlers, logging on MCP/health/insights/public routes.

- Touch RPC routers and lockfile for consistency with the RPC logging changes.
…vent enricher

Replace fragmented API key enricher with applyAuthWideEvent that resolves
both session and API key in parallel. Strip duplicated auth fields from
RPC enricher, remove redundant route fields from health/mcp/insights.
@railway-app railway-app bot temporarily deployed to Databuddy / production March 21, 2026 20:24 Inactive
@vercel vercel bot temporarily deployed to staging – documentation March 21, 2026 20:24 Inactive
@vercel vercel bot temporarily deployed to Preview – databuddy-links March 21, 2026 20:24 Inactive
…ext output

- Pre-fetch summary, top pages, errors, referrers for WoW comparison
- Replace ToolLoopAgent with generateText + Output.object (non-deprecated)
- Tighten prompts and expand insight type taxonomy
@railway-app railway-app bot temporarily deployed to Databuddy / production March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to staging – documentation March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to staging – dashboard March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to Preview – databuddy-links March 21, 2026 20:38 Inactive
@railway-app railway-app bot temporarily deployed to Databuddy / production March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to staging – documentation March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to Preview – databuddy-links March 21, 2026 20:38 Inactive
@vercel vercel bot temporarily deployed to staging – dashboard March 21, 2026 20:38 Inactive
@railway-app railway-app bot temporarily deployed to Databuddy / production March 21, 2026 20:43 Inactive
@vercel vercel bot temporarily deployed to Preview – databuddy-links March 21, 2026 20:43 Inactive
@vercel vercel bot temporarily deployed to staging – documentation March 21, 2026 20:43 Inactive
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.

1 participant