diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 7e49d94..cd24aab 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,17 +1,19 @@ import type { UserOutput } from '@boring.tools/schema' -import { sentry } from '@hono/sentry' +// import { sentry } from '@hono/sentry' import { OpenAPIHono, type z } from '@hono/zod-openapi' import { apiReference } from '@scalar/hono-api-reference' import { cors } from 'hono/cors' +import { requestId } from 'hono/request-id' import changelog from './changelog' -import user from './user' import { accessTokenApi } from './access-token' import pageApi from './page' import statisticApi from './statistic' +import userApi from './user' import { authentication } from './utils/authentication' import { handleError, handleZodError } from './utils/errors' +import { logger } from './utils/logger' import { startup } from './utils/startup' type User = z.infer @@ -31,9 +33,12 @@ export const app = new OpenAPIHono<{ Variables: Variables }>({ // dsn: 'https://1d7428bbab0a305078cf4aa380721aa2@o4508167321354240.ingest.de.sentry.io/4508167323648080', // }), // ) + app.onError(handleError) app.use('*', cors()) app.use('/v1/*', authentication) +app.use('*', requestId()) +app.use(logger()) app.openAPIRegistry.registerComponent('securitySchemes', 'AccessToken', { type: 'http', scheme: 'bearer', @@ -43,7 +48,7 @@ app.openAPIRegistry.registerComponent('securitySchemes', 'Clerk', { scheme: 'bearer', }) -app.route('/v1/user', user) +app.route('/v1/user', userApi) app.route('/v1/changelog', changelog) app.route('/v1/page', pageApi) app.route('/v1/access-token', accessTokenApi) diff --git a/apps/api/src/utils/logger.ts b/apps/api/src/utils/logger.ts new file mode 100644 index 0000000..212df03 --- /dev/null +++ b/apps/api/src/utils/logger.ts @@ -0,0 +1,59 @@ +import { db, user } from '@boring.tools/database' +import { logger as log } from '@boring.tools/logger' +import { eq } from 'drizzle-orm' +import type { MiddlewareHandler } from 'hono' +import { getPath } from 'hono/utils/url' + +export const logger = (): MiddlewareHandler => { + return async function logga(c, next) { + const { method } = c.req + const clerkUser = c.get('clerkAuth') + const requestId = c.get('requestId') + const [dbUser] = await db + .select({ id: user.id, providerId: user.providerId }) + .from(user) + .where(eq(user.providerId, clerkUser?.userId)) + const path = getPath(c.req.raw) + + log.info('Incoming', { + direction: 'in', + method, + path, + userId: dbUser.id, + requestId, + }) + + await next() + + if (c.res.status <= 399) { + log.info('Outgoing', { + direction: 'out', + method, + path, + status: c.res.status, + userId: dbUser.id, + requestId, + }) + } + + if (c.res.status >= 400 && c.res.status < 499) { + log.warn('Outgoing', { + direction: 'out', + method, + path, + status: c.res.status, + requestId, + }) + } + + if (c.res.status >= 500) { + log.error('Outgoing', { + direction: 'out', + method, + path, + status: c.res.status, + requestId, + }) + } + } +} diff --git a/bun.lockb b/bun.lockb index aa813c0..5fb3199 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/logger/package.json b/packages/logger/package.json index ee3e671..84c957e 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -12,6 +12,7 @@ "@logtail/node": "^0.5.0", "@logtail/winston": "^0.5.0", "winston": "^3.14.2", + "winston-console-format": "^1.0.8", "winston-loki": "^6.1.3" } } \ No newline at end of file diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 15fb249..09f774c 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,20 +1,41 @@ import { Logtail } from '@logtail/node' import { LogtailTransport } from '@logtail/winston' -import winston from 'winston' +import { createLogger, format, transports } from 'winston' +import { consoleFormat } from 'winston-console-format' import LokiTransport from 'winston-loki' // Create a Winston logger - passing in the Logtail transport -export const logger = winston.createLogger({ - format: winston.format.json(), +export const logger = createLogger({ + format: format.combine( + format.timestamp(), + format.ms(), + format.errors({ stack: true }), + format.splat(), + format.json(), + ), transports: [ - new winston.transports.Console({ - format: winston.format.json(), + new transports.Console({ + format: format.combine( + format.colorize({ all: true }), + format.padLevels(), + consoleFormat({ + showMeta: true, + metaStrip: ['timestamp', 'service'], + inspectOptions: { + depth: Number.POSITIVE_INFINITY, + colors: true, + maxArrayLength: Number.POSITIVE_INFINITY, + breakLength: 120, + compact: Number.POSITIVE_INFINITY, + }, + }), + ), }), new LokiTransport({ host: 'http://localhost:9100', - labels: { app: 'api' }, json: true, - format: winston.format.json(), + labels: { service: 'api' }, + format: format.json(), replaceTimestamp: true, onConnectionError: (err) => console.error(err), }),