diff --git a/.changeset/wet-stingrays-behave.md b/.changeset/wet-stingrays-behave.md new file mode 100644 index 000000000..55075a742 --- /dev/null +++ b/.changeset/wet-stingrays-behave.md @@ -0,0 +1,6 @@ +--- +"@hyperdx/api": minor +"@hyperdx/app": minor +--- + +migration: migrate to Pino for standardized and faster logging diff --git a/packages/api/package.json b/packages/api/package.json index 02601cdce..6c0b83b92 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -10,7 +10,7 @@ "@ai-sdk/anthropic": "^2.0.23", "@esm2cjs/p-queue": "^7.3.0", "@hyperdx/common-utils": "^0.7.0", - "@hyperdx/node-opentelemetry": "^0.8.2", + "@hyperdx/node-opentelemetry": "^0.9.0", "@hyperdx/passport-local-mongoose": "^9.0.1", "@opentelemetry/api": "^1.8.0", "@opentelemetry/host-metrics": "^0.35.5", @@ -27,7 +27,6 @@ "express": "^4.19.2", "express-rate-limit": "^6.7.1", "express-session": "^1.17.3", - "express-winston": "^4.2.0", "handlebars": "^4.7.8", "http-graceful-shutdown": "^3.1.13", "http-proxy-middleware": "^3.0.5", @@ -40,12 +39,13 @@ "on-headers": "^1.1.0", "passport": "^0.6.0", "passport-local": "^1.0.0", + "pino": "^10.0.0", + "pino-http": "^11.0.0", "promised-handlebars": "^2.0.1", "protobufjs": "^7.5.2", "semver": "^7.5.2", "serialize-error": "^8.1.0", "uuid": "^8.3.2", - "winston": "^3.10.0", "zod": "3.25", "zod-express-middleware": "^1.4.0" }, @@ -67,6 +67,7 @@ "jest": "^28.1.3", "migrate-mongo": "^11.0.0", "nodemon": "^2.0.20", + "pino-pretty": "^13.1.1", "rimraf": "^4.4.1", "supertest": "^6.3.1", "swagger-jsdoc": "^6.2.8", diff --git a/packages/api/src/fixtures.ts b/packages/api/src/fixtures.ts index 7f3365b50..4ac8c2de4 100644 --- a/packages/api/src/fixtures.ts +++ b/packages/api/src/fixtures.ts @@ -63,10 +63,7 @@ const healthCheck = async () => { const client = await getClickhouseClient(); const result = await client.ping(); if (!result.success) { - logger.error({ - message: 'ClickHouse health check failed', - error: result.error, - }); + logger.error({ error: result.error }, 'ClickHouse health check failed'); throw result.error; } }; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 138e885a0..f8c6d93fd 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -21,7 +21,7 @@ if (config.IS_DEV) { const server = new Server(); process.on('uncaughtException', (err: Error) => { - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Uncaught exception'); // FIXME: disable server restart until // we make sure all expected exceptions are handled properly @@ -32,7 +32,9 @@ process.on('uncaughtException', (err: Error) => { process.on('unhandledRejection', (err: any) => { // TODO: do we want to throw here ? - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Unhandled rejection'); }); -server.start().catch(e => logger.error(serializeError(e))); +server + .start() + .catch(e => logger.error({ err: serializeError(e) }, 'Server start failed')); diff --git a/packages/api/src/middleware/auth.ts b/packages/api/src/middleware/auth.ts index e89f54966..618598afc 100644 --- a/packages/api/src/middleware/auth.ts +++ b/packages/api/src/middleware/auth.ts @@ -31,7 +31,8 @@ export function redirectToDashboard(req: Request, res: Response) { return res.redirect(`${config.FRONTEND_URL}/search`); } else { logger.error( - `Password login for user failed, user or team not found ${req?.user?._id}`, + { userId: req?.user?._id }, + 'Password login for user failed, user or team not found', ); res.redirect(`${config.FRONTEND_URL}/login?err=unknown`); } @@ -43,7 +44,7 @@ export function handleAuthError( res: Response, next: NextFunction, ) { - logger.debug({ message: 'Auth error', authErr: serializeError(err) }); + logger.debug({ authErr: serializeError(err) }, 'Auth error'); if (res.headersSent) { return next(err); } diff --git a/packages/api/src/models/index.ts b/packages/api/src/models/index.ts index 6952c7405..d3666d1b5 100644 --- a/packages/api/src/models/index.ts +++ b/packages/api/src/models/index.ts @@ -22,12 +22,12 @@ mongoose.connection.on('disconnected', () => { logger.info('Lost connection to MongoDB server'); }); -mongoose.connection.on('error', () => { - logger.error('Could not connect to MongoDB'); +mongoose.connection.on('error', err => { + logger.error({ err }, 'Could not connect to MongoDB'); }); mongoose.connection.on('reconnected', () => { - logger.error('Reconnected to MongoDB'); + logger.warn('Reconnected to MongoDB'); }); mongoose.connection.on('reconnectFailed', () => { diff --git a/packages/api/src/opamp/controllers/opampController.ts b/packages/api/src/opamp/controllers/opampController.ts index d4ec9a106..b838bd39d 100644 --- a/packages/api/src/opamp/controllers/opampController.ts +++ b/packages/api/src/opamp/controllers/opampController.ts @@ -303,7 +303,7 @@ export class OpampController { // Decode the AgentToServer message const agentToServer = decodeAgentToServer(req.body); - logger.debug('agentToServer', agentToServer); + logger.debug({ agentToServer }, 'agentToServer'); logger.debug( // @ts-ignore `Received message from agent: ${agentToServer.instanceUid?.toString( @@ -353,7 +353,7 @@ export class OpampController { res.setHeader('Content-Type', 'application/x-protobuf'); res.send(encodedResponse); } catch (error) { - logger.error('Error handling OpAMP message:', error); + logger.error({ err: error }, 'Error handling OpAMP message'); res.status(500).send('Internal Server Error'); } } diff --git a/packages/api/src/opamp/services/agentService.ts b/packages/api/src/opamp/services/agentService.ts index 2efb7b81f..69ef840b4 100644 --- a/packages/api/src/opamp/services/agentService.ts +++ b/packages/api/src/opamp/services/agentService.ts @@ -63,7 +63,7 @@ export class AgentService { return agent; } catch (error) { - logger.error('Error processing agent status:', error); + logger.error({ err: error }, 'Error processing agent status'); throw error; } } diff --git a/packages/api/src/opamp/utils/protobuf.ts b/packages/api/src/opamp/utils/protobuf.ts index 85247f1f0..817b43c0a 100644 --- a/packages/api/src/opamp/utils/protobuf.ts +++ b/packages/api/src/opamp/utils/protobuf.ts @@ -21,7 +21,7 @@ try { root = protobuf.loadSync(PROTO_PATH); logger.debug('OpAMP proto definition loaded successfully'); } catch (error) { - logger.error('Failed to load OpAMP proto definition:', error); + logger.error({ err: error }, 'Failed to load OpAMP proto definition'); throw error; } @@ -46,7 +46,7 @@ export function decodeAgentToServer(data: Buffer): protobuf.Message { try { return AgentToServer.decode(data); } catch (error) { - logger.error('Failed to decode AgentToServer message:', error); + logger.error({ err: error }, 'Failed to decode AgentToServer message'); throw error; } } @@ -68,7 +68,7 @@ export function encodeServerToAgent(message: any): Buffer { // Encode the message return Buffer.from(ServerToAgent.encode(serverToAgent).finish()); } catch (error) { - logger.error('Failed to encode ServerToAgent message:', error); + logger.error({ err: error }, 'Failed to encode ServerToAgent message'); throw error; } } @@ -105,7 +105,7 @@ export function createRemoteConfig( configHash: configHash, }; } catch (error) { - logger.error('Failed to create remote config message:', error); + logger.error({ err: error }, 'Failed to create remote config message'); throw error; } } @@ -130,7 +130,7 @@ function calculateConfigHash(configFiles: Map): Buffer { return hash.digest(); } catch (error) { - logger.error('Failed to calculate config hash:', error); + logger.error({ err: error }, 'Failed to calculate config hash'); throw error; } } diff --git a/packages/api/src/routers/api/ai.ts b/packages/api/src/routers/api/ai.ts index 096b110a2..cef6f3a2e 100644 --- a/packages/api/src/routers/api/ai.ts +++ b/packages/api/src/routers/api/ai.ts @@ -188,7 +188,7 @@ router.post( const source = await getSource(teamId.toString(), sourceId); if (source == null) { - logger.error({ message: 'invalid source id', sourceId, teamId }); + logger.error({ sourceId, teamId }, 'invalid source id'); return res.status(400).json({ error: 'Invalid source', }); diff --git a/packages/api/src/routers/api/root.ts b/packages/api/src/routers/api/root.ts index bfbf65268..eb7e569e6 100644 --- a/packages/api/src/routers/api/root.ts +++ b/packages/api/src/routers/api/root.ts @@ -90,7 +90,10 @@ router.post( password, async (err: Error, user: any) => { if (err) { - logger.error(serializeError(err)); + logger.error( + { err: serializeError(err) }, + 'User registration error', + ); return res.status(400).json({ error: 'invalid' }); } @@ -107,7 +110,8 @@ router.post( await setupTeamDefaults(team._id.toString()); } catch (error) { logger.error( - `Failed to setup team defaults: ${serializeError(error)}`, + { err: serializeError(error) }, + 'Failed to setup team defaults', ); // Continue with registration even if setup defaults fails } @@ -118,7 +122,8 @@ router.post( } logger.error( - `Password login for user failed, user or team not found ${req?.user?._id}`, + { userId: req?.user?._id }, + 'Password login for user failed, user or team not found', ); return res.status(400).json({ error: 'invalid' }); }); @@ -167,7 +172,7 @@ router.post('/team/setup/:token', async (req, res, next) => { password, // TODO: validate password async (err: Error, user: any) => { if (err) { - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Team setup error'); return res.redirect( `${config.FRONTEND_URL}/join-team?token=${token}&err=500`, ); @@ -196,7 +201,7 @@ router.get('/ext/silence-alert/:token', async (req, res) => { await silenceAlertByToken(token); } catch (e) { isError = true; - logger.error(e); + logger.error({ err: e }, 'Failed to silence alert'); } // TODO: Create a template for utility pages diff --git a/packages/api/src/server.ts b/packages/api/src/server.ts index e60779ad6..3a967844b 100644 --- a/packages/api/src/server.ts +++ b/packages/api/src/server.ts @@ -31,7 +31,10 @@ export default class Server { if (mongoCloseResult.status === 'rejected') { hasError = true; - logger.error(serializeError(mongoCloseResult.reason)); + logger.error( + { err: serializeError(mongoCloseResult.reason) }, + 'MongoDB client close failed', + ); } else { logger.info('MongoDB client closed.'); } diff --git a/packages/api/src/setupDefaults.ts b/packages/api/src/setupDefaults.ts index 72f5fe27f..859c6a239 100644 --- a/packages/api/src/setupDefaults.ts +++ b/packages/api/src/setupDefaults.ts @@ -36,7 +36,7 @@ export async function setupTeamDefaults(teamId: string) { // Get the team object const team = await getTeam(teamId); if (!team) { - logger.warn(`Team not found with ID: ${teamId}`); + logger.warn({ teamId }, 'Team not found'); return; } @@ -71,7 +71,7 @@ export async function setupTeamDefaults(teamId: string) { `Created default connection: ${connectionConfig.name} (${newConnection._id})`, ); } catch (error) { - logger.error(`Failed to create connection: ${error}`); + logger.error({ err: error }, 'Failed to create connection'); } } } else if (parsedDefaultConnections) { @@ -136,7 +136,7 @@ export async function setupTeamDefaults(teamId: string) { c => c.name === connectionId, ); if (!connection) { - logger.warn(`Connection not found with name: ${connectionId}`); + logger.warn({ connectionId }, 'Connection not found with name'); continue; } connectionId = connection._id.toString(); @@ -166,7 +166,7 @@ export async function setupTeamDefaults(teamId: string) { // Store the created source for the second pass createdSources[sourceConfig.name] = newSource; } catch (error) { - logger.error(`Failed to create source: ${error}`); + logger.error({ err: error }, 'Failed to create source'); } } @@ -227,7 +227,7 @@ export async function setupTeamDefaults(teamId: string) { ); } } catch (error) { - logger.error(`Failed to update source references: ${error}`); + logger.error({ err: error }, 'Failed to update source references'); } } } else if (parsedDefaultSources) { diff --git a/packages/api/src/tasks/checkAlerts.ts b/packages/api/src/tasks/checkAlerts.ts index e28dc4c1a..ccbe486aa 100644 --- a/packages/api/src/tasks/checkAlerts.ts +++ b/packages/api/src/tasks/checkAlerts.ts @@ -92,11 +92,13 @@ const fireChannelEvent = async ({ } if ((alert.silenced?.until?.getTime() ?? 0) > Date.now()) { - logger.info({ - message: 'Skipped firing alert due to silence', - alertId: alert.id, - silenced: alert.silenced, - }); + logger.info( + { + alertId: alert.id, + silenced: alert.silenced, + }, + 'Skipped firing alert due to silence', + ); return; } @@ -157,14 +159,16 @@ export const processAlert = async ( previous && fns.getTime(previous.createdAt) === fns.getTime(nowInMinsRoundDown) ) { - logger.info({ - message: `Skipped to check alert since the time diff is still less than 1 window size`, - windowSizeInMins, - nowInMinsRoundDown, - previous, - now, - alertId: alert.id, - }); + logger.info( + { + windowSizeInMins, + nowInMinsRoundDown, + previous, + now, + alertId: alert.id, + }, + `Skipped to check alert since the time diff is still less than 1 window size`, + ); return; } const checkStartTime = previous @@ -218,20 +222,24 @@ export const processAlert = async ( }; } } else { - logger.error({ - message: `Unsupported alert source: ${alert.source}`, - alertId: alert.id, - }); + logger.error( + { + alertId: alert.id, + }, + `Unsupported alert source: ${alert.source}`, + ); return; } // Fetch data if (chartConfig == null) { - logger.error({ - message: 'Failed to build chart config', - chartConfig, - alertId: alert.id, - }); + logger.error( + { + chartConfig, + alertId: alert.id, + }, + 'Failed to build chart config', + ); return; } @@ -241,13 +249,15 @@ export const processAlert = async ( metadata, }); - logger.info({ - message: `Received alert metric [${alert.source} source]`, - alertId: alert.id, - checksData, - checkStartTime, - checkEndTime, - }); + logger.info( + { + alertId: alert.id, + checksData, + checkStartTime, + checkEndTime, + }, + `Received alert metric [${alert.source} source]`, + ); // TODO: support INSUFFICIENT_DATA state const history: IAlertHistory = { @@ -276,19 +286,23 @@ export const processAlert = async ( ); if (timestampColumnName == null) { - logger.error({ - message: 'Failed to find timestamp column', - meta, - alertId: alert.id, - }); + logger.error( + { + meta, + alertId: alert.id, + }, + 'Failed to find timestamp column', + ); return; } if (valueColumnNames.size === 0) { - logger.error({ - message: 'Failed to find value column', - meta, - alertId: alert.id, - }); + logger.error( + { + meta, + alertId: alert.id, + }, + 'Failed to find value column', + ); return; } diff --git a/packages/api/src/tasks/index.ts b/packages/api/src/tasks/index.ts index 15a1b5d93..8e4675796 100644 --- a/packages/api/src/tasks/index.ts +++ b/packages/api/src/tasks/index.ts @@ -34,11 +34,13 @@ const main = async (argv: TaskArgs) => { `Task [${task.name()}] finished in ${(performance.now() - t0).toFixed(2)} ms`, ); } catch (e: unknown) { - logger.error({ - message: `Task [${task.name()}] failed: ${serializeError(e)}`, - cause: e, - task, - }); + logger.error( + { + cause: e, + task, + }, + `Task [${task.name()}] failed: ${serializeError(e)}`, + ); } finally { await task.asyncDispose(); span.end(); @@ -70,19 +72,19 @@ if (!RUN_SCHEDULED_TASKS_EXTERNALLY) { }) .catch(err => { console.log(err); - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Task execution failed'); process.exit(1); }); } process.on('uncaughtException', (err: Error) => { console.log(err); - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Uncaught exception'); process.exit(1); }); process.on('unhandledRejection', (err: any) => { console.log(err); - logger.error(serializeError(err)); + logger.error({ err: serializeError(err) }, 'Unhandled rejection'); process.exit(1); }); diff --git a/packages/api/src/tasks/providers/index.ts b/packages/api/src/tasks/providers/index.ts index a13416159..3553c4a95 100644 --- a/packages/api/src/tasks/providers/index.ts +++ b/packages/api/src/tasks/providers/index.ts @@ -102,11 +102,13 @@ export async function loadProvider( try { return providerFn(); } catch (err) { - logger.error({ - message: `error creating instance of ${providerName} provider; using default`, - cause: err, - providerName, - }); + logger.error( + { + cause: err, + providerName, + }, + `error creating instance of ${providerName} provider; using default`, + ); } } } diff --git a/packages/api/src/tasks/template.ts b/packages/api/src/tasks/template.ts index 6d99a71c4..c57876be4 100644 --- a/packages/api/src/tasks/template.ts +++ b/packages/api/src/tasks/template.ts @@ -106,15 +106,17 @@ function validateWebhookUrl( // check that hostname ends in "slack.com" if (!isValidSlackUrl(webhook.url)) { const message = `Slack Webhook URL ${webhook.url} does not have hostname that ends in 'slack.com'`; - logger.warn({ - webhook: { - id: webhook._id.toString(), - name: webhook.name, - url: webhook.url, - body: webhook.body, + logger.warn( + { + webhook: { + id: webhook._id.toString(), + name: webhook.name, + url: webhook.url, + body: webhook.body, + }, }, message, - }); + ); throw new Error(`SSRF AllowedDomainError: ${message}`); } } else { @@ -124,15 +126,17 @@ function validateWebhookUrl( const message = `Webhook attempting to query blacklisted route ${blacklistedWebhookHosts.get( url.host, )}`; - logger.warn({ - webhook: { - id: webhook._id.toString(), - name: webhook.name, - url: webhook.url, - body: webhook.body, + logger.warn( + { + webhook: { + id: webhook._id.toString(), + name: webhook.name, + url: webhook.url, + body: webhook.body, + }, }, message, - }); + ); throw new Error(`SSRF AllowedDomainError: ${message}`); } } @@ -200,10 +204,12 @@ export const handleSendGenericWebhook = async ( title: escapeJsonString(message.title), }); } catch (e) { - logger.error({ - message: 'Failed to compile generic webhook body', - error: serializeError(e), - }); + logger.error( + { + error: serializeError(e), + }, + 'Failed to compile generic webhook body', + ); return; } @@ -220,10 +226,12 @@ export const handleSendGenericWebhook = async ( throw new Error(errorText); } } catch (e) { - logger.error({ - message: 'Failed to send generic webhook message', - error: serializeError(e), - }); + logger.error( + { + error: serializeError(e), + }, + 'Failed to send generic webhook message', + ); } }; @@ -347,15 +355,18 @@ const getPopulatedChannel = ( findWebhookByName(channelIdOrNamePrefix, teamWebhooksById); if (!webhook) { - logger.error('webhook not found', { - webhookId: channelIdOrNamePrefix, - }); + logger.error( + { + webhookId: channelIdOrNamePrefix, + }, + 'webhook not found', + ); return undefined; } return { type: 'webhook', channel: webhook }; } default: { - logger.error(`unsupported alert channel type: ${channelType}`); + logger.error({ channelType }, 'Unsupported alert channel type'); return undefined; } } @@ -505,12 +516,14 @@ export const renderAlertTemplate = async ({ 2500, ); } catch (e) { - logger.error({ - message: 'Failed to fetch sample logs', - savedSearchId: savedSearch.id, - chartConfig, - error: serializeError(e), - }); + logger.error( + { + savedSearchId: savedSearch.id, + chartConfig, + error: serializeError(e), + }, + 'Failed to fetch sample logs', + ); } rawTemplateBody = `${group ? `Group: "${group}"` : ''} diff --git a/packages/api/src/tasks/usageStats.ts b/packages/api/src/tasks/usageStats.ts index e9969916d..ac017db0e 100644 --- a/packages/api/src/tasks/usageStats.ts +++ b/packages/api/src/tasks/usageStats.ts @@ -4,7 +4,7 @@ import { MetricsDataType, SourceKind } from '@hyperdx/common-utils/dist/types'; import * as HyperDX from '@hyperdx/node-opentelemetry'; import ms from 'ms'; import os from 'os'; -import winston from 'winston'; +import pino from 'pino'; import * as config from '@/config'; import Connection from '@/models/connection'; @@ -12,19 +12,19 @@ import { Source, SourceDocument } from '@/models/source'; import Team from '@/models/team'; import User from '@/models/user'; -const logger = winston.createLogger({ +const logger = pino({ level: 'info', - format: winston.format.json(), - transports: [ - HyperDX.getWinstonTransport('info', { - headers: { - Authorization: '3f26ffad-14cf-4fb7-9dc9-e64fa0b84ee0', // hyperdx usage stats service api key - }, - baseUrl: 'https://in-otel.hyperdx.io/v1/logs', - maxLevel: 'info', - service: 'hyperdx-oss-usage-stats', - }), - ], + transport: { + targets: [ + HyperDX.getPinoTransport('info', { + headers: { + Authorization: '3f26ffad-14cf-4fb7-9dc9-e64fa0b84ee0', // hyperdx usage stats service api key + }, + baseUrl: 'https://in-otel.hyperdx.io/v1/logs', + service: 'hyperdx-oss-usage-stats', + }), + ], + }, }); function extractTableNames(source: SourceDocument): string[] { @@ -128,37 +128,39 @@ async function getUsageStats() { getClickhouseTableSize(), ]); const clusterId = team[0]?._id.toString(); - logger.info({ - message: 'track-hyperdx-oss-usage-stats', - clusterId, - version: config.CODE_VERSION, - userCounts, - os: { - arch: os.arch(), - freemem: os.freemem(), - uptime: os.uptime(), - }, - chStats: { - tables: chTables.reduce( - (acc, curr) => ({ - ...acc, - [curr.table]: { - avgDaySize: parseInt(curr.avgDaySize), - days: parseInt(curr.days), - lastModified: new Date(curr.latestModification).getTime(), - maxTime: new Date(curr.max_time).getTime(), - minTime: new Date(curr.min_time).getTime(), - rows: parseInt(curr.rows), - size: parseInt(curr.size), - }, - }), - {}, - ), - rows: chTables.reduce((acc, curr) => acc + parseInt(curr.rows), 0), - size: chTables.reduce((acc, curr) => acc + parseInt(curr.size), 0), + logger.info( + { + clusterId, + version: config.CODE_VERSION, + userCounts, + os: { + arch: os.arch(), + freemem: os.freemem(), + uptime: os.uptime(), + }, + chStats: { + tables: chTables.reduce( + (acc, curr) => ({ + ...acc, + [curr.table]: { + avgDaySize: parseInt(curr.avgDaySize), + days: parseInt(curr.days), + lastModified: new Date(curr.latestModification).getTime(), + maxTime: new Date(curr.max_time).getTime(), + minTime: new Date(curr.min_time).getTime(), + rows: parseInt(curr.rows), + size: parseInt(curr.size), + }, + }), + {}, + ), + rows: chTables.reduce((acc, curr) => acc + parseInt(curr.rows), 0), + size: chTables.reduce((acc, curr) => acc + parseInt(curr.size), 0), + }, + timestamp: nowInMs, }, - timestamp: nowInMs, - }); + 'track-hyperdx-oss-usage-stats', + ); } catch (err) { // ignore } diff --git a/packages/api/src/utils/logger.ts b/packages/api/src/utils/logger.ts index 897887a7c..26ef73dd5 100644 --- a/packages/api/src/utils/logger.ts +++ b/packages/api/src/utils/logger.ts @@ -1,57 +1,100 @@ -import { getWinstonTransport } from '@hyperdx/node-opentelemetry'; -import expressWinston from 'express-winston'; -import winston, { addColors } from 'winston'; - import { - APP_TYPE, - HYPERDX_API_KEY, - HYPERDX_LOG_LEVEL, - IS_PROD, -} from '@/config'; - -// LOCAL DEV ONLY -addColors({ - error: 'bold red', - warn: 'bold yellow', - info: 'white', - http: 'gray', - verbose: 'bold magenta', - debug: 'green', - silly: 'cyan', -}); + getPinoMixinFunction, + getPinoTransport, +} from '@hyperdx/node-opentelemetry'; +import type { Request, Response } from 'express'; +import pino from 'pino'; +import pinoHttp from 'pino-http'; -const MAX_LEVEL = HYPERDX_LOG_LEVEL ?? 'debug'; -const DEFAULT_FORMAT = winston.format.combine( - winston.format.errors({ stack: true }), - winston.format.json(), -); +import * as config from '@/config'; -const hyperdxTransport = HYPERDX_API_KEY - ? getWinstonTransport(MAX_LEVEL, { - bufferSize: APP_TYPE === 'scheduled-task' ? 1 : 100, +const MAX_LEVEL = config.HYPERDX_LOG_LEVEL ?? 'debug'; + +const hyperdxTransport = config.HYPERDX_API_KEY + ? getPinoTransport(MAX_LEVEL, { + detectResources: true, }) : null; -export const expressLogger = expressWinston.logger({ +// Configure transport based on environment and whether HyperDX is enabled +const getTransport = () => { + const targets: any[] = []; + + // Add HyperDX transport if API key is configured + if (hyperdxTransport) { + targets.push(hyperdxTransport); + } + + if (config.IS_DEV || config.IS_CI) { + // In development, use pino-pretty for nice console output + targets.push({ + target: 'pino-pretty', + level: MAX_LEVEL, + options: { + colorize: true, + translateTime: 'SYS:standard', + ignore: 'pid,hostname,trace_id,span_id,trace_flags', + }, + }); + } else { + targets.push({ + target: 'pino/file', + level: MAX_LEVEL, + options: { destination: 1 }, // this writes to STDOUT + }); + } + + // If only one target, return it directly; otherwise return multi-transport + if (targets.length === 0) { + return undefined; + } else if (targets.length === 1) { + return targets[0]; + } else { + return { targets }; + } +}; + +const logger = pino({ level: MAX_LEVEL, - format: DEFAULT_FORMAT, - msg: IS_PROD - ? undefined - : 'HTTP {{res.statusCode}} {{req.method}} {{req.url}} {{res.responseTime}}ms', - transports: [ - new winston.transports.Console(), - ...(hyperdxTransport ? [hyperdxTransport] : []), - ], - meta: IS_PROD, + transport: getTransport(), + mixin: getPinoMixinFunction, }); -const logger = winston.createLogger({ - level: MAX_LEVEL, - format: DEFAULT_FORMAT, - transports: [ - new winston.transports.Console(), - ...(hyperdxTransport ? [hyperdxTransport] : []), - ], +export const expressLogger = pinoHttp({ + logger, + customLogLevel: (_req, res, err) => { + if (res.statusCode >= 400 && res.statusCode < 500) { + return 'warn'; + } else if (res.statusCode >= 500 || err) { + return 'error'; + } + return 'info'; + }, + customSuccessMessage: (req: Request, res: Response) => { + return `HTTP ${req.method} ${req.originalUrl}`; + }, + customErrorMessage: (req: Request, res: Response, err) => { + return `HTTP ${req.method} ${req.originalUrl}`; + }, + customProps: (req: Request, res: Response) => { + const user = req.user; + if (user) { + return { + userId: user._id?.toString(), + userEmail: user.email, + }; + } + return {}; + }, + // Only disable req/res serializers in development/CI + ...(config.IS_DEV || config.IS_CI + ? { + serializers: { + req: () => undefined, + res: () => undefined, + }, + } + : {}), }); export default logger; diff --git a/packages/api/src/utils/passport.ts b/packages/api/src/utils/passport.ts index 74750bd3f..ab91ed898 100644 --- a/packages/api/src/utils/passport.ts +++ b/packages/api/src/utils/passport.ts @@ -45,7 +45,7 @@ passport.use( } return done(null, user, error); } catch (err) { - logger.error(`Login for "${username}" failed, error: ${err}"`); + logger.error({ err, username }, 'Login failed with error'); return done(err); } }, diff --git a/packages/app/package.json b/packages/app/package.json index 9536eada3..cd2086661 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -30,7 +30,7 @@ "@hookform/resolvers": "^3.9.0", "@hyperdx/browser": "^0.21.1", "@hyperdx/common-utils": "^0.7.0", - "@hyperdx/node-opentelemetry": "^0.8.2", + "@hyperdx/node-opentelemetry": "^0.9.0", "@lezer/highlight": "^1.2.0", "@mantine/core": "7.9.2", "@mantine/dates": "^7.11.2", diff --git a/yarn.lock b/yarn.lock index 43d60790e..841b918c2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3402,17 +3402,6 @@ __metadata: languageName: node linkType: hard -"@dabh/diagnostics@npm:^2.0.2": - version: 2.0.3 - resolution: "@dabh/diagnostics@npm:2.0.3" - dependencies: - colorspace: "npm:1.1.x" - enabled: "npm:2.0.x" - kuler: "npm:^2.0.0" - checksum: 10c0/a5133df8492802465ed01f2f0a5784585241a1030c362d54a602ed1839816d6c93d71dde05cf2ddb4fd0796238c19774406bd62fa2564b637907b495f52425fe - languageName: node - linkType: hard - "@discoveryjs/json-ext@npm:^0.5.3": version: 0.5.7 resolution: "@discoveryjs/json-ext@npm:0.5.7" @@ -4496,7 +4485,7 @@ __metadata: "@ai-sdk/anthropic": "npm:^2.0.23" "@esm2cjs/p-queue": "npm:^7.3.0" "@hyperdx/common-utils": "npm:^0.7.0" - "@hyperdx/node-opentelemetry": "npm:^0.8.2" + "@hyperdx/node-opentelemetry": "npm:^0.9.0" "@hyperdx/passport-local-mongoose": "npm:^9.0.1" "@opentelemetry/api": "npm:^1.8.0" "@opentelemetry/host-metrics": "npm:^0.35.5" @@ -4527,7 +4516,6 @@ __metadata: express: "npm:^4.19.2" express-rate-limit: "npm:^6.7.1" express-session: "npm:^1.17.3" - express-winston: "npm:^4.2.0" handlebars: "npm:^4.7.8" http-graceful-shutdown: "npm:^3.1.13" http-proxy-middleware: "npm:^3.0.5" @@ -4543,6 +4531,9 @@ __metadata: on-headers: "npm:^1.1.0" passport: "npm:^0.6.0" passport-local: "npm:^1.0.0" + pino: "npm:^10.0.0" + pino-http: "npm:^11.0.0" + pino-pretty: "npm:^13.1.1" promised-handlebars: "npm:^2.0.1" protobufjs: "npm:^7.5.2" rimraf: "npm:^4.4.1" @@ -4557,7 +4548,6 @@ __metadata: tsconfig-paths: "npm:^4.2.0" typescript: "npm:5.9.3" uuid: "npm:^8.3.2" - winston: "npm:^3.10.0" zod: "npm:3.25" zod-express-middleware: "npm:^1.4.0" languageName: unknown @@ -4574,7 +4564,7 @@ __metadata: "@hookform/resolvers": "npm:^3.9.0" "@hyperdx/browser": "npm:^0.21.1" "@hyperdx/common-utils": "npm:^0.7.0" - "@hyperdx/node-opentelemetry": "npm:^0.8.2" + "@hyperdx/node-opentelemetry": "npm:^0.9.0" "@jedmao/location": "npm:^3.0.0" "@lezer/highlight": "npm:^1.2.0" "@mantine/core": "npm:7.9.2" @@ -4786,9 +4776,9 @@ __metadata: languageName: node linkType: hard -"@hyperdx/node-opentelemetry@npm:^0.8.2": - version: 0.8.2 - resolution: "@hyperdx/node-opentelemetry@npm:0.8.2" +"@hyperdx/node-opentelemetry@npm:^0.9.0": + version: 0.9.0 + resolution: "@hyperdx/node-opentelemetry@npm:0.9.0" dependencies: "@hyperdx/instrumentation-exception": "npm:^0.1.0" "@hyperdx/instrumentation-sentry-node": "npm:^0.1.0" @@ -4796,8 +4786,11 @@ __metadata: "@opentelemetry/api-logs": "npm:^0.57.2" "@opentelemetry/auto-instrumentations-node": "npm:^0.56.1" "@opentelemetry/core": "npm:^1.30.1" + "@opentelemetry/exporter-logs-otlp-grpc": "npm:^0.57.2" "@opentelemetry/exporter-logs-otlp-http": "npm:^0.57.2" + "@opentelemetry/exporter-metrics-otlp-grpc": "npm:^0.57.2" "@opentelemetry/exporter-metrics-otlp-proto": "npm:^0.57.2" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:^0.57.2" "@opentelemetry/exporter-trace-otlp-proto": "npm:^0.57.2" "@opentelemetry/instrumentation": "npm:^0.57.2" "@opentelemetry/instrumentation-http": "npm:^0.57.2" @@ -4823,7 +4816,7 @@ __metadata: winston-transport: "npm:^4.7.0" bin: opentelemetry-instrument: build/bin/opentelemetry-instrument.js - checksum: 10c0/9f723d78b3f0da6fb3fab38826385384f3aeeeb4e2852609151effac336627d006522bcc74618f5384b1ec778b488d5dcf51c992d013f9310e780bc7349e0975 + checksum: 10c0/dc4101e78b1aca867377b68568a29ad2c1a12ae79ecd07486fbd152d1bc9d4b057469154cb49c7b4a8f6f690f9851d0cb497381bd7dd11ce11bca71e0f884981 languageName: node linkType: hard @@ -6416,7 +6409,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-grpc@npm:0.57.2": +"@opentelemetry/exporter-logs-otlp-grpc@npm:0.57.2, @opentelemetry/exporter-logs-otlp-grpc@npm:^0.57.2": version: 0.57.2 resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.57.2" dependencies: @@ -6464,7 +6457,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-metrics-otlp-grpc@npm:0.57.2": +"@opentelemetry/exporter-metrics-otlp-grpc@npm:0.57.2, @opentelemetry/exporter-metrics-otlp-grpc@npm:^0.57.2": version: 0.57.2 resolution: "@opentelemetry/exporter-metrics-otlp-grpc@npm:0.57.2" dependencies: @@ -6526,7 +6519,7 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-grpc@npm:0.57.2": +"@opentelemetry/exporter-trace-otlp-grpc@npm:0.57.2, @opentelemetry/exporter-trace-otlp-grpc@npm:^0.57.2": version: 0.57.2 resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.57.2" dependencies: @@ -12127,6 +12120,13 @@ __metadata: languageName: node linkType: hard +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 10c0/e329a6665512736a9bbb073e1761b4ec102f7926cce35037753146a9db9c8104f5044c1662e4a863576ce544fb8be27cd2be6bc8c1a40147d03f31eb1cfb6e8a + languageName: node + linkType: hard + "attr-accept@npm:^2.2.4": version: 2.2.5 resolution: "attr-accept@npm:2.2.5" @@ -13486,7 +13486,7 @@ __metadata: languageName: node linkType: hard -"color@npm:^3.1.3, color@npm:^3.2.1": +"color@npm:^3.2.1": version: 3.2.1 resolution: "color@npm:3.2.1" dependencies: @@ -13513,7 +13513,7 @@ __metadata: languageName: node linkType: hard -"colorette@npm:^2.0.10": +"colorette@npm:^2.0.10, colorette@npm:^2.0.7": version: 2.0.20 resolution: "colorette@npm:2.0.20" checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 @@ -13527,16 +13527,6 @@ __metadata: languageName: node linkType: hard -"colorspace@npm:1.1.x": - version: 1.1.4 - resolution: "colorspace@npm:1.1.4" - dependencies: - color: "npm:^3.1.3" - text-hex: "npm:1.0.x" - checksum: 10c0/af5f91ff7f8e146b96e439ac20ed79b197210193bde721b47380a75b21751d90fa56390c773bb67c0aedd34ff85091883a437ab56861c779bd507d639ba7e123 - languageName: node - linkType: hard - "combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" @@ -14271,6 +14261,13 @@ __metadata: languageName: node linkType: hard +"dateformat@npm:^4.6.3": + version: 4.6.3 + resolution: "dateformat@npm:4.6.3" + checksum: 10c0/e2023b905e8cfe2eb8444fb558562b524807a51cdfe712570f360f873271600b5c94aebffaf11efb285e2c072264a7cf243eadb68f3eba0f8cc85fb86cd25df6 + languageName: node + linkType: hard + "dayjs@npm:^1.10.0": version: 1.11.7 resolution: "dayjs@npm:1.11.7" @@ -14974,13 +14971,6 @@ __metadata: languageName: node linkType: hard -"enabled@npm:2.0.x": - version: 2.0.0 - resolution: "enabled@npm:2.0.0" - checksum: 10c0/3b2c2af9bc7f8b9e291610f2dde4a75cf6ee52a68f4dd585482fbdf9a55d65388940e024e56d40bb03e05ef6671f5f53021fa8b72a20e954d7066ec28166713f - languageName: node - linkType: hard - "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -16375,18 +16365,6 @@ __metadata: languageName: node linkType: hard -"express-winston@npm:^4.2.0": - version: 4.2.0 - resolution: "express-winston@npm:4.2.0" - dependencies: - chalk: "npm:^2.4.2" - lodash: "npm:^4.17.21" - peerDependencies: - winston: ">=3.x <4" - checksum: 10c0/8f80993e7d7696b22a12c68ffb72ea0cd3ad980d8394073fd972162cdca116b8f506974dd504987e350cddfdbf55402871762dcb9c69f2f7feaef2df6d93ef09 - languageName: node - linkType: hard - "express@npm:^4.17.3, express@npm:^4.19.2": version: 4.19.2 resolution: "express@npm:4.19.2" @@ -16460,6 +16438,13 @@ __metadata: languageName: node linkType: hard +"fast-copy@npm:^3.0.2": + version: 3.0.2 + resolution: "fast-copy@npm:3.0.2" + checksum: 10c0/02e8b9fd03c8c024d2987760ce126456a0e17470850b51e11a1c3254eed6832e4733ded2d93316c82bc0b36aeb991ad1ff48d1ba95effe7add7c3ab8d8eb554a + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -16875,13 +16860,6 @@ __metadata: languageName: node linkType: hard -"fn.name@npm:1.x.x": - version: 1.1.0 - resolution: "fn.name@npm:1.1.0" - checksum: 10c0/8ad62aa2d4f0b2a76d09dba36cfec61c540c13a0fd72e5d94164e430f987a7ce6a743112bbeb14877c810ef500d1f73d7f56e76d029d2e3413f20d79e3460a9a - languageName: node - linkType: hard - "focus-visible@npm:^5.2.0": version: 5.2.0 resolution: "focus-visible@npm:5.2.0" @@ -17973,6 +17951,13 @@ __metadata: languageName: node linkType: hard +"help-me@npm:^5.0.0": + version: 5.0.0 + resolution: "help-me@npm:5.0.0" + checksum: 10c0/054c0e2e9ae2231c85ab5e04f75109b9d068ffcc54e58fb22079822a5ace8ff3d02c66fd45379c902ad5ab825e5d2e1451fcc2f7eab1eb49e7d488133ba4cacb + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -20191,13 +20176,6 @@ __metadata: languageName: node linkType: hard -"kuler@npm:^2.0.0": - version: 2.0.0 - resolution: "kuler@npm:2.0.0" - checksum: 10c0/0a4e99d92ca373f8f74d1dc37931909c4d0d82aebc94cf2ba265771160fc12c8df34eaaac80805efbda367e2795cb1f1dd4c3d404b6b1cf38aec94035b503d2d - languageName: node - linkType: hard - "ky-universal@npm:^0.10.1": version: 0.10.1 resolution: "ky-universal@npm:0.10.1" @@ -20564,7 +20542,7 @@ __metadata: languageName: node linkType: hard -"logform@npm:^2.3.2, logform@npm:^2.4.0": +"logform@npm:^2.3.2": version: 2.5.1 resolution: "logform@npm:2.5.1" dependencies: @@ -22925,6 +22903,13 @@ __metadata: languageName: node linkType: hard +"on-exit-leak-free@npm:^2.1.0": + version: 2.1.2 + resolution: "on-exit-leak-free@npm:2.1.2" + checksum: 10c0/faea2e1c9d696ecee919026c32be8d6a633a7ac1240b3b87e944a380e8a11dc9c95c4a1f8fb0568de7ab8db3823e790f12bda45296b1d111e341aad3922a0570 + languageName: node + linkType: hard + "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -22957,15 +22942,6 @@ __metadata: languageName: node linkType: hard -"one-time@npm:^1.0.0": - version: 1.0.0 - resolution: "one-time@npm:1.0.0" - dependencies: - fn.name: "npm:1.x.x" - checksum: 10c0/6e4887b331edbb954f4e915831cbec0a7b9956c36f4feb5f6de98c448ac02ff881fd8d9b55a6b1b55030af184c6b648f340a76eb211812f4ad8c9b4b8692fdaa - languageName: node - linkType: hard - "onetime@npm:^5.1.0, onetime@npm:^5.1.2": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -23623,6 +23599,78 @@ __metadata: languageName: node linkType: hard +"pino-abstract-transport@npm:^2.0.0": + version: 2.0.0 + resolution: "pino-abstract-transport@npm:2.0.0" + dependencies: + split2: "npm:^4.0.0" + checksum: 10c0/02c05b8f2ffce0d7c774c8e588f61e8b77de8ccb5f8125afd4a7325c9ea0e6af7fb78168999657712ae843e4462bb70ac550dfd6284f930ee57f17f486f25a9f + languageName: node + linkType: hard + +"pino-http@npm:^11.0.0": + version: 11.0.0 + resolution: "pino-http@npm:11.0.0" + dependencies: + get-caller-file: "npm:^2.0.5" + pino: "npm:^10.0.0" + pino-std-serializers: "npm:^7.0.0" + process-warning: "npm:^5.0.0" + checksum: 10c0/75110c7a7f1b1c4eadfbff3b87599ef9d100c20c3ffd19541f0cb37cd11285a3f221bb90d33df0772ed94a3f7d5b2007bd7365c5f5525cff2178d4deef140ee5 + languageName: node + linkType: hard + +"pino-pretty@npm:^13.1.1": + version: 13.1.1 + resolution: "pino-pretty@npm:13.1.1" + dependencies: + colorette: "npm:^2.0.7" + dateformat: "npm:^4.6.3" + fast-copy: "npm:^3.0.2" + fast-safe-stringify: "npm:^2.1.1" + help-me: "npm:^5.0.0" + joycon: "npm:^3.1.1" + minimist: "npm:^1.2.6" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^2.0.0" + pump: "npm:^3.0.0" + secure-json-parse: "npm:^4.0.0" + sonic-boom: "npm:^4.0.1" + strip-json-comments: "npm:^5.0.2" + bin: + pino-pretty: bin.js + checksum: 10c0/845c07afd3d73cb96ad2049cfa7fca12b8280a51e30d6db8b490857690637556bb8e7f05b2fa640b3e4a7edd9b1369110042d670fda743ef98fe3be29876c8c7 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^7.0.0": + version: 7.0.0 + resolution: "pino-std-serializers@npm:7.0.0" + checksum: 10c0/73e694d542e8de94445a03a98396cf383306de41fd75ecc07085d57ed7a57896198508a0dec6eefad8d701044af21eb27253ccc352586a03cf0d4a0bd25b4133 + languageName: node + linkType: hard + +"pino@npm:^10.0.0": + version: 10.0.0 + resolution: "pino@npm:10.0.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^2.0.0" + pino-std-serializers: "npm:^7.0.0" + process-warning: "npm:^5.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.2.0" + safe-stable-stringify: "npm:^2.3.1" + slow-redact: "npm:^0.3.0" + sonic-boom: "npm:^4.0.1" + thread-stream: "npm:^3.0.0" + bin: + pino: bin.js + checksum: 10c0/f95fcc51523310e9ece1822f8ef4d8e6c2b35f67eca9805fe18fdef21dfac81fa128f1ebaa3c9a11571120854391b10b3b339f2e5836f805edaf6936781c6e6f + languageName: node + linkType: hard + "pirates@npm:^4.0.1, pirates@npm:^4.0.6": version: 4.0.6 resolution: "pirates@npm:4.0.6" @@ -24132,6 +24180,13 @@ __metadata: languageName: node linkType: hard +"process-warning@npm:^5.0.0": + version: 5.0.0 + resolution: "process-warning@npm:5.0.0" + checksum: 10c0/941f48863d368ec161e0b5890ba0c6af94170078f3d6b5e915c19b36fb59edb0dc2f8e834d25e0d375a8bf368a49d490f080508842168832b93489d17843ec29 + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -24450,6 +24505,13 @@ __metadata: languageName: node linkType: hard +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 10c0/fe5acc6f775b172ca5b4373df26f7e4fd347975578199e7d74b2ae4077f0af05baa27d231de1e80e8f72d88275ccc6028568a7a8c9ee5e7368ace0e18eff93a4 + languageName: node + linkType: hard + "quick-lru@npm:^4.0.1": version: 4.0.1 resolution: "quick-lru@npm:4.0.1" @@ -25215,6 +25277,13 @@ __metadata: languageName: node linkType: hard +"real-require@npm:^0.2.0": + version: 0.2.0 + resolution: "real-require@npm:0.2.0" + checksum: 10c0/23eea5623642f0477412ef8b91acd3969015a1501ed34992ada0e3af521d3c865bb2fe4cdbfec5fe4b505f6d1ef6a03e5c3652520837a8c3b53decff7e74b6a0 + languageName: node + linkType: hard + "recast@npm:^0.23.3, recast@npm:^0.23.5": version: 0.23.7 resolution: "recast@npm:0.23.7" @@ -26106,6 +26175,13 @@ __metadata: languageName: node linkType: hard +"secure-json-parse@npm:^4.0.0": + version: 4.1.0 + resolution: "secure-json-parse@npm:4.1.0" + checksum: 10c0/52b3f8125ea974db1333a5b63e6a1df550c36c0d5f9a263911d6732812bd02e938b30be324dcbbb9da3ef9bf5a84849e0dd911f56544003d3c09e8eee12504de + languageName: node + linkType: hard + "semver@npm:2 || 3 || 4 || 5, semver@npm:^5.7.1": version: 5.7.1 resolution: "semver@npm:5.7.1" @@ -26558,6 +26634,13 @@ __metadata: languageName: node linkType: hard +"slow-redact@npm:^0.3.0": + version: 0.3.1 + resolution: "slow-redact@npm:0.3.1" + checksum: 10c0/7f495a85108f554ea492b7fa1c8892b690a8172cb6d8cd77c56fef00a15d15f46b497a8eb61355a61cc06cbb34246088dc3372ed2ffb22e645c8c5dabc334563 + languageName: node + linkType: hard + "smart-buffer@npm:^4.2.0": version: 4.2.0 resolution: "smart-buffer@npm:4.2.0" @@ -26619,6 +26702,15 @@ __metadata: languageName: node linkType: hard +"sonic-boom@npm:^4.0.1": + version: 4.2.0 + resolution: "sonic-boom@npm:4.2.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 10c0/ae897e6c2cd6d3cb7cdcf608bc182393b19c61c9413a85ce33ffd25891485589f39bece0db1de24381d0a38fc03d08c9862ded0c60f184f1b852f51f97af9684 + languageName: node + linkType: hard + "source-map-js@npm:>=0.6.2 <2.0.0, source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" @@ -26806,13 +26898,6 @@ __metadata: languageName: node linkType: hard -"stack-trace@npm:0.0.x": - version: 0.0.10 - resolution: "stack-trace@npm:0.0.10" - checksum: 10c0/9ff3dabfad4049b635a85456f927a075c9d0c210e3ea336412d18220b2a86cbb9b13ec46d6c37b70a302a4ea4d49e30e5d4944dd60ae784073f1cde778ac8f4b - languageName: node - linkType: hard - "stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -27162,6 +27247,13 @@ __metadata: languageName: node linkType: hard +"strip-json-comments@npm:^5.0.2": + version: 5.0.3 + resolution: "strip-json-comments@npm:5.0.3" + checksum: 10c0/daaf20b29f69fb51112698f4a9a662490dbb78d5baf6127c75a0a83c2ac6c078a8c0f74b389ad5e0519d6fc359c4a57cb9971b1ae201aef62ce45a13247791e0 + languageName: node + linkType: hard + "strnum@npm:^1.0.5": version: 1.0.5 resolution: "strnum@npm:1.0.5" @@ -27724,13 +27816,6 @@ __metadata: languageName: node linkType: hard -"text-hex@npm:1.0.x": - version: 1.0.0 - resolution: "text-hex@npm:1.0.0" - checksum: 10c0/57d8d320d92c79d7c03ffb8339b825bb9637c2cbccf14304309f51d8950015c44464b6fd1b6820a3d4821241c68825634f09f5a2d9d501e84f7c6fd14376860d - languageName: node - linkType: hard - "text-table@npm:^0.2.0": version: 0.2.0 resolution: "text-table@npm:0.2.0" @@ -27756,6 +27841,15 @@ __metadata: languageName: node linkType: hard +"thread-stream@npm:^3.0.0": + version: 3.1.0 + resolution: "thread-stream@npm:3.1.0" + dependencies: + real-require: "npm:^0.2.0" + checksum: 10c0/c36118379940b77a6ef3e6f4d5dd31e97b8210c3f7b9a54eb8fe6358ab173f6d0acfaf69b9c3db024b948c0c5fd2a7df93e2e49151af02076b35ada3205ec9a6 + languageName: node + linkType: hard + "through2@npm:^2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" @@ -29644,17 +29738,6 @@ __metadata: languageName: node linkType: hard -"winston-transport@npm:^4.5.0": - version: 4.5.0 - resolution: "winston-transport@npm:4.5.0" - dependencies: - logform: "npm:^2.3.2" - readable-stream: "npm:^3.6.0" - triple-beam: "npm:^1.3.0" - checksum: 10c0/110a47c5acc87c3aa0f101741c0a992e52a86802272838c18aede8178d2b5e80254d2433dcac3439cefbc2777d9e22e65f84e9cee3130681c58e4ae5d58f50c3 - languageName: node - linkType: hard - "winston-transport@npm:^4.7.0": version: 4.7.0 resolution: "winston-transport@npm:4.7.0" @@ -29666,25 +29749,6 @@ __metadata: languageName: node linkType: hard -"winston@npm:^3.10.0": - version: 3.10.0 - resolution: "winston@npm:3.10.0" - dependencies: - "@colors/colors": "npm:1.5.0" - "@dabh/diagnostics": "npm:^2.0.2" - async: "npm:^3.2.3" - is-stream: "npm:^2.0.0" - logform: "npm:^2.4.0" - one-time: "npm:^1.0.0" - readable-stream: "npm:^3.4.0" - safe-stable-stringify: "npm:^2.3.1" - stack-trace: "npm:0.0.x" - triple-beam: "npm:^1.3.0" - winston-transport: "npm:^4.5.0" - checksum: 10c0/eb9298ae9c335f54d4731525231df9f216d39a55bc1676b0375cbd9d2a14e302d0a700972ffb97626ab352e41f607739b5b8ee5feaadcf06c11977cb8ecd834a - languageName: node - linkType: hard - "word-wrap@npm:~1.2.3": version: 1.2.5 resolution: "word-wrap@npm:1.2.5"