-
Notifications
You must be signed in to change notification settings - Fork 1
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
feat: better logging #62
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Injectable, Logger, LoggerService } from "@nestjs/common"; | ||
import { Logger as DrizzleLoggerInterface } from "drizzle-orm"; | ||
|
||
@Injectable() | ||
export class DrizzleLogger implements DrizzleLoggerInterface { | ||
constructor(private readonly logger: LoggerService) {} | ||
|
||
logQuery(query: string, params: unknown[]): void { | ||
if (query && params) this.logger.log(`Query: ${query} ${params}`); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,28 @@ | ||
import { Inject, Injectable, Scope } from "@nestjs/common"; | ||
import { Inject, Injectable, Logger, Scope } from "@nestjs/common"; | ||
import { CONTEXT } from "@nestjs/graphql"; | ||
import { NodePgDatabase, drizzle } from "drizzle-orm/node-postgres"; | ||
import { Pool } from "pg"; | ||
import { DrizzleLogger } from "./drizzle.logger"; | ||
import { CONNECTION_POOL } from "./drizzle.module-definition"; | ||
import * as schema from "./schema"; | ||
import { DRIZZLE_DATABASE_KEY } from "./transaction.interceptor"; | ||
|
||
@Injectable({ scope: Scope.REQUEST }) | ||
export class DrizzleService { | ||
private readonly logger = new Logger("Drizzle:Service"); | ||
|
||
constructor( | ||
@Inject(CONNECTION_POOL) private readonly pool: Pool, | ||
@Inject(CONTEXT) private readonly context: any, | ||
) {} | ||
|
||
public get db(): NodePgDatabase<typeof schema> { | ||
return ( | ||
this.context[DRIZZLE_DATABASE_KEY] ?? drizzle(this.pool, { schema }) | ||
this.context[DRIZZLE_DATABASE_KEY] ?? | ||
drizzle(this.pool, { | ||
schema, | ||
logger: new DrizzleLogger(this.logger), | ||
}) | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,61 @@ | ||||||
import { | ||||||
ApolloServerPlugin, | ||||||
BaseContext, | ||||||
GraphQLRequestContext, | ||||||
GraphQLRequestContextWillSendResponse, | ||||||
GraphQLRequestListener, | ||||||
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. Unused import
Suggested change
|
||||||
} from "@apollo/server"; | ||||||
import { Plugin } from "@nestjs/apollo"; | ||||||
import { Logger } from "@nestjs/common"; | ||||||
import { performance } from "perf_hooks"; | ||||||
|
||||||
@Plugin() | ||||||
export class ApolloLogger implements ApolloServerPlugin { | ||||||
private readonly logger = new Logger("GraphQL"); | ||||||
|
||||||
async requestDidStart(requestContext: GraphQLRequestContext<BaseContext>) { | ||||||
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. Mark full access specifier as the
Suggested change
|
||||||
const thatLogger = this.logger; | ||||||
if (requestContext.request.operationName === "IntrospectionQuery") { | ||||||
return; | ||||||
} | ||||||
|
||||||
requestContext.request.http?.headers.set( | ||||||
"x-request-id", | ||||||
crypto.randomUUID(), | ||||||
); | ||||||
if (!requestContext.request.http?.headers.get("x-session-id")) { | ||||||
requestContext.request.http?.headers.set( | ||||||
"x-session-id", | ||||||
crypto.randomUUID(), | ||||||
); | ||||||
} | ||||||
const now = performance.now(); | ||||||
return { | ||||||
async willSendResponse( | ||||||
responseContext: GraphQLRequestContextWillSendResponse<BaseContext>, | ||||||
) { | ||||||
if ( | ||||||
requestContext.request.operationName === | ||||||
"IntrospectionQuery" | ||||||
) { | ||||||
return; | ||||||
} | ||||||
thatLogger.log({ | ||||||
sessionId: | ||||||
requestContext.request.http?.headers.get( | ||||||
"x-session-id", | ||||||
), | ||||||
requestId: | ||||||
requestContext.request.http?.headers.get( | ||||||
"x-request-id", | ||||||
), | ||||||
query: requestContext.request.query, | ||||||
variables: requestContext.request.variables, | ||||||
resposneTime: performance.now() - now, | ||||||
responseDate: new Date().toISOString(), | ||||||
error: responseContext.errors, | ||||||
}); | ||||||
}, | ||||||
}; | ||||||
} | ||||||
} |
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. This is not the place of configs. A separate config module is required. 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. @bassiounix This is a feature request, we can create a separate issue for this. The intention of this PR is to add logging, not create a config module. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import * as winston from "winston"; | ||
import "winston-daily-rotate-file"; | ||
import { utilities as nestWinstonModuleUtilities } from "nest-winston"; | ||
|
||
const dbRotateTransport = new winston.transports.DailyRotateFile({ | ||
filename: "logs/db-%DATE%.log", | ||
datePattern: "YYYY-MM-DD", | ||
zippedArchive: true, | ||
maxSize: "20m", | ||
maxFiles: "14d", | ||
format: winston.format.combine( | ||
winston.format((info) => { | ||
if ( | ||
info.context === "Drizzle:Service" || | ||
info.context === "Drizzle:TransactionInterceptor" | ||
) | ||
return info; | ||
else return false; | ||
})(), | ||
winston.format.json(), | ||
), | ||
}); | ||
|
||
const httpRotateTransport = new winston.transports.DailyRotateFile({ | ||
filename: "logs/graphql-%DATE%.log", | ||
datePattern: "YYYY-MM-DD", | ||
zippedArchive: true, | ||
maxSize: "20m", | ||
maxFiles: "14d", | ||
format: winston.format.combine( | ||
winston.format((info) => { | ||
if (info.context === "GraphQL") return info; | ||
else return false; | ||
})(), | ||
winston.format.json(), | ||
), | ||
}); | ||
|
||
const errorRotateTransport = new winston.transports.DailyRotateFile({ | ||
filename: "logs/error-%DATE%.log", | ||
datePattern: "YYYY-MM-DD", | ||
zippedArchive: true, | ||
maxSize: "20m", | ||
maxFiles: "14d", | ||
level: "error", | ||
format: winston.format.combine( | ||
winston.format.timestamp(), | ||
winston.format.json(), | ||
), | ||
}); | ||
|
||
export const winstonConfig = { | ||
level: process.env.LOG_LEVEL || "info", | ||
format: winston.format.combine( | ||
winston.format.timestamp(), | ||
winston.format.json(), | ||
), | ||
handleExceptions: true, | ||
handleRejections: true, | ||
exitOnError: false, | ||
transports: [ | ||
errorRotateTransport, | ||
dbRotateTransport, | ||
httpRotateTransport, | ||
new winston.transports.Console({ | ||
format: winston.format.combine( | ||
nestWinstonModuleUtilities.format.nestLike("NestWinston", { | ||
colors: true, | ||
prettyPrint: true, | ||
}), | ||
), | ||
}), | ||
], | ||
}; | ||
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.
Why the db is not injected?
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.
Ya 3ammena, separate issue wallah 😭 See #2