koa.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  2. import type {
  3. Docs,
  4. DocsParams,
  5. OnboardingConfig,
  6. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  7. import {getUploadSourceMapsStep} from 'sentry/components/onboarding/gettingStartedDoc/utils';
  8. import {
  9. getCrashReportApiIntroduction,
  10. getCrashReportInstallDescription,
  11. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  12. import {getJSServerMetricsOnboarding} from 'sentry/components/onboarding/gettingStartedDoc/utils/metricsOnboarding';
  13. import {ProductSolution} from 'sentry/components/onboarding/productSelection';
  14. import {t, tct} from 'sentry/locale';
  15. import type {ProductSelectionMap} from 'sentry/utils/gettingStartedDocs/node';
  16. import {
  17. getDefaultNodeImports,
  18. getInstallConfig,
  19. } from 'sentry/utils/gettingStartedDocs/node';
  20. type Params = DocsParams;
  21. const productSelection = (params: Params): ProductSelectionMap => {
  22. return {
  23. [ProductSolution.ERROR_MONITORING]: true,
  24. [ProductSolution.PROFILING]: params.isProfilingSelected,
  25. [ProductSolution.PERFORMANCE_MONITORING]: params.isPerformanceSelected,
  26. [ProductSolution.SESSION_REPLAY]: params.isReplaySelected,
  27. };
  28. };
  29. const getSdkSetupSnippet = (params: Params) => `
  30. ${getDefaultNodeImports({productSelection: productSelection(params)}).join('\n')}
  31. import { stripUrlQueryAndFragment } from "@sentry/utils";
  32. import Koa from "koa";
  33. const app = new Koa();
  34. Sentry.init({
  35. dsn: "${params.dsn}",
  36. integrations: [${
  37. params.isPerformanceSelected
  38. ? `
  39. // Automatically instrument Node.js libraries and frameworks
  40. ...Sentry.autoDiscoverNodePerformanceMonitoringIntegrations(),`
  41. : ''
  42. }${
  43. params.isProfilingSelected
  44. ? `
  45. new ProfilingIntegration(),`
  46. : ''
  47. }
  48. ],${
  49. params.isPerformanceSelected
  50. ? `
  51. // Performance Monitoring
  52. tracesSampleRate: 1.0, // Capture 100% of the transactions`
  53. : ''
  54. }${
  55. params.isProfilingSelected
  56. ? `
  57. // Set sampling rate for profiling - this is relative to tracesSampleRate
  58. profilesSampleRate: 1.0,`
  59. : ''
  60. }
  61. });${
  62. params.isPerformanceSelected
  63. ? `
  64. const requestHandler = (ctx, next) => {
  65. return new Promise((resolve, reject) => {
  66. Sentry.runWithAsyncContext(async () => {
  67. const hub = Sentry.getCurrentHub();
  68. hub.configureScope((scope) =>
  69. scope.addEventProcessor((event) =>
  70. Sentry.addRequestDataToEvent(event, ctx.request, {
  71. include: {
  72. user: false,
  73. },
  74. })
  75. )
  76. );
  77. try {
  78. await next();
  79. } catch (err) {
  80. reject(err);
  81. }
  82. resolve();
  83. });
  84. });
  85. };
  86. // This tracing middleware creates a transaction per request
  87. const tracingMiddleWare = async (ctx, next) => {
  88. const reqMethod = (ctx.method || "").toUpperCase();
  89. const reqUrl = ctx.url && stripUrlQueryAndFragment(ctx.url);
  90. // Connect to trace of upstream app
  91. let traceparentData;
  92. if (ctx.request.get("sentry-trace")) {
  93. traceparentData = Sentry.extractTraceparentData(
  94. ctx.request.get("sentry-trace")
  95. );
  96. }
  97. const transaction = Sentry.startTransaction({
  98. name: \`\${reqMethod} \${reqUrl}\`,
  99. op: "http.server",
  100. ...traceparentData,
  101. });
  102. ctx.__sentry_transaction = transaction;
  103. // We put the transaction on the scope so users can attach children to it
  104. Sentry.getCurrentHub().configureScope((scope) => {
  105. scope.setSpan(transaction);
  106. });
  107. ctx.res.on("finish", () => {
  108. // Push \`transaction.finish\` to the next event loop so open spans have a chance to finish before the transaction closes
  109. setImmediate(() => {
  110. // If you're using koa router, set the matched route as transaction name
  111. if (ctx._matchedRoute) {
  112. const mountPath = ctx.mountPath || "";
  113. transaction.setName(\`\${reqMethod} \${mountPath}\${ctx._matchedRoute}\`);
  114. }
  115. transaction.setHttpStatus(ctx.status);
  116. transaction.finish();
  117. });
  118. });
  119. await next();
  120. };
  121. app.use(requestHandler);
  122. app.use(tracingMiddleWare);`
  123. : ''
  124. }
  125. // Send errors to Sentry
  126. app.on("error", (err, ctx) => {
  127. Sentry.withScope((scope) => {
  128. scope.addEventProcessor((event) => {
  129. return Sentry.addRequestDataToEvent(event, ctx.request);
  130. });
  131. Sentry.captureException(err);
  132. });
  133. });
  134. app.listen(3000);`;
  135. const getVerifySnippet = () => `
  136. app.use(async function () {
  137. throw new Error("My first Sentry error!");
  138. });
  139. `;
  140. const onboarding: OnboardingConfig = {
  141. install: params => [
  142. {
  143. type: StepType.INSTALL,
  144. description: t('Add the Sentry Node SDK as a dependency:'),
  145. configurations: getInstallConfig(params, {
  146. additionalPackages: params.isPerformanceSelected ? ['@sentry/utils'] : [],
  147. }),
  148. },
  149. ],
  150. configure: params => [
  151. {
  152. type: StepType.CONFIGURE,
  153. description: tct(
  154. "Initialize Sentry as early as possible in your application's lifecycle, for example in your [code:index.ts/js] entry point:",
  155. {code: <code />}
  156. ),
  157. configurations: [
  158. {
  159. language: 'javascript',
  160. code: getSdkSetupSnippet(params),
  161. },
  162. ],
  163. },
  164. getUploadSourceMapsStep({
  165. guideLink: 'https://docs.sentry.io/platforms/node/guides/koa/sourcemaps/',
  166. ...params,
  167. }),
  168. ],
  169. verify: () => [
  170. {
  171. type: StepType.VERIFY,
  172. description: t(
  173. "This snippet contains an intentional error and can be used as a test to make sure that everything's working as expected."
  174. ),
  175. configurations: [
  176. {
  177. language: 'javascript',
  178. code: getVerifySnippet(),
  179. },
  180. ],
  181. },
  182. ],
  183. };
  184. const feedbackOnboardingNode: OnboardingConfig = {
  185. introduction: () => getCrashReportApiIntroduction(),
  186. install: () => [
  187. {
  188. type: StepType.INSTALL,
  189. description: getCrashReportInstallDescription(),
  190. configurations: [
  191. {
  192. code: [
  193. {
  194. label: 'JavaScript',
  195. value: 'javascript',
  196. language: 'javascript',
  197. code: `import * as Sentry from "@sentry/node";
  198. const eventId = Sentry.captureMessage("User Feedback");
  199. // OR: const eventId = Sentry.lastEventId();
  200. const userFeedback = {
  201. event_id: eventId,
  202. name: "John Doe",
  203. email: "john@doe.com",
  204. comments: "I really like your App, thanks!",
  205. };
  206. Sentry.captureUserFeedback(userFeedback);
  207. `,
  208. },
  209. ],
  210. },
  211. ],
  212. },
  213. ],
  214. configure: () => [],
  215. verify: () => [],
  216. nextSteps: () => [],
  217. };
  218. const docs: Docs = {
  219. onboarding,
  220. feedbackOnboardingCrashApi: feedbackOnboardingNode,
  221. customMetricsOnboarding: getJSServerMetricsOnboarding(),
  222. };
  223. export default docs;