diff --git a/package-lock.json b/package-lock.json index 000e977d..51b2896a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,7 +60,8 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "patch-package": "^7.0.0", - "pino": "^8.14.1", + "pino": "^8.21.0", + "pino-pretty": "^13.1.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.8.1", @@ -104,7 +105,7 @@ "typescript": "^5.9.3" }, "engines": { - "node": "20.x", + "node": "22.x", "npm": "10.x" } }, @@ -7431,6 +7432,15 @@ "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", "license": "MIT" }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -8727,9 +8737,9 @@ "license": "MIT" }, "node_modules/express/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -8781,6 +8791,12 @@ "node": ">=4" } }, + "node_modules/fast-copy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz", + "integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -10008,6 +10024,12 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -11764,6 +11786,15 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/jpeg-js": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz", @@ -15067,6 +15098,60 @@ "process-warning": "^3.0.0" } }, + "node_modules/pino-pretty": { + "version": "13.1.3", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz", + "integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^4.0.0", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^3.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-pretty/node_modules/pino-abstract-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz", + "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty/node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/pino-pretty/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pino-std-serializers": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", @@ -16350,6 +16435,22 @@ "dev": true, "license": "MIT" }, + "node_modules/secure-json-parse": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/seek-bzip": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", diff --git a/package.json b/package.json index 226cb14b..2b2663df 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "private": true, "license": "UNLICENSED", "engines": { - "node": "20.x", + "node": "22.x", "npm": "10.x" }, "scripts": { @@ -78,7 +78,8 @@ "passport-jwt": "^4.0.1", "passport-local": "^1.0.0", "patch-package": "^7.0.0", - "pino": "^8.14.1", + "pino": "^8.21.0", + "pino-pretty": "^13.1.3", "reflect-metadata": "^0.1.13", "rimraf": "^3.0.2", "rxjs": "^7.8.1", diff --git a/src/app.module.ts b/src/app.module.ts index dc6ac80b..b8703a27 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -23,9 +23,49 @@ import { OpenGraphModule } from './open-graph/open-graph.module'; import { UserModule } from './user/user.module'; import GraphQLJSON from 'graphql-type-json'; import { DalModule } from './dal/dal.module'; +import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ + LoggerModule.forRootAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => { + const options = { + singleLine: true, + colorize: true, + levelFirst: false, + translateTime: 'yyyy-mm-dd HH:MM:ss', + destination: 1, + }; + return { + pinoHttp: { + transport: { + targets: [ + { + target: 'pino-pretty', + level: 'info', + options, + }, + { + target: 'pino-pretty', + level: 'info', + options: { + ...options, + // app.log file in data path + destination: path.join( + configService.getOrThrow('DATA_PATH'), + 'app.log', + ), + mkdir: true, + }, + }, + ], + }, + }, + }; + }, + }), SentryModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ @@ -56,6 +96,7 @@ import { DalModule } from './dal/dal.module'; envFilePath: ['.env.local', '.env'], validationSchema: Joi.object({ APP_NAME: Joi.string().required(), + DATA_PATH: Joi.string().default(path.join(process.cwd(), 'data')), ACCESS_TOKEN_SECRET: Joi.string().required(), PUBLIC_VAPID_KEY: Joi.optional(), PRIVATE_VAPID_KEY: Joi.optional(), diff --git a/src/main.ts b/src/main.ts index c4e3d32e..0b70afbb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,22 +8,21 @@ import { AppModule } from './app.module'; /* eslint-disable */ import express = require('express'); import { ModerationInterceptor } from './moderation/moderation.interceptor'; -import { LogLevel } from '@nestjs/common'; import { join } from 'path'; import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.js'; import { createProxyMiddleware } from 'http-proxy-middleware'; +import { Logger } from 'nestjs-pino'; async function bootstrap() { const instance = express(); instance.use('/avatars', require('adorable-avatars/dist/index')); - const logLevels: LogLevel[] = process.env.NODE_ENV === 'development' - ? ['log', 'debug', 'error', 'verbose', 'warn'] - : ['error']; + const app: NestExpressApplication = await NestFactory.create( AppModule, - new ExpressAdapter(instance), - { logger: logLevels, } - ); + new ExpressAdapter(instance), { + bufferLogs: true, + }); + app.useLogger(app.get(Logger)); // for MVC server side rendering app.useStaticAssets(join(__dirname, '..', 'public'));