feat(api): improve logging with loki and grafana

This commit is contained in:
Lars Hampe 2024-11-12 21:08:40 +01:00
parent 33cfe91461
commit f284f74734
5 changed files with 96 additions and 10 deletions

View File

@ -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<typeof UserOutput>
@ -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)

View File

@ -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,
})
}
}
}

BIN
bun.lockb

Binary file not shown.

View File

@ -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"
}
}

View File

@ -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),
}),