datadog-frontend-toolkit 1.0.58 → 1.0.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,7 +25,7 @@ function buildDashboardPayload(service, env, team) {
25
25
  {
26
26
  definition: {
27
27
  type: 'note',
28
- content: `# ${service} Frontend Observability\n**Auto-provisioned** by datadog-frontend-toolkit — use the \`$env\` dropdown above to filter by environment`,
28
+ content: `# ${service} Frontend Observability\n**Auto-provisioned** by datadog-frontend-toolkit — use the \`$env\` dropdown above to filter by environment.\n\n**Top section:** Key signals to detect production issues at a glance. **Bottom section:** Full observability detail for deep investigation.`,
29
29
  background_color: 'blue',
30
30
  font_size: '16',
31
31
  text_align: 'left',
@@ -34,39 +34,56 @@ function buildDashboardPayload(service, env, team) {
34
34
  tick_pos: '50%',
35
35
  },
36
36
  },
37
- // RUM Overview Group
37
+ // ═══════════════════════════════════════════════════════════════
38
+ // SECTION 1: Production Health — concise, actionable, error-focused
39
+ // ═══════════════════════════════════════════════════════════════
38
40
  {
39
41
  definition: {
40
42
  type: 'group',
41
- title: 'Real User Monitoring',
43
+ title: '🚨 Production Health — Key Error Signals',
42
44
  layout_type: 'ordered',
43
45
  widgets: [
46
+ // Row 1: Critical numbers at a glance
44
47
  {
45
48
  definition: {
46
49
  type: 'query_value',
47
- title: 'Active Sessions',
50
+ title: '⚠️ Error Rate',
48
51
  requests: [
49
52
  {
50
53
  response_format: 'scalar',
51
54
  queries: [
52
55
  {
53
56
  data_source: 'rum',
54
- name: 'query1',
55
- compute: { aggregation: 'cardinality', metric: '@session.id' },
56
- search: { query: `service:${service} $env` },
57
+ name: 'errors',
58
+ compute: { aggregation: 'count' },
59
+ search: { query: `service:${service} $env @type:error` },
57
60
  indexes: ['*'],
58
61
  },
62
+ {
63
+ data_source: 'rum',
64
+ name: 'total',
65
+ compute: { aggregation: 'count' },
66
+ search: { query: `service:${service} $env @type:view` },
67
+ indexes: ['*'],
68
+ },
69
+ ],
70
+ formulas: [{ formula: '(errors / total) * 100' }],
71
+ conditional_formats: [
72
+ { comparator: '>', value: 5, palette: 'white_on_red' },
73
+ { comparator: '>', value: 2, palette: 'white_on_yellow' },
74
+ { comparator: '<=', value: 2, palette: 'white_on_green' },
59
75
  ],
60
76
  },
61
77
  ],
62
78
  autoscale: true,
63
- precision: 0,
79
+ precision: 2,
80
+ custom_unit: '%',
64
81
  },
65
82
  },
66
83
  {
67
84
  definition: {
68
85
  type: 'query_value',
69
- title: 'Page Views (1h)',
86
+ title: '🐛 JS Errors',
70
87
  requests: [
71
88
  {
72
89
  response_format: 'scalar',
@@ -75,10 +92,15 @@ function buildDashboardPayload(service, env, team) {
75
92
  data_source: 'rum',
76
93
  name: 'query1',
77
94
  compute: { aggregation: 'count' },
78
- search: { query: `service:${service} $env @type:view` },
95
+ search: { query: `service:${service} $env @type:error` },
79
96
  indexes: ['*'],
80
97
  },
81
98
  ],
99
+ conditional_formats: [
100
+ { comparator: '>', value: 100, palette: 'white_on_red' },
101
+ { comparator: '>', value: 20, palette: 'white_on_yellow' },
102
+ { comparator: '<=', value: 20, palette: 'white_on_green' },
103
+ ],
82
104
  },
83
105
  ],
84
106
  autoscale: true,
@@ -88,123 +110,117 @@ function buildDashboardPayload(service, env, team) {
88
110
  {
89
111
  definition: {
90
112
  type: 'query_value',
91
- title: 'Error Rate',
113
+ title: '🔌 Failed API Calls (4xx/5xx)',
92
114
  requests: [
93
115
  {
94
116
  response_format: 'scalar',
95
117
  queries: [
96
118
  {
97
119
  data_source: 'rum',
98
- name: 'errors',
99
- compute: { aggregation: 'count' },
100
- search: { query: `service:${service} $env @type:error` },
101
- indexes: ['*'],
102
- },
103
- {
104
- data_source: 'rum',
105
- name: 'total',
120
+ name: 'query1',
106
121
  compute: { aggregation: 'count' },
107
- search: { query: `service:${service} $env @type:view` },
122
+ search: { query: `service:${service} $env @type:resource @resource.type:(xhr OR fetch) @resource.status_code:>=400` },
108
123
  indexes: ['*'],
109
124
  },
110
125
  ],
111
- formulas: [{ formula: '(errors / total) * 100' }],
126
+ conditional_formats: [
127
+ { comparator: '>', value: 50, palette: 'white_on_red' },
128
+ { comparator: '>', value: 10, palette: 'white_on_yellow' },
129
+ { comparator: '<=', value: 10, palette: 'white_on_green' },
130
+ ],
112
131
  },
113
132
  ],
114
133
  autoscale: true,
115
- precision: 2,
116
- custom_unit: '%',
134
+ precision: 0,
117
135
  },
118
136
  },
119
- ],
120
- },
121
- },
122
- // Web Vitals Group
123
- {
124
- definition: {
125
- type: 'group',
126
- title: 'Web Vitals',
127
- layout_type: 'ordered',
128
- widgets: [
129
137
  {
130
138
  definition: {
131
- type: 'timeseries',
132
- title: 'Largest Contentful Paint (LCP)',
139
+ type: 'query_value',
140
+ title: '😡 Rage Clicks',
133
141
  requests: [
134
142
  {
135
- response_format: 'timeseries',
143
+ response_format: 'scalar',
136
144
  queries: [
137
145
  {
138
146
  data_source: 'rum',
139
147
  name: 'query1',
140
- compute: { aggregation: 'pc75', metric: '@view.largest_contentful_paint' },
141
- search: { query: `service:${service} $env @type:view` },
148
+ compute: { aggregation: 'count' },
149
+ search: { query: `service:${service} $env @type:action @action.type:click @action.frustration.type:rage_click` },
142
150
  indexes: ['*'],
143
- group_by: [],
144
151
  },
145
152
  ],
153
+ conditional_formats: [
154
+ { comparator: '>', value: 20, palette: 'white_on_red' },
155
+ { comparator: '>', value: 5, palette: 'white_on_yellow' },
156
+ { comparator: '<=', value: 5, palette: 'white_on_green' },
157
+ ],
146
158
  },
147
159
  ],
148
- markers: [
149
- { value: 'y = 2500', display_type: 'warning dashed', label: 'Good threshold' },
150
- { value: 'y = 4000', display_type: 'error dashed', label: 'Poor threshold' },
151
- ],
160
+ autoscale: true,
161
+ precision: 0,
152
162
  },
153
163
  },
154
164
  {
155
165
  definition: {
156
- type: 'timeseries',
157
- title: 'Cumulative Layout Shift (CLS)',
166
+ type: 'query_value',
167
+ title: '⏱️ LCP (p75)',
158
168
  requests: [
159
169
  {
160
- response_format: 'timeseries',
170
+ response_format: 'scalar',
161
171
  queries: [
162
172
  {
163
173
  data_source: 'rum',
164
174
  name: 'query1',
165
- compute: { aggregation: 'pc75', metric: '@view.cumulative_layout_shift' },
175
+ compute: { aggregation: 'pc75', metric: '@view.largest_contentful_paint' },
166
176
  search: { query: `service:${service} $env @type:view` },
167
177
  indexes: ['*'],
168
- group_by: [],
169
178
  },
170
179
  ],
180
+ conditional_formats: [
181
+ { comparator: '>', value: 4000, palette: 'white_on_red' },
182
+ { comparator: '>', value: 2500, palette: 'white_on_yellow' },
183
+ { comparator: '<=', value: 2500, palette: 'white_on_green' },
184
+ ],
171
185
  },
172
186
  ],
173
- markers: [
174
- { value: 'y = 0.1', display_type: 'warning dashed', label: 'Good threshold' },
175
- { value: 'y = 0.25', display_type: 'error dashed', label: 'Poor threshold' },
176
- ],
187
+ autoscale: true,
188
+ precision: 0,
189
+ custom_unit: 'ms',
177
190
  },
178
191
  },
179
192
  {
180
193
  definition: {
181
- type: 'timeseries',
182
- title: 'Interaction to Next Paint (INP)',
194
+ type: 'query_value',
195
+ title: '👥 Affected Sessions',
183
196
  requests: [
184
197
  {
185
- response_format: 'timeseries',
198
+ response_format: 'scalar',
186
199
  queries: [
187
200
  {
188
201
  data_source: 'rum',
189
202
  name: 'query1',
190
- compute: { aggregation: 'pc75', metric: '@view.interaction_to_next_paint' },
191
- search: { query: `service:${service} $env @type:view` },
203
+ compute: { aggregation: 'cardinality', metric: '@session.id' },
204
+ search: { query: `service:${service} $env @type:error` },
192
205
  indexes: ['*'],
193
- group_by: [],
194
206
  },
195
207
  ],
208
+ conditional_formats: [
209
+ { comparator: '>', value: 50, palette: 'white_on_red' },
210
+ { comparator: '>', value: 10, palette: 'white_on_yellow' },
211
+ { comparator: '<=', value: 10, palette: 'white_on_green' },
212
+ ],
196
213
  },
197
214
  ],
198
- markers: [
199
- { value: 'y = 200', display_type: 'warning dashed', label: 'Good threshold' },
200
- { value: 'y = 500', display_type: 'error dashed', label: 'Poor threshold' },
201
- ],
215
+ autoscale: true,
216
+ precision: 0,
202
217
  },
203
218
  },
219
+ // Row 2: Error timeline — are errors spiking?
204
220
  {
205
221
  definition: {
206
222
  type: 'timeseries',
207
- title: 'First Contentful Paint (FCP)',
223
+ title: '📈 Error Trend (is it spiking?)',
208
224
  requests: [
209
225
  {
210
226
  response_format: 'timeseries',
@@ -212,37 +228,24 @@ function buildDashboardPayload(service, env, team) {
212
228
  {
213
229
  data_source: 'rum',
214
230
  name: 'query1',
215
- compute: { aggregation: 'pc75', metric: '@view.first_contentful_paint' },
216
- search: { query: `service:${service} $env @type:view` },
231
+ compute: { aggregation: 'count' },
232
+ search: { query: `service:${service} $env @type:error` },
217
233
  indexes: ['*'],
218
- group_by: [],
234
+ group_by: [{ facet: '@error.source', limit: 10, sort: { aggregation: 'count', order: 'desc' } }],
219
235
  },
220
236
  ],
221
237
  },
222
238
  ],
223
- markers: [
224
- { value: 'y = 1800', display_type: 'warning dashed', label: 'Good threshold' },
225
- { value: 'y = 3000', display_type: 'error dashed', label: 'Poor threshold' },
226
- ],
227
239
  },
228
240
  },
229
- ],
230
- },
231
- },
232
- // Errors Group
233
- {
234
- definition: {
235
- type: 'group',
236
- title: 'Errors & Logs',
237
- layout_type: 'ordered',
238
- widgets: [
241
+ // Row 3: What exactly is breaking?
239
242
  {
240
243
  definition: {
241
- type: 'timeseries',
242
- title: 'Errors Over Time',
244
+ type: 'toplist',
245
+ title: '🔴 Top Errors What is breaking?',
243
246
  requests: [
244
247
  {
245
- response_format: 'timeseries',
248
+ response_format: 'scalar',
246
249
  queries: [
247
250
  {
248
251
  data_source: 'rum',
@@ -250,9 +253,10 @@ function buildDashboardPayload(service, env, team) {
250
253
  compute: { aggregation: 'count' },
251
254
  search: { query: `service:${service} $env @type:error` },
252
255
  indexes: ['*'],
253
- group_by: [{ facet: '@error.source', limit: 10, sort: { aggregation: 'count', order: 'desc' } }],
256
+ group_by: [{ facet: '@error.message', limit: 10, sort: { aggregation: 'count', order: 'desc' } }],
254
257
  },
255
258
  ],
259
+ formulas: [{ formula: 'query1' }],
256
260
  },
257
261
  ],
258
262
  },
@@ -260,7 +264,7 @@ function buildDashboardPayload(service, env, team) {
260
264
  {
261
265
  definition: {
262
266
  type: 'toplist',
263
- title: 'Top Errors',
267
+ title: '🔌 Failing API Endpoints',
264
268
  requests: [
265
269
  {
266
270
  response_format: 'scalar',
@@ -269,9 +273,9 @@ function buildDashboardPayload(service, env, team) {
269
273
  data_source: 'rum',
270
274
  name: 'query1',
271
275
  compute: { aggregation: 'count' },
272
- search: { query: `service:${service} $env @type:error` },
276
+ search: { query: `service:${service} $env @type:resource @resource.type:(xhr OR fetch) @resource.status_code:>=400` },
273
277
  indexes: ['*'],
274
- group_by: [{ facet: '@error.message', limit: 10, sort: { aggregation: 'count', order: 'desc' } }],
278
+ group_by: [{ facet: '@resource.url', limit: 10, sort: { aggregation: 'count', order: 'desc' } }],
275
279
  },
276
280
  ],
277
281
  formulas: [{ formula: 'query1' }],
@@ -279,11 +283,12 @@ function buildDashboardPayload(service, env, team) {
279
283
  ],
280
284
  },
281
285
  },
286
+ // Row 4: Recent logs — what happened last?
282
287
  {
283
288
  definition: {
284
289
  type: 'log_stream',
285
- title: 'Recent Error Logs',
286
- query: `service:${service} $env status:(error OR critical)`,
290
+ title: '📋 Recent Error Logs',
291
+ query: `service:${service} env:${env} status:(error OR critical)`,
287
292
  columns: ['timestamp', 'message', 'status'],
288
293
  indexes: ['*'],
289
294
  message_display: 'expanded-md',
@@ -295,17 +300,54 @@ function buildDashboardPayload(service, env, team) {
295
300
  ],
296
301
  },
297
302
  },
298
- // API Endpoint Errors Group
303
+ // ═══════════════════════════════════════════════════════════════
304
+ // SECTION 2: Detailed Observability — deep-dive investigation
305
+ // ═══════════════════════════════════════════════════════════════
306
+ {
307
+ definition: {
308
+ type: 'note',
309
+ content: '---\n## 📊 Detailed Observability\nThe sections below provide full diagnostic data for deep investigation: traffic volume, Web Vitals trends, API error breakdown, geo performance, browser/device analysis, user frustration patterns, and resource loading.',
310
+ background_color: 'gray',
311
+ font_size: '14',
312
+ text_align: 'left',
313
+ show_tick: false,
314
+ tick_edge: 'left',
315
+ tick_pos: '50%',
316
+ },
317
+ },
318
+ // Traffic Overview Group
299
319
  {
300
320
  definition: {
301
321
  type: 'group',
302
- title: 'API Endpoint Errors',
322
+ title: 'Traffic Overview',
303
323
  layout_type: 'ordered',
304
324
  widgets: [
305
325
  {
306
326
  definition: {
307
- type: 'toplist',
308
- title: 'Top Failing Endpoints (by count)',
327
+ type: 'query_value',
328
+ title: 'Active Sessions',
329
+ requests: [
330
+ {
331
+ response_format: 'scalar',
332
+ queries: [
333
+ {
334
+ data_source: 'rum',
335
+ name: 'query1',
336
+ compute: { aggregation: 'cardinality', metric: '@session.id' },
337
+ search: { query: `service:${service} $env` },
338
+ indexes: ['*'],
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ autoscale: true,
344
+ precision: 0,
345
+ },
346
+ },
347
+ {
348
+ definition: {
349
+ type: 'query_value',
350
+ title: 'Page Views',
309
351
  requests: [
310
352
  {
311
353
  response_format: 'scalar',
@@ -314,16 +356,136 @@ function buildDashboardPayload(service, env, team) {
314
356
  data_source: 'rum',
315
357
  name: 'query1',
316
358
  compute: { aggregation: 'count' },
317
- search: { query: `service:${service} $env @type:resource @resource.type:(xhr OR fetch) @resource.status_code:>=400` },
359
+ search: { query: `service:${service} $env @type:view` },
318
360
  indexes: ['*'],
319
- group_by: [{ facet: '@resource.url', limit: 25, sort: { aggregation: 'count', order: 'desc' } }],
320
361
  },
321
362
  ],
322
- formulas: [{ formula: 'query1' }],
323
363
  },
324
364
  ],
365
+ autoscale: true,
366
+ precision: 0,
367
+ },
368
+ },
369
+ ],
370
+ },
371
+ },
372
+ // Web Vitals Group
373
+ {
374
+ definition: {
375
+ type: 'group',
376
+ title: 'Web Vitals',
377
+ layout_type: 'ordered',
378
+ widgets: [
379
+ {
380
+ definition: {
381
+ type: 'timeseries',
382
+ title: 'Largest Contentful Paint (LCP)',
383
+ requests: [
384
+ {
385
+ response_format: 'timeseries',
386
+ queries: [
387
+ {
388
+ data_source: 'rum',
389
+ name: 'query1',
390
+ compute: { aggregation: 'pc75', metric: '@view.largest_contentful_paint' },
391
+ search: { query: `service:${service} $env @type:view` },
392
+ indexes: ['*'],
393
+ group_by: [],
394
+ },
395
+ ],
396
+ },
397
+ ],
398
+ markers: [
399
+ { value: 'y = 2500', display_type: 'warning dashed', label: 'Good threshold' },
400
+ { value: 'y = 4000', display_type: 'error dashed', label: 'Poor threshold' },
401
+ ],
325
402
  },
326
403
  },
404
+ {
405
+ definition: {
406
+ type: 'timeseries',
407
+ title: 'Cumulative Layout Shift (CLS)',
408
+ requests: [
409
+ {
410
+ response_format: 'timeseries',
411
+ queries: [
412
+ {
413
+ data_source: 'rum',
414
+ name: 'query1',
415
+ compute: { aggregation: 'pc75', metric: '@view.cumulative_layout_shift' },
416
+ search: { query: `service:${service} $env @type:view` },
417
+ indexes: ['*'],
418
+ group_by: [],
419
+ },
420
+ ],
421
+ },
422
+ ],
423
+ markers: [
424
+ { value: 'y = 0.1', display_type: 'warning dashed', label: 'Good threshold' },
425
+ { value: 'y = 0.25', display_type: 'error dashed', label: 'Poor threshold' },
426
+ ],
427
+ },
428
+ },
429
+ {
430
+ definition: {
431
+ type: 'timeseries',
432
+ title: 'Interaction to Next Paint (INP)',
433
+ requests: [
434
+ {
435
+ response_format: 'timeseries',
436
+ queries: [
437
+ {
438
+ data_source: 'rum',
439
+ name: 'query1',
440
+ compute: { aggregation: 'pc75', metric: '@view.interaction_to_next_paint' },
441
+ search: { query: `service:${service} $env @type:view` },
442
+ indexes: ['*'],
443
+ group_by: [],
444
+ },
445
+ ],
446
+ },
447
+ ],
448
+ markers: [
449
+ { value: 'y = 200', display_type: 'warning dashed', label: 'Good threshold' },
450
+ { value: 'y = 500', display_type: 'error dashed', label: 'Poor threshold' },
451
+ ],
452
+ },
453
+ },
454
+ {
455
+ definition: {
456
+ type: 'timeseries',
457
+ title: 'First Contentful Paint (FCP)',
458
+ requests: [
459
+ {
460
+ response_format: 'timeseries',
461
+ queries: [
462
+ {
463
+ data_source: 'rum',
464
+ name: 'query1',
465
+ compute: { aggregation: 'pc75', metric: '@view.first_contentful_paint' },
466
+ search: { query: `service:${service} $env @type:view` },
467
+ indexes: ['*'],
468
+ group_by: [],
469
+ },
470
+ ],
471
+ },
472
+ ],
473
+ markers: [
474
+ { value: 'y = 1800', display_type: 'warning dashed', label: 'Good threshold' },
475
+ { value: 'y = 3000', display_type: 'error dashed', label: 'Poor threshold' },
476
+ ],
477
+ },
478
+ },
479
+ ],
480
+ },
481
+ },
482
+ // API Endpoint Errors Group
483
+ {
484
+ definition: {
485
+ type: 'group',
486
+ title: 'API Endpoint Errors',
487
+ layout_type: 'ordered',
488
+ widgets: [
327
489
  {
328
490
  definition: {
329
491
  type: 'toplist',
@@ -597,57 +759,13 @@ function buildDashboardPayload(service, env, team) {
597
759
  ],
598
760
  },
599
761
  },
600
- // Frustrated Sessions Group
762
+ // User Frustration Group
601
763
  {
602
764
  definition: {
603
765
  type: 'group',
604
766
  title: 'User Frustration',
605
767
  layout_type: 'ordered',
606
768
  widgets: [
607
- {
608
- definition: {
609
- type: 'query_value',
610
- title: 'Frustrated Sessions',
611
- requests: [
612
- {
613
- response_format: 'scalar',
614
- queries: [
615
- {
616
- data_source: 'rum',
617
- name: 'query1',
618
- compute: { aggregation: 'cardinality', metric: '@session.id' },
619
- search: { query: `service:${service} $env @type:error` },
620
- indexes: ['*'],
621
- },
622
- ],
623
- },
624
- ],
625
- autoscale: true,
626
- precision: 0,
627
- },
628
- },
629
- {
630
- definition: {
631
- type: 'query_value',
632
- title: 'Rage Clicks',
633
- requests: [
634
- {
635
- response_format: 'scalar',
636
- queries: [
637
- {
638
- data_source: 'rum',
639
- name: 'query1',
640
- compute: { aggregation: 'count' },
641
- search: { query: `service:${service} $env @type:action @action.type:click @action.frustration.type:rage_click` },
642
- indexes: ['*'],
643
- },
644
- ],
645
- },
646
- ],
647
- autoscale: true,
648
- precision: 0,
649
- },
650
- },
651
769
  {
652
770
  definition: {
653
771
  type: 'timeseries',