-
Notifications
You must be signed in to change notification settings - Fork 53
feat: improve observability #956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 3 commits
d1e7b5c
a49aa8c
a07702a
98adc18
ec27f46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -313,7 +313,16 @@ const wrapLoader = ( | |||||||||||||||||||||||||||||||||||||||
| new Response(jsonStringEncoded, { | ||||||||||||||||||||||||||||||||||||||||
| headers: headers, | ||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||
| ).catch((error) => logger.error(`loader error ${error}`)); | ||||||||||||||||||||||||||||||||||||||||
| ).catch((error) => | ||||||||||||||||||||||||||||||||||||||||
| logger.error(`loader error ${error}`, { | ||||||||||||||||||||||||||||||||||||||||
| loader, | ||||||||||||||||||||||||||||||||||||||||
| cacheKey: cacheKeyValue, | ||||||||||||||||||||||||||||||||||||||||
| error: { | ||||||||||||||||||||||||||||||||||||||||
| message: error.message, | ||||||||||||||||||||||||||||||||||||||||
| stack: error.stack, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| return json; | ||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||
|
|
@@ -336,7 +345,14 @@ const wrapLoader = ( | |||||||||||||||||||||||||||||||||||||||
| stats.cache.add(1, { status, loader }); | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| callHandlerAndCache().catch((error) => | ||||||||||||||||||||||||||||||||||||||||
| logger.error(`loader error ${error}`) | ||||||||||||||||||||||||||||||||||||||||
| logger.error(`loader error ${error}`, { | ||||||||||||||||||||||||||||||||||||||||
| loader, | ||||||||||||||||||||||||||||||||||||||||
| cacheKey: cacheKeyValue, | ||||||||||||||||||||||||||||||||||||||||
| error: { | ||||||||||||||||||||||||||||||||||||||||
| message: error.message, | ||||||||||||||||||||||||||||||||||||||||
| stack: error.stack, | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+348
to
356
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Same here: flatten and standardize exception fields in revalidate path Match the structure used above for consistency and exporter compatibility. Apply: - callHandlerAndCache().catch((error) =>
- logger.error(`loader error ${error}`, {
- loader,
- cacheKey: cacheKeyValue,
- error: {
- message: error.message,
- stack: error.stack,
- },
- })
- );
+ callHandlerAndCache().catch((error) =>
+ logger.error(`loader revalidate failed`, {
+ loader,
+ cacheKey: cacheKeyValue,
+ "exception.type": error?.name,
+ "exception.message": error?.message,
+ "exception.stacktrace": error?.stack,
+ "event.name": "loader.revalidate.error",
+ })
+ );📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||
| status = "hit"; | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,7 @@ import * as log from "@std/log"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { type LevelName, LogLevels } from "@std/log/levels"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { LogRecord } from "@std/log/logger"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Context } from "../../deco.ts"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Attributes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -133,6 +134,9 @@ export class OpenTelemetryHandler extends log.BaseHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const otelSeverityNumber = this.toOtelSeverityNumber(logRecord.level); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Automatically enrich logs with request context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const requestContext = this.extractRequestContext(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this._logger?.emit({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severityNumber: otelSeverityNumber, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| severityText: OTEL_SEVERITY_NAME_MAP[otelSeverityNumber] ?? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -141,10 +145,40 @@ export class OpenTelemetryHandler extends log.BaseHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| attributes: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...typeof firstArg === "object" ? firstArg : {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| loggerName: logRecord.loggerName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...requestContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Extracts request context data from the current context | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private extractRequestContext(): Record<string, unknown> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const activeContext = Context.active(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const requestContext = activeContext.request; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| siteId: activeContext.siteId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| siteName: activeContext.site, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| platform: activeContext.platform, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| deploymentId: activeContext.deploymentId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| request: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| url: requestContext?.url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| method: requestContext?.method, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pathname: requestContext?.pathname, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userAgent: requestContext?.userAgent, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| correlationId: requestContext?.correlationId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| framework: requestContext?.framework, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error("Error extracting request context"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If context is not available, return empty object | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Return flat, spec-aligned keys (and default framework) from extractRequestContext Use dot-delimited keys and OTEL-ish names; avoid nested objects. Also default framework to "fresh". Apply: - private extractRequestContext(): Record<string, unknown> {
+ private extractRequestContext(): Record<string, unknown> {
try {
const activeContext = Context.active();
- const requestContext = activeContext.request;
-
- return {
- siteId: activeContext.siteId,
- siteName: activeContext.site,
- platform: activeContext.platform,
- deploymentId: activeContext.deploymentId,
- request: {
- url: requestContext?.url,
- method: requestContext?.method,
- pathname: requestContext?.pathname,
- userAgent: requestContext?.userAgent,
- correlationId: requestContext?.correlationId,
- framework: requestContext?.framework,
- },
- };
+ const r = activeContext.request;
+ return {
+ "site.id": activeContext.siteId,
+ "site.name": activeContext.site,
+ "host.platform": activeContext.platform,
+ "deployment.id": activeContext.deploymentId,
+ "request.url": r?.url,
+ "request.method": r?.method,
+ "request.pathname": r?.pathname,
+ "user_agent.original": r?.userAgent,
+ "request.correlation_id": r?.correlationId,
+ "framework.name": r?.framework ?? "fresh",
+ };
} catch {
console.error("Error extracting request context");
// If context is not available, return empty object
return {};
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log(_msg: string) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| flush() {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -222,13 +222,24 @@ export class Deco<TAppManifest extends AppManifest = AppManifest> { | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const liveContext = this.ctx; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const request = forceHttps(req); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const url = new URL(request.url); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // Create a new request context | ||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request.url = request.url; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request.method = request.method; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request.pathname = url.pathname; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request.userAgent = request.headers.get("user-agent") || | ||||||||||||||||||||||||||||||||||||||||||||||||||
| undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| liveContext.request.correlationId = correlationId; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
223
to
236
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid mutating the shared context; use Context.active() to prevent cross-request leaks this.ctx is the global/default context. Mutating liveContext.request here risks leaking request data across concurrent requests if AsyncLocalStorage isn’t bound with a per-request clone. Bind per-request context and/or read the active one before mutation. Apply: - const liveContext = this.ctx;
+ const liveContext = Context.active();And set the request context atomically to reduce intermediate inconsistent states: - // Create a new request context
- liveContext.request = {};
-
- liveContext.request.url = request.url;
- liveContext.request.method = request.method;
- liveContext.request.pathname = url.pathname;
- liveContext.request.userAgent = request.headers.get("user-agent") ||
- undefined;
- liveContext.request.correlationId = correlationId;
+ liveContext.request = {
+ url: request.url,
+ method: request.method,
+ pathname: url.pathname,
+ userAgent: request.headers.get("user-agent") ?? undefined,
+ correlationId,
+ };📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| state.release = liveContext.release!; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const response = { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| headers: new Headers(defaultHeaders), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| status: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| state.url = new URL(request.url); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| state.url = url; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| state.response = response; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| state.bag = new WeakMap(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| state.vary = vary(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use OTEL exception semantic keys and flatten error attributes
Nested error objects in attributes are non-compliant. Flatten and align with exception.* keys. Also avoid duplicating the error details in both message and attributes if noisy.
Apply:
📝 Committable suggestion
🤖 Prompt for AI Agents