From c967d922551c481b1b71da941aedcdaa28d7688b Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Fri, 25 Apr 2025 05:37:04 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9D=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/package.json | 2 +- apps/api/src/lib/logger/gcp-formatters.ts | 4 ++++ apps/api/src/lib/middleware/access-logger.ts | 7 +++++++ apps/api/src/lib/middleware/error-handler.ts | 6 +++--- apps/api/src/lib/middleware/index.ts | 1 + apps/api/src/lib/middleware/unless.ts | 8 ++++++++ apps/api/src/server.ts | 1 + 7 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 apps/api/src/lib/middleware/access-logger.ts create mode 100644 apps/api/src/lib/middleware/unless.ts diff --git a/apps/api/package.json b/apps/api/package.json index 59dd69db..f2ab4699 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -31,7 +31,7 @@ "dev": "tsup --watch", "clean": "del dist", "test": "run-s test:*", - "test:lint": "eslint src", + "test:lint": "eslint .", "test:typecheck": "tsc --noEmit", "test:unit": "vitest run" }, diff --git a/apps/api/src/lib/logger/gcp-formatters.ts b/apps/api/src/lib/logger/gcp-formatters.ts index 12ecb161..0d584b4f 100644 --- a/apps/api/src/lib/logger/gcp-formatters.ts +++ b/apps/api/src/lib/logger/gcp-formatters.ts @@ -57,11 +57,13 @@ export function pinoLevelToGcpSeverity( export function getGcpLoggingTimestamp() { const seconds = Date.now() / 1000 const secondsRounded = Math.floor(seconds) + // The following line is 2x as fast as seconds % 1000 // Uses Math.round, not Math.floor due to JS floating point... // eg for a Date.now()=1713024754120 // (seconds-secondsRounded)*1000 => 119.99988555908203 const millis = Math.round((seconds - secondsRounded) * 1000) + return `,"timestamp":{"seconds":${secondsRounded},"nanos":${millis}000000}` } @@ -86,10 +88,12 @@ export function formatGcpLogObject( entry['logging.googleapis.com/trace'] = entry.trace_id delete entry.trace_id } + if ((entry.span_id as string | undefined)?.length) { entry['logging.googleapis.com/spanId'] = entry.span_id delete entry.span_id } + // Trace flags is a bit field even though there is one on defined bit, // so lets convert it to an int and test against a bitmask. // @see https://www.w3.org/TR/trace-context/#trace-flags diff --git a/apps/api/src/lib/middleware/access-logger.ts b/apps/api/src/lib/middleware/access-logger.ts new file mode 100644 index 00000000..7e724b06 --- /dev/null +++ b/apps/api/src/lib/middleware/access-logger.ts @@ -0,0 +1,7 @@ +import { logger as honoLogger } from 'hono/logger' + +import { logger } from '@/lib/logger' + +import { unless } from './unless' + +export const accessLogger = unless(honoLogger(logger.trace), '/v1/health') diff --git a/apps/api/src/lib/middleware/error-handler.ts b/apps/api/src/lib/middleware/error-handler.ts index 0ec89e77..2e826bcb 100644 --- a/apps/api/src/lib/middleware/error-handler.ts +++ b/apps/api/src/lib/middleware/error-handler.ts @@ -4,8 +4,8 @@ import { createMiddleware } from 'hono/factory' import { HTTPException } from 'hono/http-exception' import type { AuthenticatedEnv } from '@/lib/types' - -import { HttpError } from '../errors' +import { HttpError } from '@/lib/errors' +import { logger } from '@/lib/logger' export const errorHandler = createMiddleware( async function errorHandlerMiddleware(ctx, next) { @@ -28,7 +28,7 @@ export const errorHandler = createMiddleware( } if (status >= 500) { - console.error('http error', status, message) + logger.error({ err, status, message }) Sentry.captureException(err) } diff --git a/apps/api/src/lib/middleware/index.ts b/apps/api/src/lib/middleware/index.ts index 53d01efb..6c94637c 100644 --- a/apps/api/src/lib/middleware/index.ts +++ b/apps/api/src/lib/middleware/index.ts @@ -1,3 +1,4 @@ +export * from './access-logger' export * from './authenticate' export * from './error-handler' export * from './me' diff --git a/apps/api/src/lib/middleware/unless.ts b/apps/api/src/lib/middleware/unless.ts new file mode 100644 index 00000000..661c06dc --- /dev/null +++ b/apps/api/src/lib/middleware/unless.ts @@ -0,0 +1,8 @@ +import type { MiddlewareHandler } from 'hono' + +export const unless = + (mw: MiddlewareHandler, ...excluded: string[]): MiddlewareHandler => + async (c, next) => { + if (excluded.includes(c.req.path)) return next() + return mw(c, next) + } diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 2a7af066..cf2c9a1f 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -16,6 +16,7 @@ export const app = new Hono() app.use(sentry()) app.use(compress()) +app.use(middleware.accessLogger) app.use(middleware.responseTime) app.use(middleware.errorHandler) app.use(cors())