koa.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. import {Layout, LayoutProps} from 'sentry/components/onboarding/gettingStartedDoc/layout';
  2. import {ModuleProps} from 'sentry/components/onboarding/gettingStartedDoc/sdkDocumentation';
  3. import {StepProps, StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  4. import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
  5. import {t, tct} from 'sentry/locale';
  6. import {
  7. getDefaultInitParams,
  8. getDefaultNodeImports,
  9. getInstallSnippet,
  10. getProductInitParams,
  11. getProductIntegrations,
  12. getProductSelectionMap,
  13. joinWithIndentation,
  14. } from 'sentry/utils/gettingStartedDocs/node';
  15. interface StepsParams {
  16. hasPerformanceMonitoring: boolean;
  17. importContent: string;
  18. initContent: string;
  19. installSnippetNpm: string;
  20. installSnippetYarn: string;
  21. sourceMapStep: StepProps;
  22. }
  23. const performanceIntegrations: string[] = [
  24. '// Automatically instrument Node.js libraries and frameworks',
  25. '...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),',
  26. ];
  27. export const steps = ({
  28. installSnippetYarn,
  29. installSnippetNpm,
  30. importContent,
  31. initContent,
  32. hasPerformanceMonitoring,
  33. sourceMapStep,
  34. }: StepsParams): LayoutProps['steps'] => [
  35. {
  36. type: StepType.INSTALL,
  37. description: t('Add the Sentry Node SDK as a dependency:'),
  38. configurations: [
  39. {
  40. code: [
  41. {
  42. label: 'npm',
  43. value: 'npm',
  44. language: 'bash',
  45. code: installSnippetNpm,
  46. },
  47. {
  48. label: 'yarn',
  49. value: 'yarn',
  50. language: 'bash',
  51. code: installSnippetYarn,
  52. },
  53. ],
  54. },
  55. ],
  56. },
  57. {
  58. type: StepType.CONFIGURE,
  59. description: (
  60. <p>
  61. {tct(
  62. "Initialize Sentry as early as possible in your application's lifecycle, for example in your [code:index.ts/js] entry point:",
  63. {code: <code />}
  64. )}
  65. </p>
  66. ),
  67. configurations: [
  68. {
  69. language: 'javascript',
  70. code: `
  71. ${importContent}
  72. const app = new Koa();
  73. Sentry.init({
  74. ${initContent}
  75. });${
  76. hasPerformanceMonitoring
  77. ? `
  78. const requestHandler = (ctx, next) => {
  79. return new Promise((resolve, reject) => {
  80. Sentry.runWithAsyncContext(async () => {
  81. const hub = Sentry.getCurrentHub();
  82. hub.configureScope((scope) =>
  83. scope.addEventProcessor((event) =>
  84. Sentry.addRequestDataToEvent(event, ctx.request, {
  85. include: {
  86. user: false,
  87. },
  88. })
  89. )
  90. );
  91. try {
  92. await next();
  93. } catch (err) {
  94. reject(err);
  95. }
  96. resolve();
  97. });
  98. });
  99. };
  100. // This tracing middleware creates a transaction per request
  101. const tracingMiddleWare = async (ctx, next) => {
  102. const reqMethod = (ctx.method || "").toUpperCase();
  103. const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);
  104. // Connect to trace of upstream app
  105. let traceparentData;
  106. if (ctx.request.get("sentry-trace")) {
  107. traceparentData = Sentry.extractTraceparentData(
  108. ctx.request.get("sentry-trace")
  109. );
  110. }
  111. const transaction = Sentry.startTransaction({
  112. name: \`\${reqMethod} \${reqUrl}\`,
  113. op: "http.server",
  114. ...traceparentData,
  115. });
  116. ctx.__sentry_transaction = transaction;
  117. // We put the transaction on the scope so users can attach children to it
  118. Sentry.getCurrentHub().configureScope((scope) => {
  119. scope.setSpan(transaction);
  120. });
  121. ctx.res.on("finish", () => {
  122. // Push \`transaction.finish\` to the next event loop so open spans have a chance to finish before the transaction closes
  123. setImmediate(() => {
  124. // If you're using koa router, set the matched route as transaction name
  125. if (ctx._matchedRoute) {
  126. const mountPath = ctx.mountPath || "";
  127. transaction.setName(\`\${reqMethod} \${mountPath}\${ctx._matchedRoute}\`);
  128. }
  129. transaction.setHttpStatus(ctx.status);
  130. transaction.finish();
  131. });
  132. });
  133. await next();
  134. };
  135. app.use(requestHandler);
  136. app.use(tracingMiddleWare);`
  137. : ''
  138. }
  139. // Send errors to Sentry
  140. app.on("error", (err, ctx) => {
  141. Sentry.withScope((scope) => {
  142. scope.addEventProcessor((event) => {
  143. return Sentry.addRequestDataToEvent(event, ctx.request);
  144. });
  145. Sentry.captureException(err);
  146. });
  147. });
  148. app.listen(3000);
  149. `,
  150. },
  151. ],
  152. },
  153. sourceMapStep,
  154. {
  155. type: StepType.VERIFY,
  156. description: t(
  157. "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
  158. ),
  159. configurations: [
  160. {
  161. language: 'javascript',
  162. code: `
  163. app.use(async function () {
  164. throw new Error("My first Sentry error!");
  165. });
  166. `,
  167. },
  168. ],
  169. },
  170. ];
  171. export function GettingStartedWithKoa({
  172. dsn,
  173. newOrg,
  174. platformKey,
  175. activeProductSelection = [],
  176. organization,
  177. projectId,
  178. ...props
  179. }: ModuleProps) {
  180. const productSelection = getProductSelectionMap(activeProductSelection);
  181. const additionalPackages = productSelection['performance-monitoring']
  182. ? ['@sentry/utils']
  183. : [];
  184. let imports = getDefaultNodeImports({productSelection});
  185. imports = imports.concat([
  186. 'import { stripUrlQueryAndFragment } from "@sentry/utils";',
  187. 'import Koa from "koa";',
  188. ]);
  189. const integrations = [
  190. ...(productSelection['performance-monitoring'] ? performanceIntegrations : []),
  191. ...getProductIntegrations({productSelection}),
  192. ];
  193. const integrationParam =
  194. integrations.length > 0
  195. ? `integrations: [\n${joinWithIndentation(integrations)}\n],`
  196. : null;
  197. const initContent = joinWithIndentation([
  198. ...getDefaultInitParams({dsn}),
  199. ...(integrationParam ? [integrationParam] : []),
  200. ...getProductInitParams({productSelection}),
  201. ]);
  202. return (
  203. <Layout
  204. steps={steps({
  205. installSnippetNpm: getInstallSnippet({
  206. additionalPackages,
  207. productSelection,
  208. packageManager: 'npm',
  209. }),
  210. installSnippetYarn: getInstallSnippet({
  211. additionalPackages,
  212. productSelection,
  213. packageManager: 'yarn',
  214. }),
  215. importContent: imports.join('\n'),
  216. initContent,
  217. hasPerformanceMonitoring: productSelection['performance-monitoring'],
  218. sourceMapStep: getUploadSourceMapsStep({
  219. guideLink: 'https://docs.sentry.io/platforms/node/guides/koa/sourcemaps/',
  220. organization,
  221. platformKey,
  222. projectId,
  223. newOrg,
  224. }),
  225. })}
  226. newOrg={newOrg}
  227. platformKey={platformKey}
  228. {...props}
  229. />
  230. );
  231. }
  232. export default GettingStartedWithKoa;