koa.tsx 7.2 KB

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