feat: add more api logs (#1870)

Adds more detailed API logging using Pino
This commit is contained in:
David Nguyen
2025-06-30 19:46:32 +10:00
committed by GitHub
parent 0cc729e9bd
commit 7487399123
74 changed files with 1395 additions and 544 deletions

View File

@ -4,7 +4,9 @@ import type { AnyZodObject } from 'zod';
import { AppError, genericErrorCodeToTrpcErrorCodeMap } from '@documenso/lib/errors/app-error';
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
import type { TrpcApiLog } from '@documenso/lib/types/api-logs';
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
import { alphaid } from '@documenso/lib/universal/id';
import { isAdmin } from '@documenso/lib/utils/is-admin';
import type { TrpcContext } from './context';
@ -66,11 +68,13 @@ const t = initTRPC
* Middlewares
*/
export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => {
const logger = ctx.logger.child({
const infoToLog: TrpcApiLog = {
path,
auth: ctx.metadata.auth,
source: ctx.metadata.source,
});
trpcMiddleware: 'authenticated',
unverifiedTeamId: ctx.teamId,
};
const authorizationHeader = ctx.req.headers.get('authorization');
@ -85,10 +89,11 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path })
const apiToken = await getApiTokenByToken({ token });
logger.info({
ctx.logger.info({
...infoToLog,
userId: apiToken.user.id,
apiTokenId: apiToken.id,
});
} satisfies TrpcApiLog);
return await next({
ctx: {
@ -122,14 +127,21 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path })
});
}
logger.info({
// Recreate the logger with a sub request ID to differentiate between batched requests.
const trpcSessionLogger = ctx.logger.child({
nonBatchedRequestId: alphaid(),
});
trpcSessionLogger.info({
...infoToLog,
userId: ctx.user.id,
apiTokenId: null,
});
} satisfies TrpcApiLog);
return await next({
ctx: {
...ctx,
logger: trpcSessionLogger,
user: ctx.user,
session: ctx.session,
metadata: {
@ -145,10 +157,26 @@ export const authenticatedMiddleware = t.middleware(async ({ ctx, next, path })
});
});
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) => {
export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next, path }) => {
// Recreate the logger with a sub request ID to differentiate between batched requests.
const trpcSessionLogger = ctx.logger.child({
nonBatchedRequestId: alphaid(),
});
ctx.logger.info({
path,
auth: ctx.metadata.auth,
source: ctx.metadata.source,
userId: ctx.user?.id,
apiTokenId: null,
trpcMiddleware: 'maybeAuthenticated',
unverifiedTeamId: ctx.teamId,
} satisfies TrpcApiLog);
return await next({
ctx: {
...ctx,
logger: trpcSessionLogger,
user: ctx.user,
session: ctx.session,
metadata: {
@ -166,7 +194,7 @@ export const maybeAuthenticatedMiddleware = t.middleware(async ({ ctx, next }) =
});
});
export const adminMiddleware = t.middleware(async ({ ctx, next }) => {
export const adminMiddleware = t.middleware(async ({ ctx, next, path }) => {
if (!ctx.session || !ctx.user) {
throw new TRPCError({
code: 'UNAUTHORIZED',
@ -183,9 +211,24 @@ export const adminMiddleware = t.middleware(async ({ ctx, next }) => {
});
}
// Recreate the logger with a sub request ID to differentiate between batched requests.
const trpcSessionLogger = ctx.logger.child({
nonBatchedRequestId: alphaid(),
});
trpcSessionLogger.info({
path,
auth: ctx.metadata.auth,
source: ctx.metadata.source,
userId: ctx.user.id,
apiTokenId: null,
trpcMiddleware: 'admin',
} satisfies TrpcApiLog);
return await next({
ctx: {
...ctx,
logger: trpcSessionLogger,
user: ctx.user,
session: ctx.session,
metadata: {
@ -201,11 +244,34 @@ export const adminMiddleware = t.middleware(async ({ ctx, next }) => {
});
});
export const procedureMiddleware = t.middleware(async ({ ctx, next, path }) => {
// Recreate the logger with a sub request ID to differentiate between batched requests.
const trpcSessionLogger = ctx.logger.child({
nonBatchedRequestId: alphaid(),
});
trpcSessionLogger.info({
path,
auth: ctx.metadata.auth,
source: ctx.metadata.source,
userId: ctx.user?.id,
apiTokenId: null,
trpcMiddleware: 'procedure',
} satisfies TrpcApiLog);
return await next({
ctx: {
...ctx,
logger: trpcSessionLogger,
},
});
});
/**
* Routers and Procedures
*/
export const router = t.router;
export const procedure = t.procedure;
export const procedure = t.procedure.use(procedureMiddleware);
export const authenticatedProcedure = t.procedure.use(authenticatedMiddleware);
// While this is functionally the same as `procedure`, it's useful for indicating purpose
export const maybeAuthenticatedProcedure = t.procedure.use(maybeAuthenticatedMiddleware);