diff --git a/lib/app.js b/lib/app.js index 041298ebb..79f5c1f70 100644 --- a/lib/app.js +++ b/lib/app.js @@ -6,6 +6,7 @@ import nodeHttp from 'http'; import nodeHttps from 'https'; import config from '../config/index.js'; +import logger from './services/logger.js'; import express from './services/express.js'; import mongooseService from './services/mongoose.js'; import migrations from './services/migrations.js'; @@ -94,26 +95,25 @@ const logConfiguration = async () => { // Create server URL const server = `${(config.secure && config.secure.credentials ? 'https://' : 'http://') + config.api.host}:${config.api.port}`; // Logging initialization - console.log(chalk.green(config.app.title)); - console.log(); - console.log(chalk.green(`Environment: ${process.env.NODE_ENV ? process.env.NODE_ENV : 'develoment'}`)); - console.log(chalk.green(`Server: ${server}`)); - console.log(chalk.green(`Database: ${config.db.uri}`)); - if (config.cors.origin.length > 0) console.log(chalk.green(`Cors: ${config.cors.origin}`)); + logger.info(chalk.green(config.app.title)); + logger.info(chalk.green(`Environment: ${process.env.NODE_ENV ? process.env.NODE_ENV : 'development'}`)); + logger.info(chalk.green(`Server: ${server}`)); + const safeUri = config.db.uri.replace(/\/\/[^@]+@/, '//***:***@'); + logger.info(chalk.green(`Database: ${safeUri}`)); + if (config.cors.origin.length > 0) logger.info(chalk.green(`Cors: ${config.cors.origin}`)); // SaaS readiness summary (skip in test to keep output clean) if (process.env.NODE_ENV !== 'test') { try { const { default: HomeService } = await import('../modules/home/services/home.service.js'); const checks = HomeService.getReadinessStatus(); - console.log(); - console.log(chalk.green('SaaS Readiness:')); + logger.info(chalk.green('SaaS Readiness:')); checks.forEach((c) => { const icon = c.status === 'ok' ? chalk.green('OK') : chalk.yellow('WARN'); - console.log(` ${icon} ${c.category.padEnd(12)} ${c.message}`); + logger.info(` ${icon} ${c.category.padEnd(12)} ${c.message}`); }); } catch (err) { - console.log(chalk.yellow(` SaaS readiness check failed: ${err.message}`)); + logger.warn(chalk.yellow(` SaaS readiness check failed: ${err.message}`), err); } } }; @@ -160,7 +160,7 @@ const FORCE_SHUTDOWN_TIMEOUT_MS = 5000; const shutdown = async (server) => { // Force exit if graceful shutdown hangs const forceTimeout = setTimeout(() => { - console.error(chalk.red('Forced shutdown (timeout)')); + logger.error(chalk.red('Forced shutdown (timeout)')); process.exit(1); }, FORCE_SHUTDOWN_TIMEOUT_MS); forceTimeout.unref(); @@ -171,15 +171,16 @@ const shutdown = async (server) => { await SentryService.shutdown(); await mongooseService.disconnect(); value.http.close((err) => { - console.info(chalk.yellow('Server closed')); if (err) { - console.info(chalk.red('Error on server close.', err)); + logger.error(chalk.red('Error on server close.'), err); process.exitCode = 1; + } else { + logger.info(chalk.yellow('Server closed')); } process.exit(); }); } catch (err) { - console.error(chalk.red('Shutdown error: server never started or shutdown failed'), err); + logger.error(chalk.red('Shutdown error: server never started or shutdown failed'), err); process.exit(1); } }; diff --git a/lib/helpers/mailer/index.js b/lib/helpers/mailer/index.js index 60aca596c..41985c32e 100644 --- a/lib/helpers/mailer/index.js +++ b/lib/helpers/mailer/index.js @@ -2,6 +2,7 @@ import path from 'path'; import handlebars from 'handlebars'; import config from '../../../config/index.js'; +import logger from '../../services/logger.js'; import files from '../files.js'; import NodemailerProvider from './provider.nodemailer.js'; import ResendProvider from './provider.resend.js'; @@ -93,7 +94,7 @@ const sendMail = async (mail) => { if (!Array.isArray(result?.accepted)) return { ...result, accepted: [mail.to], rejected: [] }; return result; } catch (err) { - console.error(`Mail send error: ${err.message}`); + logger.error('Mail send error', err); return null; } }; diff --git a/lib/helpers/mailer/tests/mailer.unit.tests.js b/lib/helpers/mailer/tests/mailer.unit.tests.js index faf000344..0b5225ca0 100644 --- a/lib/helpers/mailer/tests/mailer.unit.tests.js +++ b/lib/helpers/mailer/tests/mailer.unit.tests.js @@ -29,6 +29,15 @@ jest.unstable_mockModule('../provider.nodemailer.js', () => ({ default: jest.fn().mockImplementation(() => ({ send: jest.fn() })), })); +jest.unstable_mockModule('../../../services/logger.js', () => ({ + default: { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }, +})); + const { default: mailer } = await import('../index.js'); describe('mailer index with resend provider unit tests:', () => { diff --git a/lib/services/express.js b/lib/services/express.js index b76b94431..a22cd752f 100644 --- a/lib/services/express.js +++ b/lib/services/express.js @@ -117,12 +117,12 @@ const initPreParserRoutes = async (app) => { try { const route = await import(path.resolve(routePath)); if (typeof route.default !== 'function') { - console.warn(`Pre-parser route ${routePath} does not export a default function`); + logger.warn(`Pre-parser route ${routePath} does not export a default function`); continue; } route.default(app); } catch (err) { - console.error(`Failed to load pre-parser route: ${routePath}`, err); + logger.error(`Failed to load pre-parser route: ${routePath}`, err); throw err; } } @@ -237,7 +237,7 @@ const initModulesServerRoutes = async (app) => { const initErrorRoutes = (app) => { app.use((err, req, res, next) => { if (!err) return next(); - console.error(err.stack); + logger.error(err.stack); res.status(err.status || 500).send({ message: err.message, code: err.code, diff --git a/lib/services/migrations.js b/lib/services/migrations.js index deb046212..6d70748fe 100644 --- a/lib/services/migrations.js +++ b/lib/services/migrations.js @@ -5,6 +5,7 @@ import chalk from 'chalk'; import mongoose from 'mongoose'; import path from 'path'; import { glob } from 'glob'; +import logger from './logger.js'; /** * Scan all modules for migration files matching `modules/*/migrations/*.js`. @@ -89,7 +90,7 @@ const runMigration = async (filePath, executed) => { // Atomically claim the migration to prevent concurrent execution const claimed = await claimMigration(name); if (!claimed) { - console.log(chalk.yellow(` Migration already claimed by another runner: ${name}`)); + logger.warn(chalk.yellow(` Migration already claimed by another runner: ${name}`)); return false; } @@ -115,7 +116,7 @@ const runMigration = async (filePath, executed) => { throw err; } - console.log(chalk.green(` Migration executed: ${name}`)); + logger.info(chalk.green(` Migration executed: ${name}`)); return true; }; @@ -133,30 +134,29 @@ const run = async () => { const files = await discoverMigrationFiles(); if (files.length === 0) { - console.log(chalk.yellow('No migration files found.')); + logger.warn(chalk.yellow('No migration files found.')); return { total: 0, executed: 0 }; } const executed = await getExecutedMigrations(); let executedCount = 0; - console.log(chalk.yellow(`Running migrations (${files.length} found, ${executed.size} already executed)...`)); + logger.info(chalk.yellow(`Running migrations (${files.length} found, ${executed.size} already executed)...`)); for (const filePath of files) { try { const wasRun = await runMigration(filePath, executed); if (wasRun) executedCount++; } catch (err) { - console.error(chalk.red(`Migration failed: ${path.basename(filePath)}`)); - console.error(chalk.red(err.message)); + logger.error(chalk.red(`Migration failed: ${path.basename(filePath)}`), err); throw err; } } if (executedCount > 0) { - console.log(chalk.green(`Migrations complete: ${executedCount} executed.`)); + logger.info(chalk.green(`Migrations complete: ${executedCount} executed.`)); } else { - console.log(chalk.yellow('All migrations already up to date.')); + logger.info(chalk.yellow('All migrations already up to date.')); } return { total: files.length, executed: executedCount }; diff --git a/lib/services/mongoose.js b/lib/services/mongoose.js index b0beb5b61..c1884a89a 100644 --- a/lib/services/mongoose.js +++ b/lib/services/mongoose.js @@ -6,6 +6,7 @@ import chalk from 'chalk'; import mongoose from 'mongoose'; import path from 'path'; import config from '../../config/index.js'; +import logger from './logger.js'; /** * Load all mongoose related models @@ -38,13 +39,13 @@ const connect = async () => { await mongoose.connect(config.db.uri, mongoOptions); mongoose.set('debug', config.db.debug); - console.info(chalk.yellow('Connected to MongoDB.')); + logger.info(chalk.yellow('Connected to MongoDB.')); return mongoose; } catch (err) { // Log Error - console.error(chalk.red('Could not connect to MongoDB!')); - console.log(err); + logger.error(chalk.red('Could not connect to MongoDB!')); + logger.error(err); throw err; } }; @@ -54,7 +55,7 @@ const connect = async () => { */ const disconnect = async () => { await mongoose.disconnect(); - console.info(chalk.yellow('Disconnected from MongoDB.')); + logger.info(chalk.yellow('Disconnected from MongoDB.')); }; export default { diff --git a/lib/services/seed.js b/lib/services/seed.js index 54be2283d..c2af82d3c 100644 --- a/lib/services/seed.js +++ b/lib/services/seed.js @@ -5,6 +5,7 @@ import _ from 'lodash'; import chalk from 'chalk'; import config from '../../config/index.js'; +import logger from './logger.js'; import AppError from '../helpers/AppError.js'; @@ -19,10 +20,10 @@ const seedTheUser = (UserService, user) => async (password) => { if (process.env.NODE_ENV === 'test' && (await UserService.get(user))) UserService.remove(user); try { const result = await UserService.create(user); - if (seedOptions.logResults) console.log(chalk.bold.blue(`Database Seeding: Local ${user.email} added with password set to ${password}`)); + if (seedOptions.logResults) logger.info(chalk.bold.blue(`Database Seeding: Local ${user.email} added with password set to ***`)); return result; } catch (err) { - console.log(err); + logger.error(err); throw new AppError('Failed to seedTheUser.', { code: 'LIB_ERROR' }); } }; @@ -31,10 +32,10 @@ const seedTheUser = (UserService, user) => async (password) => { const seedTasks = async (TaskService, task, user) => { try { const result = await TaskService.create(task, user); - if (seedOptions.logResults) console.log(chalk.bold.blue(`Database Seeding: Local ${task.title} added`)); + if (seedOptions.logResults) logger.info(chalk.bold.blue(`Database Seeding: Local ${task.title} added`)); return result; } catch (err) { - console.log(err); + logger.error(err); throw new AppError('Failed to seedTasks.', { code: 'LIB_ERROR' }); } }; @@ -66,7 +67,7 @@ const start = async (options, UserService, AuthService, TaskService) => { } } } catch (err) { - console.log(err); + logger.error(err); return new AppError('Error on seed start.'); } @@ -84,7 +85,7 @@ const user = async (user, UserService, AuthService) => { pwd = await AuthService.generateRandomPassphrase(); result.push(await seedTheUser(UserService, user)(pwd)); } catch (err) { - console.log(err); + logger.error(err); return new AppError('Error on seed start.'); } diff --git a/lib/services/sentry.js b/lib/services/sentry.js index 28a55cebf..b1b542c86 100644 --- a/lib/services/sentry.js +++ b/lib/services/sentry.js @@ -2,6 +2,7 @@ * Module dependencies */ import config from '../../config/index.js'; +import logger from './logger.js'; /** * Sentry client reference (null when not configured). @@ -32,7 +33,7 @@ const init = async () => { tracesSampleRate: process.env.NODE_ENV === 'production' ? 0.1 : 1.0, }); } catch (err) { - console.error('Sentry init failed:', err.message); + logger.error('Sentry init failed:', err); Sentry = null; } }; diff --git a/lib/services/tests/sentry.unit.tests.js b/lib/services/tests/sentry.unit.tests.js index 0989a0928..1162608d6 100644 --- a/lib/services/tests/sentry.unit.tests.js +++ b/lib/services/tests/sentry.unit.tests.js @@ -10,6 +10,15 @@ jest.unstable_mockModule('../../../config/index.js', () => ({ }, })); +jest.unstable_mockModule('../logger.js', () => ({ + default: { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }, +})); + describe('SentryService unit tests:', () => { let SentryService; diff --git a/modules/audit/audit.init.js b/modules/audit/audit.init.js index 4385c9f13..9c9ada0e8 100644 --- a/modules/audit/audit.init.js +++ b/modules/audit/audit.init.js @@ -2,6 +2,7 @@ * Module dependencies */ import config from '../../config/index.js'; +import logger from '../../lib/services/logger.js'; import auditMiddleware from './middlewares/audit.middleware.js'; /** @@ -22,6 +23,6 @@ export default async (app) => { if (process.env.NODE_ENV !== 'test') { const enabled = config.audit?.enabled ?? false; - console.log(`Audit module: ${enabled ? 'enabled' : 'disabled'}`); + logger.info(`Audit module: ${enabled ? 'enabled' : 'disabled'}`); } }; diff --git a/modules/auth/tests/auth.abilities.integration.tests.js b/modules/auth/tests/auth.abilities.integration.tests.js index b11e62c33..62bc880fb 100644 --- a/modules/auth/tests/auth.abilities.integration.tests.js +++ b/modules/auth/tests/auth.abilities.integration.tests.js @@ -42,6 +42,14 @@ describe('Auth abilities integration tests:', () => { agent = request.agent(init.app); adminAgent = request.agent(init.app); + // clean up stale users from previous runs on shared databases + for (const email of [userCredentials.email, adminCredentials.email]) { + try { + const existing = await UserService.getBrut({ email }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } + } + // Create a regular user const userRes = await agent .post('/api/auth/signup') diff --git a/modules/auth/tests/auth.authorization.integration.tests.js b/modules/auth/tests/auth.authorization.integration.tests.js index 7e7161b94..c9758666b 100644 --- a/modules/auth/tests/auth.authorization.integration.tests.js +++ b/modules/auth/tests/auth.authorization.integration.tests.js @@ -40,6 +40,14 @@ describe('Authorization integration tests:', () => { adminAgent = request.agent(init.app); otherAgent = request.agent(init.app); + // clean up stale users from previous runs on shared databases + for (const email of ['auth-test-user@test.com', 'auth-test-admin@test.com', 'auth-test-other@test.com']) { + try { + const existing = await UserService.getBrut({ email }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } + } + // Create a normal user via agent const userRes = await agent .post('/api/auth/signup') diff --git a/modules/auth/tests/auth.integration.tests.js b/modules/auth/tests/auth.integration.tests.js index bbbec71a1..fdbffafcb 100644 --- a/modules/auth/tests/auth.integration.tests.js +++ b/modules/auth/tests/auth.integration.tests.js @@ -66,6 +66,14 @@ describe('Auth integration tests:', () => { _userEdited.email = credentials[1].email; _userEdited.password = credentials[1].password; + // clean up stale users from previous runs on shared databases + for (const email of [credentials[0].email, credentials[1].email, 'register_new_user_@test.com']) { + try { + const existing = await UserService.getBrut({ email }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } + } + // add user try { const result = await agent.post('/api/auth/signup').send(_user).expect(200); @@ -645,6 +653,11 @@ describe('Auth integration tests:', () => { password: credentials[0].password, provider: 'local', }; + // clean up stale users from previous runs on shared databases + try { + const existing = await UserService.getBrut({ email: credentials[0].email }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } try { const result = await agent.post('/api/auth/signup').send(_user).expect(200); user = result.body.user; @@ -722,6 +735,13 @@ describe('Auth integration tests:', () => { let secUser; beforeEach(async () => { + // clean up stale users from previous runs on shared databases + for (const email of [secEmail, 'cookieflag@test.com']) { + try { + const existing = await UserService.getBrut({ email }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } + } try { const result = await agent.post('/api/auth/signup').send({ firstName: 'Sec', @@ -873,6 +893,11 @@ describe('Auth integration tests:', () => { let verifyUser; beforeEach(async () => { + // clean up stale users from previous runs on shared databases + try { + const existing = await UserService.getBrut({ email: verifyEmail }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } try { const result = await agent.post('/api/auth/signup').send({ firstName: 'Verify', @@ -998,6 +1023,11 @@ describe('Auth integration tests:', () => { let lockUser; beforeEach(async () => { + // clean up stale users from previous runs on shared databases + try { + const existing = await UserService.getBrut({ email: lockEmail }); + if (existing) await UserService.remove(existing); + } catch (_) { /* cleanup – ignore errors */ } try { const result = await agent.post('/api/auth/signup').send({ firstName: 'Lock', diff --git a/modules/auth/tests/auth.signup.organization.integration.tests.js b/modules/auth/tests/auth.signup.organization.integration.tests.js index e83f9fa85..5070be60b 100644 --- a/modules/auth/tests/auth.signup.organization.integration.tests.js +++ b/modules/auth/tests/auth.signup.organization.integration.tests.js @@ -44,6 +44,18 @@ describe('Auth signup organization integration tests:', () => { } catch (_) { /* cleanup — ignore errors */ } }; + /** + * Remove a user by email if they exist (cleanup stale data from previous runs). + * @param {string} email - email address of the user to purge + * @returns {Promise} + */ + const purgeUser = async (email) => { + try { + const existing = await UserService.getBrut({ email }); + if (existing) await cleanupUser(existing); + } catch (_) { /* cleanup – ignore errors */ } + }; + beforeAll(async () => { try { const init = await bootstrap(); @@ -66,6 +78,8 @@ describe('Auth signup organization integration tests:', () => { config.organizations = { enabled: false, autoCreate: true, domainMatching: true }; let user; + // clean up stale users from previous runs on shared databases + await purgeUser('silent-org@test.com'); try { const result = await agent .post('/api/auth/signup') @@ -108,6 +122,9 @@ describe('Auth signup organization integration tests:', () => { // First, create an existing organization with a specific domain let firstUser; let secondUser; + // clean up stale users from previous runs on shared databases + await purgeUser('first@matchdomain.com'); + await purgeUser('second@matchdomain.com'); try { // Sign up first user to create the org with domain const result1 = await agent @@ -162,6 +179,8 @@ describe('Auth signup organization integration tests:', () => { config.organizations = { enabled: true, autoCreate: true, domainMatching: true }; let user; + // clean up stale users from previous runs on shared databases + await purgeUser('solo@uniquedomain123.com'); try { const result = await agent .post('/api/auth/signup') @@ -200,6 +219,8 @@ describe('Auth signup organization integration tests:', () => { config.organizations = { enabled: true, autoCreate: true, domainMatching: false }; let user; + // clean up stale users from previous runs on shared databases + await purgeUser('personal@somecompany.com'); try { const result = await agent .post('/api/auth/signup') @@ -238,6 +259,8 @@ describe('Auth signup organization integration tests:', () => { config.organizations = { enabled: true, autoCreate: false, domainMatching: true }; let user; + // clean up stale users from previous runs on shared databases + await purgeUser('manual@setup.com'); try { const result = await agent .post('/api/auth/signup') @@ -273,6 +296,8 @@ describe('Auth signup organization integration tests:', () => { config.organizations = { enabled: false, autoCreate: true, domainMatching: true }; let user; + // clean up stale users from previous runs on shared databases + await purgeUser('response-check@test.com'); try { const result = await agent .post('/api/auth/signup') diff --git a/modules/billing/controllers/billing.webhook.controller.js b/modules/billing/controllers/billing.webhook.controller.js index ab2653a27..083bc7bf5 100644 --- a/modules/billing/controllers/billing.webhook.controller.js +++ b/modules/billing/controllers/billing.webhook.controller.js @@ -2,6 +2,7 @@ * Module dependencies */ import config from '../../../config/index.js'; +import logger from '../../../lib/services/logger.js'; import getStripe from '../lib/stripe.js'; import BillingWebhookService from '../services/billing.webhook.service.js'; @@ -44,7 +45,7 @@ const handleWebhook = async (req, res) => { } return res.status(200).json({ received: true }); } catch (err) { - console.error('Stripe webhook handler error:', err); + logger.error('Stripe webhook handler error:', err); return res.status(500).json({ error: 'Webhook handler failed' }); } }; diff --git a/modules/billing/tests/billing.controller.unit.tests.js b/modules/billing/tests/billing.controller.unit.tests.js index b8380db65..ffa980397 100644 --- a/modules/billing/tests/billing.controller.unit.tests.js +++ b/modules/billing/tests/billing.controller.unit.tests.js @@ -36,6 +36,15 @@ describe('Billing webhook controller unit tests:', () => { default: jest.fn(() => mockStripeInstance), })); + jest.unstable_mockModule('../../../lib/services/logger.js', () => ({ + default: { + error: jest.fn(), + warn: jest.fn(), + info: jest.fn(), + debug: jest.fn(), + }, + })); + res = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), @@ -183,13 +192,12 @@ describe('Billing webhook controller unit tests:', () => { const mod = await import('../controllers/billing.webhook.controller.js'); BillingWebhookController = mod.default; - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const loggerMod = await import('../../../lib/services/logger.js'); const req = { headers: { 'stripe-signature': 'sig_ok' }, body: 'raw' }; await BillingWebhookController.handleWebhook(req, res); - expect(consoleSpy).toHaveBeenCalledWith('Stripe webhook handler error:', expect.any(Error)); + expect(loggerMod.default.error).toHaveBeenCalledWith('Stripe webhook handler error:', expect.any(Error)); expect(res.status).toHaveBeenCalledWith(500); expect(res.json).toHaveBeenCalledWith({ error: 'Webhook handler failed' }); - consoleSpy.mockRestore(); }); }); diff --git a/modules/core/tests/core.integration.tests.js b/modules/core/tests/core.integration.tests.js index cfe87310b..bd60baeea 100644 --- a/modules/core/tests/core.integration.tests.js +++ b/modules/core/tests/core.integration.tests.js @@ -5,6 +5,7 @@ import path from 'path'; import { jest } from '@jest/globals'; import config from '../../../config/index.js'; +import logger from '../../../lib/services/logger.js'; import mongooseService from '../../../lib/services/mongoose.js'; import seed from '../../../lib/services/seed.js'; @@ -260,13 +261,13 @@ describe('Core integration tests:', () => { describe('Seed service', () => { it('should log results when logResults option is enabled', async () => { - const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + const loggerSpy = jest.spyOn(logger, 'info').mockImplementation(() => {}); const result = await seed.start({ logResults: true }, UserService, AuthService, TaskService); - expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Database Seeding')); + expect(loggerSpy).toHaveBeenCalledWith(expect.stringContaining('Database Seeding')); expect(result).toBeInstanceOf(Array); - consoleSpy.mockRestore(); + loggerSpy.mockRestore(); }); it('should seed a single user via seed.user()', async () => { diff --git a/server.js b/server.js index f9c524d01..d0d9697ae 100644 --- a/server.js +++ b/server.js @@ -6,19 +6,20 @@ import chalk from 'chalk'; import path from 'path'; const app = await import(path.resolve('./lib/app.js')); +const { default: logger } = await import(path.resolve('./lib/services/logger.js')); const server = app.start().catch((e) => { - console.log(chalk.red(`Server failed`)); - console.log(JSON.stringify(e.message)); + logger.error(chalk.red(`Server failed`)); + logger.error(e.message, e); throw e; }); process.on('SIGINT', () => { - console.info(chalk.blue(' SIGINT Graceful shutdown ', new Date().toISOString())); + logger.info(chalk.blue(' SIGINT Graceful shutdown ', new Date().toISOString())); app.shutdown(server); }); process.on('SIGTERM', () => { - console.info(chalk.blue(' SIGTERM Graceful shutdown ', new Date().toISOString())); + logger.info(chalk.blue(' SIGTERM Graceful shutdown ', new Date().toISOString())); app.shutdown(server); });