elixir.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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 replayOnboardingJsLoader from 'sentry/gettingStartedDocs/javascript/jsLoader/jsLoader';
  10. import {t, tct} from 'sentry/locale';
  11. type Params = DocsParams;
  12. const getInstallSnippet = () => `
  13. defp deps do
  14. [
  15. # ...
  16. {:sentry, "~> 8.0"},
  17. {:jason, "~> 1.1"},
  18. {:hackney, "~> 1.8"},
  19. # if you are using plug_cowboy
  20. {:plug_cowboy, "~> 2.3"}
  21. ]
  22. end`;
  23. const getConfigureSnippet = (params: Params) => `
  24. config :sentry,
  25. dsn: "${params.dsn}",
  26. environment_name: :prod,
  27. enable_source_code_context: true,
  28. root_source_code_path: File.cwd!(),
  29. tags: %{
  30. env: "production"
  31. },
  32. included_environments: [:prod]`;
  33. const getConfigureSnippetMixEnv = (params: Params) => `
  34. config :sentry, dsn: "${params.dsn}",
  35. included_environments: [:prod],
  36. environment_name: Mix.env`;
  37. const getCustomEnvironmentNameSnippet = (params: Params) => `
  38. config :sentry, dsn: "${params.dsn}",
  39. included_environments: ~w(production staging),
  40. environment_name: System.get_env("RELEASE_LEVEL") || "development"`;
  41. const getConfigureRouterSnippet = () => `
  42. # Phoenix
  43. use Sentry.PlugCapture
  44. use Phoenix.Endpoint, otp_app: :my_app
  45. # ...
  46. plug Plug.Parsers,
  47. parsers: [:urlencoded, :multipart, :json],
  48. pass: ["*/*"],
  49. json_decoder: Phoenix.json_library()
  50. plug Sentry.PlugContext
  51. # Plug
  52. use Plug.Router
  53. use Sentry.PlugCapture
  54. # ...
  55. plug Plug.Parsers,
  56. parsers: [:urlencoded, :multipart, :json],
  57. pass: ["*/*"],
  58. json_decoder: Phoenix.json_library()
  59. plug Sentry.PlugContext`;
  60. const getCaptureExceptionSnippet = () => `
  61. # lib/my_app/application.ex
  62. def start(_type, _args) do
  63. Logger.add_backend(Sentry.LoggerBackend)`;
  64. const getCaptureErrorsSnippet = () => `
  65. try do
  66. ThisWillError.really()
  67. rescue
  68. my_exception ->
  69. Sentry.capture_exception(my_exception, [stacktrace: __STACKTRACE__, extra: %{extra: information}])
  70. end`;
  71. const onboarding: OnboardingConfig = {
  72. install: () => [
  73. {
  74. type: StepType.INSTALL,
  75. description: tct(
  76. 'Edit your [mixCode:mix.exs] file to add it as a dependency and add the [sentryCode::sentry] package to your applications:',
  77. {sentryCode: <code />, mixCode: <code />}
  78. ),
  79. configurations: [
  80. {
  81. language: 'elixir',
  82. description: <p>{tct('Install [code:sentry-sdk]:', {code: <code />})}</p>,
  83. code: getInstallSnippet(),
  84. },
  85. ],
  86. },
  87. ],
  88. configure: params => [
  89. {
  90. type: StepType.CONFIGURE,
  91. description: tct(
  92. 'Setup the application production environment in your [code:config/prod.exs]',
  93. {
  94. code: <code />,
  95. }
  96. ),
  97. configurations: [
  98. {
  99. language: 'elixir',
  100. code: getConfigureSnippet(params),
  101. },
  102. {
  103. description: (
  104. <Fragment>
  105. <p>
  106. {tct(
  107. '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].',
  108. {
  109. environmentNameCode: <code />,
  110. includedEnvironmentsCode: <code />,
  111. prodCode: <code />,
  112. configCode: <code />,
  113. }
  114. )}
  115. </p>
  116. <p>
  117. {tct(
  118. 'An alternative is to use [code:Mix.env] in your general configuration file:',
  119. {code: <code />}
  120. )}
  121. </p>
  122. </Fragment>
  123. ),
  124. configurations: [
  125. {
  126. language: 'elixir',
  127. code: getConfigureSnippetMixEnv(params),
  128. },
  129. ],
  130. },
  131. {
  132. description: (
  133. <Fragment>
  134. <p>
  135. {tct(
  136. '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.',
  137. {
  138. prodCode: <code />,
  139. includedEnvironmentsCode: <code />,
  140. }
  141. )}
  142. </p>
  143. {t(
  144. "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."
  145. )}
  146. </Fragment>
  147. ),
  148. language: 'elixir',
  149. code: getCustomEnvironmentNameSnippet(params),
  150. },
  151. {
  152. description: (
  153. <Fragment>
  154. <p>
  155. {tct(
  156. "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].",
  157. {
  158. code: <code />,
  159. }
  160. )}
  161. </p>
  162. <p>
  163. {tct(
  164. 'If using an environment with Plug or Phoenix, add the following to [plugRouterCode:Plug.Router] or [phoenixEndpointCode:Phoenix.Endpoint]:',
  165. {plugRouterCode: <code />, phoenixEndpointCode: <code />}
  166. )}
  167. </p>
  168. </Fragment>
  169. ),
  170. language: 'elixir',
  171. code: getConfigureRouterSnippet(),
  172. additionalInfo: tct(
  173. '[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.',
  174. {
  175. sentryPlugCaptureCode: <code />,
  176. sentryPlugContextCode: <code />,
  177. sentryPlugParsersCode: <code />,
  178. }
  179. ),
  180. },
  181. ],
  182. },
  183. {
  184. title: t('Capture Crashed Process Exceptions'),
  185. description: tct(
  186. '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:',
  187. {
  188. link: (
  189. <ExternalLink href="https://hexdocs.pm/logger/Logger.html#module-backends" />
  190. ),
  191. }
  192. ),
  193. configurations: [
  194. {
  195. language: 'elixir',
  196. code: getCaptureExceptionSnippet(),
  197. },
  198. ],
  199. },
  200. {
  201. title: t('Capturing Errors'),
  202. description: (
  203. <Fragment>
  204. {t(
  205. 'If you use the LoggerBackend and set up the Plug/Phoenix integrations, all errors will bubble up to Sentry.'
  206. )}
  207. <p>{t('Otherwise, we provide a simple way to capture exceptions manually:')}</p>
  208. </Fragment>
  209. ),
  210. configurations: [
  211. {
  212. language: 'elixir',
  213. code: getCaptureErrorsSnippet(),
  214. },
  215. ],
  216. },
  217. ],
  218. verify: () => [],
  219. };
  220. const docs: Docs = {
  221. onboarding,
  222. replayOnboardingJsLoader,
  223. };
  224. export default docs;