elixir.tsx 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import {Fragment} from 'react';
  2. import ExternalLink from 'sentry/components/links/externalLink';
  3. import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/step';
  4. import type {
  5. Docs,
  6. DocsParams,
  7. OnboardingConfig,
  8. } from 'sentry/components/onboarding/gettingStartedDoc/types';
  9. import {
  10. getCrashReportGenericInstallStep,
  11. getCrashReportModalConfigDescription,
  12. getCrashReportModalIntroduction,
  13. } from 'sentry/components/onboarding/gettingStartedDoc/utils/feedbackOnboarding';
  14. import replayOnboardingJsLoader from 'sentry/gettingStartedDocs/javascript/jsLoader/jsLoader';
  15. import {t, tct} from 'sentry/locale';
  16. type Params = DocsParams;
  17. const getInstallSnippet = () => `
  18. defp deps do
  19. [
  20. # ...
  21. {:sentry, "~> 8.0"},
  22. {:jason, "~> 1.1"},
  23. {:hackney, "~> 1.8"},
  24. # if you are using plug_cowboy
  25. {:plug_cowboy, "~> 2.3"}
  26. ]
  27. end`;
  28. const getConfigureSnippet = (params: Params) => `
  29. config :sentry,
  30. dsn: "${params.dsn}",
  31. environment_name: :prod,
  32. enable_source_code_context: true,
  33. root_source_code_path: File.cwd!(),
  34. tags: %{
  35. env: "production"
  36. },
  37. included_environments: [:prod]`;
  38. const getConfigureSnippetMixEnv = (params: Params) => `
  39. config :sentry, dsn: "${params.dsn}",
  40. included_environments: [:prod],
  41. environment_name: Mix.env`;
  42. const getCustomEnvironmentNameSnippet = (params: Params) => `
  43. config :sentry, dsn: "${params.dsn}",
  44. included_environments: ~w(production staging),
  45. environment_name: System.get_env("RELEASE_LEVEL") || "development"`;
  46. const getConfigureRouterSnippet = () => `
  47. # Phoenix
  48. use Sentry.PlugCapture
  49. use Phoenix.Endpoint, otp_app: :my_app
  50. # ...
  51. plug Plug.Parsers,
  52. parsers: [:urlencoded, :multipart, :json],
  53. pass: ["*/*"],
  54. json_decoder: Phoenix.json_library()
  55. plug Sentry.PlugContext
  56. # Plug
  57. use Plug.Router
  58. use Sentry.PlugCapture
  59. # ...
  60. plug Plug.Parsers,
  61. parsers: [:urlencoded, :multipart, :json],
  62. pass: ["*/*"],
  63. json_decoder: Phoenix.json_library()
  64. plug Sentry.PlugContext`;
  65. const getCaptureExceptionSnippet = () => `
  66. # lib/my_app/application.ex
  67. def start(_type, _args) do
  68. Logger.add_backend(Sentry.LoggerBackend)`;
  69. const getCaptureErrorsSnippet = () => `
  70. try do
  71. ThisWillError.really()
  72. rescue
  73. my_exception ->
  74. Sentry.capture_exception(my_exception, [stacktrace: __STACKTRACE__, extra: %{extra: information}])
  75. end`;
  76. const onboarding: OnboardingConfig = {
  77. install: () => [
  78. {
  79. type: StepType.INSTALL,
  80. description: tct(
  81. 'Edit your [mixCode:mix.exs] file to add it as a dependency and add the [sentryCode::sentry] package to your applications:',
  82. {sentryCode: <code />, mixCode: <code />}
  83. ),
  84. configurations: [
  85. {
  86. language: 'elixir',
  87. description: <p>{tct('Install [code:sentry-sdk]:', {code: <code />})}</p>,
  88. code: getInstallSnippet(),
  89. },
  90. ],
  91. },
  92. ],
  93. configure: params => [
  94. {
  95. type: StepType.CONFIGURE,
  96. description: tct(
  97. 'Setup the application production environment in your [code:config/prod.exs]',
  98. {
  99. code: <code />,
  100. }
  101. ),
  102. configurations: [
  103. {
  104. language: 'elixir',
  105. code: getConfigureSnippet(params),
  106. },
  107. {
  108. description: (
  109. <Fragment>
  110. <p>
  111. {tct(
  112. 'The [environmentNameCode:environment_name] and [includedEnvironmentsCode:included_environments] work together to determine if and when Sentry should record exceptions. The [environmentNameCode:environment_name] is the name of the current environment. In the example above, we have explicitly set the environment to [prodCode::prod] which works well if you are inside an environment specific configuration like [configCode:config/prod.exs].',
  113. {
  114. environmentNameCode: <code />,
  115. includedEnvironmentsCode: <code />,
  116. prodCode: <code />,
  117. configCode: <code />,
  118. }
  119. )}
  120. </p>
  121. <p>
  122. {tct(
  123. 'An alternative is to use [code:Mix.env] in your general configuration file:',
  124. {code: <code />}
  125. )}
  126. </p>
  127. </Fragment>
  128. ),
  129. configurations: [
  130. {
  131. language: 'elixir',
  132. code: getConfigureSnippetMixEnv(params),
  133. },
  134. ],
  135. },
  136. {
  137. description: (
  138. <Fragment>
  139. <p>
  140. {tct(
  141. 'This will set the environment name to whatever the current Mix environment atom is, but it will only send events if the current environment is [prodCode::prod], since that is the only entry in the [includedEnvironmentsCode:included_environments] key.',
  142. {
  143. prodCode: <code />,
  144. includedEnvironmentsCode: <code />,
  145. }
  146. )}
  147. </p>
  148. {t(
  149. "You can even rely on more custom determinations of the environment name. It's not uncommon for most applications to have a 'staging' environment. In order to handle this without adding an additional Mix environment, you can set an environment variable that determines the release level."
  150. )}
  151. </Fragment>
  152. ),
  153. language: 'elixir',
  154. code: getCustomEnvironmentNameSnippet(params),
  155. },
  156. {
  157. description: (
  158. <Fragment>
  159. <p>
  160. {tct(
  161. "In this example, we are getting the environment name from the [code:RELEASE_LEVEL] environment variable. If that variable does not exist, it will default to [code:'development']. Now, on our servers, we can set the environment variable appropriately. On our local development machines, exceptions will never be sent, because the default value is not in the list of [code:included_environments].",
  162. {
  163. code: <code />,
  164. }
  165. )}
  166. </p>
  167. <p>
  168. {tct(
  169. 'If using an environment with Plug or Phoenix, add the following to [plugRouterCode:Plug.Router] or [phoenixEndpointCode:Phoenix.Endpoint]:',
  170. {plugRouterCode: <code />, phoenixEndpointCode: <code />}
  171. )}
  172. </p>
  173. </Fragment>
  174. ),
  175. language: 'elixir',
  176. code: getConfigureRouterSnippet(),
  177. additionalInfo: tct(
  178. '[sentryPlugContextCode:Sentry.PlugContext] gathers the contextual information for errors, and [sentryPlugCaptureCode:Sentry.PlugCapture] captures and sends any errors that occur in the Plug stack. [sentryPlugContextCode:Sentry.PlugContext] should be below [sentryPlugParsersCode:Plug.Parsers] if you are using it.',
  179. {
  180. sentryPlugCaptureCode: <code />,
  181. sentryPlugContextCode: <code />,
  182. sentryPlugParsersCode: <code />,
  183. }
  184. ),
  185. },
  186. ],
  187. },
  188. {
  189. title: t('Capture Crashed Process Exceptions'),
  190. description: tct(
  191. 'This library comes with an extension to capture all error messages that the Plug handler might not. This is based on [link:Logger.Backend]. You can add it as a backend when your application starts:',
  192. {
  193. link: (
  194. <ExternalLink href="https://hexdocs.pm/logger/Logger.html#module-backends" />
  195. ),
  196. }
  197. ),
  198. configurations: [
  199. {
  200. language: 'elixir',
  201. code: getCaptureExceptionSnippet(),
  202. },
  203. ],
  204. },
  205. {
  206. title: t('Capturing Errors'),
  207. description: (
  208. <Fragment>
  209. {t(
  210. 'If you use the LoggerBackend and set up the Plug/Phoenix integrations, all errors will bubble up to Sentry.'
  211. )}
  212. <p>{t('Otherwise, we provide a simple way to capture exceptions manually:')}</p>
  213. </Fragment>
  214. ),
  215. configurations: [
  216. {
  217. language: 'elixir',
  218. code: getCaptureErrorsSnippet(),
  219. },
  220. ],
  221. },
  222. ],
  223. verify: () => [],
  224. };
  225. const crashReportOnboarding: OnboardingConfig = {
  226. introduction: () => getCrashReportModalIntroduction(),
  227. install: (params: Params) => getCrashReportGenericInstallStep(params),
  228. configure: () => [
  229. {
  230. type: StepType.CONFIGURE,
  231. description: getCrashReportModalConfigDescription({
  232. link: 'https://docs.sentry.io/platforms/elixir/user-feedback/configuration/#crash-report-modal',
  233. }),
  234. },
  235. ],
  236. verify: () => [],
  237. nextSteps: () => [],
  238. };
  239. const docs: Docs = {
  240. onboarding,
  241. replayOnboardingJsLoader,
  242. crashReportOnboarding,
  243. };
  244. export default docs;