|
1 | 1 | /** |
2 | 2 | * @sentry/ember - Official Sentry SDK for Ember.js |
3 | 3 | * |
4 | | - * This is a v2 Ember addon that provides Sentry error tracking and performance |
5 | | - * monitoring for Ember.js applications. |
6 | | - * |
7 | | - * ## Migration from v1 to v2 addon format |
8 | | - * |
9 | | - * ### 1. Config moved from environment.js to init() |
10 | | - * |
11 | | - * Configuration is now passed directly to `init()` instead of `config/environment.js`. |
12 | | - * |
13 | | - * ### 2. Initial load scripts must be added manually |
14 | | - * |
15 | | - * The v2 addon no longer automatically injects scripts into your HTML. |
16 | | - * For initial load performance measurement, manually add these scripts to `app/index.html`: |
17 | | - * |
18 | | - * ```html |
19 | | - * <!DOCTYPE html> |
20 | | - * <html> |
21 | | - * <head> |
22 | | - * <!-- Add at start of head for accurate initial load measurement --> |
23 | | - * <script>if(window.performance&&window.performance.mark){window.performance.mark('@sentry/ember:initial-load-start');}</script> |
24 | | - * </head> |
25 | | - * <body> |
26 | | - * {{content-for "body"}} |
27 | | - * <script src="{{rootURL}}assets/vendor.js"></script> |
28 | | - * <script src="{{rootURL}}assets/your-app.js"></script> |
29 | | - * <!-- Add at end of body for accurate initial load measurement --> |
30 | | - * <script>if(window.performance&&window.performance.mark){window.performance.mark('@sentry/ember:initial-load-end');}</script> |
31 | | - * </body> |
32 | | - * </html> |
33 | | - * ``` |
34 | | - * |
35 | | - * ### 3. Performance instrumentation requires manual setup |
36 | | - * |
37 | | - * In v1, performance was automatically instrumented via a built-in instance-initializer. |
38 | | - * In v2, create your own `app/instance-initializers/sentry-performance.ts`: |
39 | | - * |
40 | | - * ```typescript |
41 | | - * import type ApplicationInstance from '@ember/application/instance'; |
42 | | - * import { setupPerformance } from '@sentry/ember/performance'; |
43 | | - * |
44 | | - * export function initialize(appInstance: ApplicationInstance): void { |
45 | | - * setupPerformance(appInstance); |
46 | | - * } |
47 | | - * |
48 | | - * export default { initialize }; |
49 | | - * ``` |
50 | | - * |
51 | | - * ## Basic Usage |
52 | | - * |
53 | | - * ```typescript |
54 | | - * // In your app/app.ts or app/app.js |
55 | | - * import Application from '@ember/application'; |
56 | | - * import * as Sentry from '@sentry/ember'; |
57 | | - * |
58 | | - * Sentry.init({ |
59 | | - * dsn: 'YOUR_DSN_HERE', |
60 | | - * // ...other options |
61 | | - * }); |
62 | | - * |
63 | | - * export default class App extends Application { |
64 | | - * // ... |
65 | | - * } |
66 | | - * ``` |
67 | | - * |
68 | | - * ## Route Performance Instrumentation |
69 | | - * |
70 | | - * ```typescript |
71 | | - * // In your route file |
72 | | - * import Route from '@ember/routing/route'; |
73 | | - * import { instrumentRoutePerformance } from '@sentry/ember'; |
74 | | - * |
75 | | - * class MyRoute extends Route { |
76 | | - * // ... |
77 | | - * } |
78 | | - * |
79 | | - * export default instrumentRoutePerformance(MyRoute); |
80 | | - * ``` |
| 4 | + * @see {@link https://docs.sentry.io/platforms/javascript/guides/ember/ Sentry Ember Documentation} |
81 | 5 | */ |
82 | 6 |
|
83 | | -import { startSpan } from '@sentry/browser'; |
84 | | -import * as Sentry from '@sentry/browser'; |
85 | | -import { |
86 | | - applySdkMetadata, |
87 | | - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, |
88 | | - SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, |
89 | | -} from '@sentry/core'; |
90 | | - |
91 | | -import type Route from '@ember/routing/route'; |
92 | | -import type { BrowserOptions } from '@sentry/browser'; |
93 | | -import type { Client, TransactionSource } from '@sentry/core'; |
94 | | - |
95 | | -/** |
96 | | - * Inline script for marking initial load start time. |
97 | | - * Add this in a `<script>` tag at the start of `<head>` in your index.html |
98 | | - * for accurate initial load measurement. |
99 | | - * |
100 | | - * If using CSP, add `'sha256-${INITIAL_LOAD_HEAD_SCRIPT_HASH}'` to your script-src directive. |
101 | | - */ |
102 | | -export const INITIAL_LOAD_HEAD_SCRIPT = |
103 | | - "if(window.performance&&window.performance.mark){window.performance.mark('@sentry/ember:initial-load-start');}"; |
104 | | - |
105 | | -/** |
106 | | - * Inline script for marking initial load end time. |
107 | | - * Add this in a `<script>` tag at the end of `<body>` in your index.html |
108 | | - * for accurate initial load measurement. |
109 | | - * |
110 | | - * If using CSP, add `'sha256-${INITIAL_LOAD_BODY_SCRIPT_HASH}'` to your script-src directive. |
111 | | - */ |
112 | | -export const INITIAL_LOAD_BODY_SCRIPT = |
113 | | - "if(window.performance&&window.performance.mark){window.performance.mark('@sentry/ember:initial-load-end');}"; |
114 | | - |
115 | | -/** |
116 | | - * SHA-256 hash of INITIAL_LOAD_HEAD_SCRIPT for CSP script-src directive. |
117 | | - * Use as: `script-src 'sha256-rK59cvsWB8z8eOLy4JAib4tBp8c/beXTnlIRV+lYjhg=' ...` |
118 | | - */ |
119 | | -export const INITIAL_LOAD_HEAD_SCRIPT_HASH = |
120 | | - 'rK59cvsWB8z8eOLy4JAib4tBp8c/beXTnlIRV+lYjhg='; |
121 | | - |
122 | | -/** |
123 | | - * SHA-256 hash of INITIAL_LOAD_BODY_SCRIPT for CSP script-src directive. |
124 | | - * Use as: `script-src 'sha256-jax2B81eAvYZMwpds3uZwJJOraCENeDFUJKuNJau/bg=' ...` |
125 | | - */ |
126 | | -export const INITIAL_LOAD_BODY_SCRIPT_HASH = |
127 | | - 'jax2B81eAvYZMwpds3uZwJJOraCENeDFUJKuNJau/bg='; |
128 | | - |
129 | | -/** |
130 | | - * Initialize the Sentry SDK for Ember. |
131 | | - * |
132 | | - * This should be called early in your application's startup, typically in app/app.ts |
133 | | - * before your Application class is defined. |
134 | | - * |
135 | | - * @param config - Sentry browser options |
136 | | - * @returns The Sentry client instance |
137 | | - * |
138 | | - * @example |
139 | | - * ```typescript |
140 | | - * import * as Sentry from '@sentry/ember'; |
141 | | - * |
142 | | - * Sentry.init({ |
143 | | - * dsn: 'YOUR_DSN_HERE', |
144 | | - * tracesSampleRate: 1.0, |
145 | | - * }); |
146 | | - * ``` |
147 | | - */ |
148 | | -export function init(config?: BrowserOptions): Client | undefined { |
149 | | - const initConfig = { ...config }; |
150 | | - |
151 | | - applySdkMetadata(initConfig, 'ember'); |
152 | | - |
153 | | - return Sentry.init(initConfig); |
154 | | -} |
155 | | - |
156 | | -type RouteConstructor = new ( |
157 | | - ...args: ConstructorParameters<typeof Route> |
158 | | -) => Route; |
159 | | - |
160 | | -/** |
161 | | - * Decorator to instrument an Ember Route for performance monitoring. |
162 | | - * |
163 | | - * This wraps the route's lifecycle hooks (beforeModel, model, afterModel, setupController) |
164 | | - * with Sentry spans to track their performance. |
165 | | - * |
166 | | - * @param BaseRoute - The Route class to instrument |
167 | | - * @returns The instrumented Route class |
168 | | - * |
169 | | - * @example |
170 | | - * ```typescript |
171 | | - * import Route from '@ember/routing/route'; |
172 | | - * import { instrumentRoutePerformance } from '@sentry/ember'; |
173 | | - * |
174 | | - * class ApplicationRoute extends Route { |
175 | | - * async model() { |
176 | | - * return this.store.findAll('post'); |
177 | | - * } |
178 | | - * } |
179 | | - * |
180 | | - * export default instrumentRoutePerformance(ApplicationRoute); |
181 | | - * ``` |
182 | | - */ |
183 | | -export const instrumentRoutePerformance = <T extends RouteConstructor>( |
184 | | - BaseRoute: T, |
185 | | -): T => { |
186 | | - const instrumentFunction = async ( |
187 | | - op: string, |
188 | | - name: string, |
189 | | - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Route hooks have varied signatures that can't be unified with unknown |
190 | | - fn: (...args: any[]) => any, |
191 | | - args: unknown[], |
192 | | - source: TransactionSource, |
193 | | - ): Promise<unknown> => { |
194 | | - return startSpan( |
195 | | - { |
196 | | - attributes: { |
197 | | - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source, |
198 | | - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.ember', |
199 | | - }, |
200 | | - op, |
201 | | - name, |
202 | | - onlyIfParent: true, |
203 | | - }, |
204 | | - () => { |
205 | | - return fn(...args); |
206 | | - }, |
207 | | - ); |
208 | | - }; |
209 | | - |
210 | | - const routeName = BaseRoute.name; |
211 | | - |
212 | | - return { |
213 | | - // @ts-expect-error TS2545 We do not need to redefine a constructor here |
214 | | - [routeName]: class extends BaseRoute { |
215 | | - public beforeModel(...args: unknown[]): void | Promise<unknown> { |
216 | | - return instrumentFunction( |
217 | | - 'ui.ember.route.before_model', |
218 | | - this.fullRouteName, |
219 | | - super.beforeModel.bind(this), |
220 | | - args, |
221 | | - 'custom', |
222 | | - ); |
223 | | - } |
224 | | - |
225 | | - public async model(...args: unknown[]): Promise<unknown> { |
226 | | - return instrumentFunction( |
227 | | - 'ui.ember.route.model', |
228 | | - this.fullRouteName, |
229 | | - super.model.bind(this), |
230 | | - args, |
231 | | - 'custom', |
232 | | - ); |
233 | | - } |
234 | | - |
235 | | - public afterModel(...args: unknown[]): void | Promise<unknown> { |
236 | | - return instrumentFunction( |
237 | | - 'ui.ember.route.after_model', |
238 | | - this.fullRouteName, |
239 | | - super.afterModel.bind(this), |
240 | | - args, |
241 | | - 'custom', |
242 | | - ); |
243 | | - } |
244 | | - |
245 | | - public setupController(...args: unknown[]): void | Promise<unknown> { |
246 | | - return instrumentFunction( |
247 | | - 'ui.ember.route.setup_controller', |
248 | | - this.fullRouteName, |
249 | | - super.setupController.bind(this), |
250 | | - args, |
251 | | - 'custom', |
252 | | - ); |
253 | | - } |
254 | | - }, |
255 | | - }[routeName] as T; |
256 | | -}; |
257 | | - |
258 | 7 | // Re-export everything from @sentry/browser |
259 | 8 | export * from '@sentry/browser'; |
| 9 | + |
| 10 | +// Sentry-specific utilities |
| 11 | +export { |
| 12 | + INITIAL_LOAD_BODY_SCRIPT, |
| 13 | + INITIAL_LOAD_BODY_SCRIPT_HASH, |
| 14 | + INITIAL_LOAD_HEAD_SCRIPT, |
| 15 | + INITIAL_LOAD_HEAD_SCRIPT_HASH, |
| 16 | +} from './utils/sentry/constants.ts'; |
| 17 | +export { init } from './utils/sentry/init.ts'; |
| 18 | +export { instrumentRoutePerformance } from './utils/sentry/instrument-route-performance.ts'; |
| 19 | +export { |
| 20 | + _resetGlobalInstrumentation, |
| 21 | + setupPerformance, |
| 22 | +} from './utils/sentry/setup-performance.ts'; |
0 commit comments