Skip to content

Commit ea25142

Browse files
committed
chore: Add custom breadcrumbs to API error tracking
1 parent 4be9d84 commit ea25142

5 files changed

Lines changed: 71 additions & 35 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
"@graphql-tools/merge": "^8.3.1",
4242
"@graphql-tools/schema": "^8.5.1",
4343
"@graphql-tools/utils": "^8.9.0",
44-
"@hawk.so/nodejs": "^3.1.1",
45-
"@hawk.so/types": "^0.5.6",
44+
"@hawk.so/nodejs": "^3.3.0",
45+
"@hawk.so/types": "^0.5.8",
4646
"@n1ru4l/json-patch-plus": "^0.2.0",
4747
"@node-saml/node-saml": "^5.0.1",
4848
"@octokit/oauth-methods": "^4.0.0",

src/metrics/graphql.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import client from 'prom-client';
22
import { ApolloServerPlugin, GraphQLRequestContext, GraphQLRequestListener } from 'apollo-server-plugin-base';
33
import { GraphQLError } from 'graphql';
4+
import HawkCatcher from '@hawk.so/nodejs';
45

56
/**
67
* GraphQL operation duration histogram
@@ -71,15 +72,32 @@ export const graphqlMetricsPlugin: ApolloServerPlugin = {
7172
},
7273

7374
async willSendResponse(ctx: GraphQLRequestContext): Promise<void> {
74-
const duration = (Date.now() - startTime) / 1000;
75+
const durationMs = Date.now() - startTime;
76+
const duration = durationMs / 1000;
7577

7678
gqlOperationDuration
7779
.labels(operationName, operationType)
7880
.observe(duration);
7981

82+
const hasErrors = ctx.errors && ctx.errors.length > 0;
83+
84+
const breadcrumbData: Record<string, string | number> = { operationName, operationType, durationMs };
85+
86+
if (hasErrors) {
87+
breadcrumbData.errors = ctx.errors!.map((e: GraphQLError) => e.message).join('; ');
88+
}
89+
90+
HawkCatcher.breadcrumbs.add({
91+
type: 'request',
92+
category: 'gql',
93+
message: `${operationType} ${operationName} ${durationMs}ms${hasErrors ? ` [${ctx.errors!.length} error(s)]` : ''}`,
94+
level: hasErrors ? 'error' : 'debug',
95+
data: breadcrumbData,
96+
});
97+
8098
// Track errors if any
81-
if (ctx.errors && ctx.errors.length > 0) {
82-
ctx.errors.forEach((error: GraphQLError) => {
99+
if (hasErrors) {
100+
ctx.errors!.forEach((error: GraphQLError) => {
83101
const errorType = error.extensions?.code || error.name || 'unknown';
84102

85103
gqlOperationErrors

src/metrics/mongodb.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import promClient from 'prom-client';
22
import { MongoClient, MongoClientOptions } from 'mongodb';
33
import { Effect, sgr } from '../utils/ansi';
4+
import HawkCatcher from '@hawk.so/nodejs';
45

56
/**
67
* MongoDB command duration histogram
@@ -306,6 +307,14 @@ export function setupMongoMetrics(client: MongoClient): void {
306307
.labels(metadata.commandName, metadata.collectionFamily, metadata.db)
307308
.observe(duration);
308309

310+
HawkCatcher.breadcrumbs.add({
311+
type: 'request',
312+
category: 'db.query',
313+
message: `${metadata.db}.${metadata.collectionFamily}.${metadata.commandName} ${event.duration}ms`,
314+
level: 'debug',
315+
data: { db: metadata.db, collection: metadata.collectionFamily, command: metadata.commandName, durationMs: event.duration },
316+
});
317+
309318
// Clean up metadata
310319
// eslint-disable-next-line @typescript-eslint/no-explicit-any
311320
delete (client as any)[metadataKey];
@@ -337,6 +346,16 @@ export function setupMongoMetrics(client: MongoClient): void {
337346
.labels(metadata.commandName, errorCode)
338347
.inc();
339348

349+
const errorMsg = (event.failure as any)?.message || 'Unknown error';
350+
351+
HawkCatcher.breadcrumbs.add({
352+
type: 'error',
353+
category: 'db.query',
354+
message: `${metadata.db}.${metadata.collectionFamily}.${metadata.commandName} FAILED: ${errorMsg} ${event.duration}ms`,
355+
level: 'error',
356+
data: { db: metadata.db, collection: metadata.collectionFamily, command: metadata.commandName, durationMs: event.duration, errorCode },
357+
});
358+
340359
// Clean up metadata
341360
// eslint-disable-next-line @typescript-eslint/no-explicit-any
342361
delete (client as any)[metadataKey];

src/rabbitmq.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,22 @@ export async function setupConnections(): Promise<void> {
133133
export async function publish(exchange: string, route: string, message: string, options?: Options.Publish): Promise<void> {
134134
try {
135135
await channel.publish(exchange, route, Buffer.from(message), options);
136+
HawkCatcher.breadcrumbs.add({
137+
type: 'request',
138+
category: 'amqp.publish',
139+
message: `AMQP publish ${exchange || '(default)'}/${route}`,
140+
level: 'debug',
141+
data: { exchange, route },
142+
});
136143
debug(`Message sent: ${message}`);
137144
} catch (err) {
145+
HawkCatcher.breadcrumbs.add({
146+
type: 'error',
147+
category: 'amqp.publish',
148+
message: `AMQP publish FAILED ${exchange || '(default)'}/${route}: ${(err as Error).message}`,
149+
level: 'error',
150+
data: { exchange, route },
151+
});
138152
HawkCatcher.send(err as Error);
139153
console.log('Message was rejected:', (err as Error).stack);
140154
}

yarn.lock

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -494,26 +494,26 @@
494494
dependencies:
495495
tslib "^2.4.0"
496496

497-
"@hawk.so/nodejs@^3.1.1":
498-
version "3.1.2"
499-
resolved "https://registry.yarnpkg.com/@hawk.so/nodejs/-/nodejs-3.1.2.tgz#b06229f0c8a0d8676412329511f9f2b01e492211"
500-
integrity sha512-FqZtJDEc3G/VdirsEEfA4BodA3OGXCSy2188aPSeaLkLWswaKAnkaJNTGHQL59dtOeSbvipMJVgtoqihHkpGBQ==
497+
"@hawk.so/nodejs@^3.3.0":
498+
version "3.3.0"
499+
resolved "https://registry.yarnpkg.com/@hawk.so/nodejs/-/nodejs-3.3.0.tgz#db140b3166cbb2d1199195dd0753f478318113b5"
500+
integrity sha512-0mSJedm+dKjR8Fh8buHmfz3NVamqI7rJiS1y9a5sEECnqQEb1NQWeYO64iIfQJKgmG+Huxa5SyCl/WN0q7eugw==
501501
dependencies:
502-
"@hawk.so/types" "^0.1.15"
502+
"@hawk.so/types" "^0.3.0"
503503
axios "^0.21.1"
504504
stack-trace "^0.0.10"
505505

506-
"@hawk.so/types@^0.1.15":
507-
version "0.1.18"
508-
resolved "https://registry.yarnpkg.com/@hawk.so/types/-/types-0.1.18.tgz#746537634756825f066182737429d11ea124d5c5"
509-
integrity sha512-SvECLGmLb5t90OSpk3n8DCjJsUoyjrq/Z6Ioil80tVkbMXRdGjaHZpn/0w1gBqtgNWBfW2cSbsQPqmyDj1NsqQ==
506+
"@hawk.so/types@^0.3.0":
507+
version "0.3.0"
508+
resolved "https://registry.yarnpkg.com/@hawk.so/types/-/types-0.3.0.tgz#3332de27a2dd39832185b8237cb3a1bc4624ec41"
509+
integrity sha512-xM7bgVK3+JhXXIKXXDjVufhCIEuesAyA/o2E9qyq8h9zg04fTgwo/dJ8G+wJblu5W3bNNJ1dWllMgqaH1lY8bA==
510510
dependencies:
511-
"@types/mongodb" "^3.5.34"
511+
bson "^7.0.0"
512512

513-
"@hawk.so/types@^0.5.6":
514-
version "0.5.6"
515-
resolved "https://registry.yarnpkg.com/@hawk.so/types/-/types-0.5.6.tgz#1fbd06a79de32595936c817ff416471c0767bd5a"
516-
integrity sha512-oPoi0Zf2GZDh0OdEd+imw9VAIJcp9zwtk3jLVBOvXcX+LbTKOt0kwkcblacQpsTFB1ljleVQ15gULnV3qbHCLw==
513+
"@hawk.so/types@^0.5.8":
514+
version "0.5.8"
515+
resolved "https://registry.yarnpkg.com/@hawk.so/types/-/types-0.5.8.tgz#4278b489f77b5b0335a04ae8184f87c2112116d0"
516+
integrity sha512-3LebU/fFWFCVBHcj8yAyZqjjam9vYo7diRi8BlMBXJ5yC1fE7M44+Zb+lzudHQnysj+ZcHZyBuA/dEpGhB7vxg==
517517
dependencies:
518518
bson "^7.0.0"
519519

@@ -1373,13 +1373,6 @@
13731373
"@types/connect" "*"
13741374
"@types/node" "*"
13751375

1376-
"@types/bson@*":
1377-
version "4.2.0"
1378-
resolved "https://registry.yarnpkg.com/@types/bson/-/bson-4.2.0.tgz#a2f71e933ff54b2c3bf267b67fa221e295a33337"
1379-
integrity sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==
1380-
dependencies:
1381-
bson "*"
1382-
13831376
"@types/connect@*":
13841377
version "3.4.35"
13851378
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
@@ -1602,14 +1595,6 @@
16021595
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.0.tgz#e9a9903894405c6a6551f1774df4e64d9804d69c"
16031596
integrity sha512-fccbsHKqFDXClBZTDLA43zl0+TbxyIwyzIzwwhvoJvhNjOErCdeX2xJbURimv2EbSVUGav001PaCJg4mZxMl4w==
16041597

1605-
"@types/mongodb@^3.5.34":
1606-
version "3.6.20"
1607-
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.20.tgz#b7c5c580644f6364002b649af1c06c3c0454e1d2"
1608-
integrity sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==
1609-
dependencies:
1610-
"@types/bson" "*"
1611-
"@types/node" "*"
1612-
16131598
"@types/morgan@^1.9.10":
16141599
version "1.9.10"
16151600
resolved "https://registry.yarnpkg.com/@types/morgan/-/morgan-1.9.10.tgz#725c15d95a5e6150237524cd713bc2d68f9edf1a"
@@ -2488,7 +2473,7 @@ bser@2.1.1:
24882473
dependencies:
24892474
node-int64 "^0.4.0"
24902475

2491-
bson@*, bson@^1.1.4, bson@^6.10.4, bson@^6.7.0, bson@^7.0.0:
2476+
bson@^1.1.4, bson@^6.10.4, bson@^6.7.0, bson@^7.0.0:
24922477
version "6.10.4"
24932478
resolved "https://registry.yarnpkg.com/bson/-/bson-6.10.4.tgz#d530733bb5bb16fb25c162e01a3344fab332fd2b"
24942479
integrity sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==

0 commit comments

Comments
 (0)