Skip to content

Commit 9e9f5f3

Browse files
committed
finish transaction when response finishes, await all the things
1 parent d0e6297 commit 9e9f5f3

File tree

1 file changed

+39
-12
lines changed

1 file changed

+39
-12
lines changed

packages/nextjs/src/utils/handlers.ts

+39-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
22
import { extractTraceparentData, getActiveTransaction, hasTracingEnabled } from '@sentry/tracing';
33
import { addExceptionMechanism, isString, logger, stripUrlQueryAndFragment } from '@sentry/utils';
4-
import { NextApiHandler } from 'next';
4+
import { NextApiHandler, NextApiResponse } from 'next';
55

66
import { addRequestDataToEvent, NextRequest } from './instrumentServer';
77

@@ -51,6 +51,8 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
5151
{ request: req },
5252
);
5353
currentScope.setSpan(transaction);
54+
55+
res.on('finish', async () => await finishTransaction(res));
5456
}
5557
}
5658

@@ -65,19 +67,44 @@ export const withSentry = (handler: NextApiHandler): WrappedNextApiHandler => {
6567
});
6668
captureException(e);
6769
});
70+
await finishTransaction(res);
6871
throw e;
69-
} finally {
70-
const transaction = getActiveTransaction();
71-
if (transaction) {
72-
transaction.setHttpStatus(res.statusCode);
72+
}
73+
};
74+
};
7375

74-
transaction.finish();
75-
}
76+
async function finishTransaction(res: NextApiResponse): Promise<void> {
77+
const transaction = getActiveTransaction();
78+
79+
if (!transaction) {
80+
// nothing to do
81+
return Promise.resolve();
82+
}
83+
84+
// now that we have the transaction, pop it off of the scope so it doesn't affect future requests
85+
// TODO use domains?
86+
getCurrentHub()
87+
.getScope()
88+
?.setSpan(undefined);
89+
90+
transaction.setHttpStatus(res.statusCode);
91+
92+
const finishPromise = new Promise<void>((resolve, reject) => {
93+
// Push `transaction.finish` to the next event loop so open spans have a chance to finish before the
94+
// transaction closes
95+
setImmediate(async () => {
96+
transaction.finish();
7697
try {
98+
logger.log('Flushing event buffer');
7799
await flush(2000);
78-
} catch (e) {
79-
// no-empty
100+
logger.log('Buffer flushed');
101+
resolve();
102+
} catch (err) {
103+
logger.log('Error while flushing buffer:', err);
104+
reject(err);
80105
}
81-
}
82-
};
83-
};
106+
});
107+
});
108+
109+
return finishPromise;
110+
}

0 commit comments

Comments
 (0)