Skip to content

Commit e57998a

Browse files
committed
chore(logs): improve mongo and graphql logs
1 parent 323e42f commit e57998a

5 files changed

Lines changed: 109 additions & 10 deletions

File tree

src/index.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import PlansFactory from './models/plansFactory';
2626
import BusinessOperationsFactory from './models/businessOperationsFactory';
2727
import schema from './schema';
2828
import { graphqlUploadExpress } from 'graphql-upload';
29-
import morgan from 'morgan';
3029
import { metricsMiddleware, createMetricsServer, graphqlMetricsPlugin } from './metrics';
30+
import { requestLogger } from './utils/logger';
3131

3232
/**
3333
* Option to enable playground
@@ -85,19 +85,17 @@ class HawkAPI {
8585
next();
8686
});
8787

88-
/**
89-
* Setup request logger.
90-
* Uses 'combined' format in production for Apache-style logging,
91-
* and 'dev' format in development for colored, concise output.
92-
*/
93-
this.app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));
94-
9588
/**
9689
* Add metrics middleware to track HTTP requests
9790
*/
9891
this.app.use(metricsMiddleware);
9992

10093
this.app.use(express.json());
94+
95+
/**
96+
* Setup request logger with custom formatters (GraphQL operation name support)
97+
*/
98+
this.app.use(requestLogger);
10199
this.app.use(bodyParser.urlencoded({ extended: false }));
102100
this.app.use('/static', express.static(`./static`));
103101

src/metrics/mongodb.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,70 @@ export function withMongoMetrics(options: MongoClientOptions = {}): MongoClientO
113113
};
114114
}
115115

116+
/**
117+
* Format filter/update parameters for logging
118+
* @param params - Parameters to format
119+
* @returns Formatted string
120+
*/
121+
function formatParams(params: any): string {
122+
if (!params || Object.keys(params).length === 0) {
123+
return '';
124+
}
125+
126+
try {
127+
return JSON.stringify(params);
128+
} catch (e) {
129+
return String(params);
130+
}
131+
}
132+
133+
/**
134+
* Log MongoDB command details to console
135+
* Format: [requestId] db.collection.command(params)
136+
* @param event - MongoDB command event
137+
*/
138+
function logCommandStarted(event: any): void {
139+
const collectionRaw = extractCollectionFromCommand(event.command, event.commandName);
140+
const collection = normalizeCollectionName(collectionRaw);
141+
const db = event.databaseName || 'unknown db';
142+
const filter = event.command.filter;
143+
const update = event.command.update;
144+
const pipeline = event.command.pipeline;
145+
const projection = event.command.projection;
146+
const params = filter || update || pipeline;
147+
const paramsStr = formatParams(params);
148+
149+
console.log(`[${event.requestId}] ${db}.${collection}.${event.commandName}(${paramsStr}) ${projection ? `projection: ${formatParams(projection)}` : ''}`);
150+
}
151+
152+
/**
153+
* Log MongoDB command success to console
154+
* Format: [requestId] commandName ✓ duration
155+
* @param event - MongoDB command event
156+
*/
157+
function logCommandSucceeded(event: any): void {
158+
console.log(`[${event.requestId}] ${event.commandName}${event.duration}ms`);
159+
}
160+
161+
/**
162+
* Log MongoDB command failure to console
163+
* Format: [requestId] ✗ error
164+
* @param event - MongoDB command event
165+
*/
166+
function logCommandFailed(event: any): void {
167+
const errorMsg = event.failure?.message || event.failure?.errmsg || 'Unknown error';
168+
169+
console.error(`[${event.requestId}] ${event.commandName}${errorMsg} (${event.duration}ms)`);
170+
}
171+
116172
/**
117173
* Setup MongoDB metrics monitoring on a MongoClient
118174
* @param client - MongoDB client to monitor
119175
*/
120176
export function setupMongoMetrics(client: MongoClient): void {
121177
client.on('commandStarted', (event) => {
178+
logCommandStarted(event);
179+
122180
// Store start time and metadata for this command
123181
const metadataKey = `${event.requestId}`;
124182

@@ -139,6 +197,8 @@ export function setupMongoMetrics(client: MongoClient): void {
139197
});
140198

141199
client.on('commandSucceeded', (event) => {
200+
logCommandSucceeded(event);
201+
142202
const metadataKey = `${event.requestId}`;
143203
// eslint-disable-next-line @typescript-eslint/no-explicit-any
144204
const metadata = (client as any)[metadataKey];
@@ -157,6 +217,8 @@ export function setupMongoMetrics(client: MongoClient): void {
157217
});
158218

159219
client.on('commandFailed', (event) => {
220+
logCommandFailed(event);
221+
160222
const metadataKey = `${event.requestId}`;
161223
// eslint-disable-next-line @typescript-eslint/no-explicit-any
162224
const metadata = (client as any)[metadataKey];

src/models/eventsFactory.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ class EventsFactory extends Factory {
665665
async getEventRelease(eventId) {
666666
const eventOriginal = await this.findById(eventId);
667667

668-
if (!eventOriginal) {
668+
if (!eventOriginal || !eventOriginal.payload.release) {
669669
return null;
670670
}
671671

src/mongo.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ export async function setupConnections(): Promise<void> {
7575
databases.hawk = hawkMongoClient.db();
7676
databases.events = eventsMongoClient.db();
7777

78-
// Setup metrics monitoring for both clients
78+
/**
79+
* Log and and measure MongoDB metrics
80+
*/
7981
setupMongoMetrics(hawkMongoClient);
8082
setupMongoMetrics(eventsMongoClient);
8183
} catch (e) {

src/utils/logger.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import morgan from 'morgan';
2+
import express from 'express';
3+
4+
/**
5+
* Setup custom GraphQL-aware morgan token.
6+
* Extracts operation name from GraphQL requests to show query/mutation names in logs.
7+
*/
8+
morgan.token('graphql-operation', (req: express.Request) => {
9+
if (req.body && req.body.operationName) {
10+
return req.body.operationName;
11+
}
12+
if (req.body && req.body.query) {
13+
/* Try to extract operation name from query string if operationName is not provided */
14+
const match = req.body.query.match(/(?:query|mutation)\s+(\w+)/);
15+
16+
if (match && match[1]) {
17+
return match[1];
18+
}
19+
}
20+
21+
return '-';
22+
});
23+
24+
/**
25+
* Custom morgan format for GraphQL-aware logging.
26+
* Development: shows method, url, operation name, status, response time, content length
27+
* Production: Apache-style format with operation name included
28+
*/
29+
const customFormat = process.env.NODE_ENV === 'production'
30+
? ':remote-addr - :remote-user [:date[clf]] ":method :url :graphql-operation" :status :res[content-length] - :response-time ms'
31+
: ':method :url :graphql-operation :status :response-time ms - :res[content-length]';
32+
33+
/**
34+
* Configured morgan middleware with GraphQL operation name logging
35+
*/
36+
export const requestLogger = morgan(customFormat);
37+

0 commit comments

Comments
 (0)