rollbar 2.26.4 → 3.0.0-alpha.2

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.
Files changed (140) hide show
  1. package/.claude/settings.local.json +3 -0
  2. package/.cursor/rules/guidelines.mdc +154 -0
  3. package/.github/workflows/ci.yml +4 -6
  4. package/CLAUDE.local.md +297 -0
  5. package/CLAUDE.md +201 -0
  6. package/CLAUDE.testrunner.md +470 -0
  7. package/Gruntfile.js +59 -16
  8. package/Makefile +3 -3
  9. package/SECURITY.md +5 -0
  10. package/babel.config.json +9 -0
  11. package/codex.md +148 -0
  12. package/dist/plugins/jquery.min.js +1 -1
  13. package/dist/rollbar.js +19332 -6596
  14. package/dist/rollbar.js.map +1 -1
  15. package/dist/rollbar.min.js +2 -1
  16. package/dist/rollbar.min.js.LICENSE.txt +1 -0
  17. package/dist/rollbar.min.js.map +1 -1
  18. package/dist/rollbar.named-amd.js +19332 -6596
  19. package/dist/rollbar.named-amd.js.map +1 -1
  20. package/dist/rollbar.named-amd.min.js +2 -1
  21. package/dist/rollbar.named-amd.min.js.LICENSE.txt +1 -0
  22. package/dist/rollbar.named-amd.min.js.map +1 -1
  23. package/dist/rollbar.noconflict.umd.js +19319 -6581
  24. package/dist/rollbar.noconflict.umd.js.map +1 -1
  25. package/dist/rollbar.noconflict.umd.min.js +2 -1
  26. package/dist/rollbar.noconflict.umd.min.js.LICENSE.txt +1 -0
  27. package/dist/rollbar.noconflict.umd.min.js.map +1 -1
  28. package/dist/rollbar.snippet.js +1 -1
  29. package/dist/rollbar.umd.js +19333 -6597
  30. package/dist/rollbar.umd.js.map +1 -1
  31. package/dist/rollbar.umd.min.js +2 -1
  32. package/dist/rollbar.umd.min.js.LICENSE.txt +1 -0
  33. package/dist/rollbar.umd.min.js.map +1 -1
  34. package/eslint.config.mjs +33 -0
  35. package/karma.conf.js +5 -14
  36. package/package.json +19 -20
  37. package/src/api.js +57 -4
  38. package/src/apiUtility.js +2 -3
  39. package/src/browser/core.js +37 -9
  40. package/src/browser/replay/defaults.js +70 -0
  41. package/src/browser/replay/recorder.js +194 -0
  42. package/src/browser/replay/replayMap.js +195 -0
  43. package/src/browser/rollbar.js +11 -7
  44. package/src/browser/telemetry.js +3 -3
  45. package/src/browser/transport/fetch.js +17 -4
  46. package/src/browser/transport/xhr.js +17 -1
  47. package/src/browser/transport.js +11 -8
  48. package/src/defaults.js +1 -1
  49. package/src/queue.js +65 -4
  50. package/src/react-native/rollbar.js +1 -1
  51. package/src/rollbar.js +52 -10
  52. package/src/server/rollbar.js +3 -2
  53. package/src/telemetry.js +76 -11
  54. package/src/tracing/context.js +24 -0
  55. package/src/tracing/contextManager.js +37 -0
  56. package/src/tracing/defaults.js +7 -0
  57. package/src/tracing/exporter.js +188 -0
  58. package/src/tracing/hrtime.js +98 -0
  59. package/src/tracing/id.js +24 -0
  60. package/src/tracing/session.js +55 -0
  61. package/src/tracing/span.js +92 -0
  62. package/src/tracing/spanProcessor.js +15 -0
  63. package/src/tracing/tracer.js +46 -0
  64. package/src/tracing/tracing.js +89 -0
  65. package/src/utility.js +34 -0
  66. package/test/api.test.js +57 -12
  67. package/test/apiUtility.test.js +5 -6
  68. package/test/browser.core.test.js +1 -10
  69. package/test/browser.domUtility.test.js +1 -1
  70. package/test/browser.predicates.test.js +1 -1
  71. package/test/browser.replay.recorder.test.js +430 -0
  72. package/test/browser.rollbar.test.js +58 -12
  73. package/test/browser.telemetry.test.js +1 -1
  74. package/test/browser.transforms.test.js +20 -13
  75. package/test/browser.transport.test.js +5 -4
  76. package/test/browser.url.test.js +1 -1
  77. package/test/fixtures/replay/index.js +20 -0
  78. package/test/fixtures/replay/payloads.fixtures.js +229 -0
  79. package/test/fixtures/replay/rrwebEvents.fixtures.js +251 -0
  80. package/test/fixtures/replay/rrwebSyntheticEvents.fixtures.js +328 -0
  81. package/test/notifier.test.js +1 -1
  82. package/test/predicates.test.js +1 -1
  83. package/test/queue.test.js +1 -1
  84. package/test/rateLimiter.test.js +1 -1
  85. package/test/react-native.rollbar.test.js +1 -1
  86. package/test/react-native.transforms.test.js +2 -2
  87. package/test/react-native.transport.test.js +3 -3
  88. package/test/replay/index.js +2 -0
  89. package/test/replay/integration/api.spans.test.js +136 -0
  90. package/test/replay/integration/e2e.test.js +228 -0
  91. package/test/replay/integration/index.js +9 -0
  92. package/test/replay/integration/queue.replayMap.test.js +332 -0
  93. package/test/replay/integration/replayMap.test.js +163 -0
  94. package/test/replay/integration/sessionRecording.test.js +390 -0
  95. package/test/replay/unit/api.postSpans.test.js +150 -0
  96. package/test/replay/unit/index.js +7 -0
  97. package/test/replay/unit/queue.replayMap.test.js +225 -0
  98. package/test/replay/unit/replayMap.test.js +348 -0
  99. package/test/replay/util/index.js +5 -0
  100. package/test/replay/util/mockRecordFn.js +80 -0
  101. package/test/server.lambda.mocha.test.mjs +172 -0
  102. package/test/server.locals.constructor.mocha.test.mjs +80 -0
  103. package/test/server.locals.error-handling.mocha.test.mjs +387 -0
  104. package/test/server.locals.merge.mocha.test.mjs +267 -0
  105. package/test/server.locals.test-utils.mjs +114 -0
  106. package/test/server.parser.mocha.test.mjs +87 -0
  107. package/test/server.predicates.mocha.test.mjs +63 -0
  108. package/test/server.rollbar.constructor.mocha.test.mjs +199 -0
  109. package/test/server.rollbar.handlers.mocha.test.mjs +253 -0
  110. package/test/server.rollbar.logging.mocha.test.mjs +326 -0
  111. package/test/server.rollbar.misc.mocha.test.mjs +44 -0
  112. package/test/server.rollbar.test-utils.mjs +57 -0
  113. package/test/server.telemetry.mocha.test.mjs +377 -0
  114. package/test/server.transforms.data.mocha.test.mjs +163 -0
  115. package/test/server.transforms.error.mocha.test.mjs +199 -0
  116. package/test/server.transforms.request.mocha.test.mjs +208 -0
  117. package/test/server.transforms.scrub.mocha.test.mjs +140 -0
  118. package/test/server.transforms.sourcemaps.mocha.test.mjs +122 -0
  119. package/test/server.transforms.test-utils.mjs +62 -0
  120. package/test/server.transport.mocha.test.mjs +269 -0
  121. package/test/telemetry.test.js +132 -1
  122. package/test/tracing/contextManager.test.js +28 -0
  123. package/test/tracing/exporter.toPayload.test.js +400 -0
  124. package/test/tracing/id.test.js +24 -0
  125. package/test/tracing/span.test.js +183 -0
  126. package/test/tracing/spanProcessor.test.js +73 -0
  127. package/test/tracing/tracing.test.js +105 -0
  128. package/test/transforms.test.js +2 -2
  129. package/test/truncation.test.js +2 -2
  130. package/test/utility.test.js +44 -6
  131. package/webpack.config.js +6 -44
  132. package/.eslintignore +0 -7
  133. package/test/server.lambda.test.js +0 -194
  134. package/test/server.locals.test.js +0 -1068
  135. package/test/server.parser.test.js +0 -78
  136. package/test/server.predicates.test.js +0 -91
  137. package/test/server.rollbar.test.js +0 -728
  138. package/test/server.telemetry.test.js +0 -443
  139. package/test/server.transforms.test.js +0 -1193
  140. package/test/server.transport.test.js +0 -269
@@ -0,0 +1,400 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+ /* globals beforeEach */
4
+ /* globals afterEach */
5
+
6
+ import { expect } from 'chai';
7
+ import sinon from 'sinon';
8
+ import { EventType } from '@rrweb/types';
9
+
10
+ import { SpanExporter, spanExportQueue } from '../../src/tracing/exporter.js';
11
+ import hrtime from '../../src/tracing/hrtime.js';
12
+ import id from '../../src/tracing/id.js';
13
+ import { standardPayload } from '../fixtures/replay/payloads.fixtures.js';
14
+
15
+ describe('SpanExporter.toPayload()', function () {
16
+ let exporter;
17
+ let hrtimeStub;
18
+ let idStub;
19
+
20
+ beforeEach(function () {
21
+ spanExportQueue.length = 0;
22
+ exporter = new SpanExporter();
23
+
24
+ hrtimeStub = sinon.stub(hrtime, 'now').returns([1, 2]);
25
+ sinon.stub(hrtime, 'toNanos').returns(1000000000);
26
+ idStub = sinon.stub(id, 'gen').returns('1234567890abcdef');
27
+ });
28
+
29
+ afterEach(function () {
30
+ spanExportQueue.length = 0;
31
+ sinon.restore();
32
+ });
33
+
34
+ it('should return empty resourceSpans when queue is empty', function () {
35
+ const payload = exporter.toPayload();
36
+ expect(payload).to.deep.equal({ resourceSpans: [] });
37
+ });
38
+
39
+ it('should transform a simple span into OTLP format', function () {
40
+ const mockSpan = {
41
+ name: 'test-span',
42
+ spanContext: {
43
+ traceId: 'abcdef1234567890abcdef1234567890',
44
+ spanId: '1234567890abcdef',
45
+ },
46
+ startTime: hrtime.now(),
47
+ endTime: hrtime.now(),
48
+ attributes: {
49
+ 'test.attribute': 'test-value',
50
+ },
51
+ events: [],
52
+ resource: {
53
+ attributes: {
54
+ 'service.name': 'test-service',
55
+ },
56
+ },
57
+ };
58
+
59
+ exporter.export([mockSpan]);
60
+ const payload = exporter.toPayload();
61
+
62
+ expect(payload).to.have.property('resourceSpans').that.is.an('array');
63
+ expect(payload.resourceSpans).to.have.lengthOf(1);
64
+ expect(payload.resourceSpans[0]).to.have.property('resource');
65
+ expect(payload.resourceSpans[0])
66
+ .to.have.property('scopeSpans')
67
+ .that.is.an('array');
68
+ expect(payload.resourceSpans[0].scopeSpans).to.have.lengthOf(1);
69
+
70
+ const transformedSpan = payload.resourceSpans[0].scopeSpans[0].spans[0];
71
+ expect(transformedSpan).to.have.property('name', 'test-span');
72
+ expect(transformedSpan).to.have.property(
73
+ 'traceId',
74
+ 'abcdef1234567890abcdef1234567890',
75
+ );
76
+ expect(transformedSpan).to.have.property('spanId', '1234567890abcdef');
77
+
78
+ expect(transformedSpan).to.have.property('attributes').that.is.an('array');
79
+ const attribute = transformedSpan.attributes.find(
80
+ (attr) => attr.key === 'test.attribute',
81
+ );
82
+ expect(attribute).to.exist;
83
+ expect(attribute.value).to.have.property('stringValue', 'test-value');
84
+ });
85
+
86
+ it('should handle spans with different attribute types correctly', function () {
87
+ const mockSpan = {
88
+ name: 'attribute-test-span',
89
+ spanContext: {
90
+ traceId: 'abcdef1234567890abcdef1234567890',
91
+ spanId: '1234567890abcdef',
92
+ },
93
+ startTime: hrtime.now(),
94
+ endTime: hrtime.now(),
95
+ attributes: {
96
+ stringAttr: 'string-value',
97
+ intAttr: 42,
98
+ floatAttr: 3.14,
99
+ boolAttr: true,
100
+ nullAttr: null,
101
+ arrayAttr: [1, 'two', true],
102
+ objectAttr: { nested: 'value' },
103
+ },
104
+ events: [],
105
+ resource: {
106
+ attributes: {},
107
+ },
108
+ };
109
+
110
+ exporter.export([mockSpan]);
111
+ const payload = exporter.toPayload();
112
+ const attributes =
113
+ payload.resourceSpans[0].scopeSpans[0].spans[0].attributes;
114
+
115
+ const stringAttr = attributes.find((a) => a.key === 'stringAttr');
116
+ expect(stringAttr.value).to.have.property('stringValue', 'string-value');
117
+
118
+ const intAttr = attributes.find((a) => a.key === 'intAttr');
119
+ expect(intAttr.value).to.have.property('intValue', '42');
120
+
121
+ const floatAttr = attributes.find((a) => a.key === 'floatAttr');
122
+ expect(floatAttr.value).to.have.property('doubleValue', 3.14);
123
+
124
+ const boolAttr = attributes.find((a) => a.key === 'boolAttr');
125
+ expect(boolAttr.value).to.have.property('boolValue', true);
126
+
127
+ const nullAttr = attributes.find((a) => a.key === 'nullAttr');
128
+ expect(nullAttr.value).to.have.property('stringValue', '');
129
+
130
+ const arrayAttr = attributes.find((a) => a.key === 'arrayAttr');
131
+ expect(arrayAttr.value).to.have.property('arrayValue');
132
+ expect(arrayAttr.value.arrayValue.values).to.have.lengthOf(3);
133
+
134
+ const objectAttr = attributes.find((a) => a.key === 'objectAttr');
135
+ expect(objectAttr.value).to.have.property('kvlistValue');
136
+ });
137
+
138
+ it('should handle spans with events correctly', function () {
139
+ const eventTime = hrtime.now();
140
+
141
+ const mockSpan = {
142
+ name: 'event-test-span',
143
+ spanContext: {
144
+ traceId: 'abcdef1234567890abcdef1234567890',
145
+ spanId: '1234567890abcdef',
146
+ },
147
+ startTime: hrtime.now(),
148
+ endTime: hrtime.now(),
149
+ attributes: {},
150
+ events: [
151
+ {
152
+ name: 'test-event-1',
153
+ time: eventTime,
154
+ attributes: {
155
+ 'event.key1': 'value1',
156
+ },
157
+ },
158
+ {
159
+ name: 'test-event-2',
160
+ time: eventTime,
161
+ attributes: {
162
+ 'event.key2': 'value2',
163
+ },
164
+ },
165
+ ],
166
+ resource: {
167
+ attributes: {},
168
+ },
169
+ };
170
+
171
+ exporter.export([mockSpan]);
172
+ const payload = exporter.toPayload();
173
+ const events = payload.resourceSpans[0].scopeSpans[0].spans[0].events;
174
+
175
+ expect(events).to.have.lengthOf(2);
176
+ expect(events[0]).to.have.property('name', 'test-event-1');
177
+ expect(events[1]).to.have.property('name', 'test-event-2');
178
+
179
+ expect(events[0]).to.have.property('timeUnixNano').that.is.a('number');
180
+
181
+ const eventAttribute = events[0].attributes.find(
182
+ (a) => a.key === 'event.key1',
183
+ );
184
+ expect(eventAttribute).to.exist;
185
+ expect(eventAttribute.value).to.have.property('stringValue', 'value1');
186
+ });
187
+
188
+ it('should handle multiple spans with different scopes', function () {
189
+ const span1 = {
190
+ name: 'span1',
191
+ spanContext: {
192
+ traceId: 'abcdef1234567890abcdef1234567890',
193
+ spanId: '1111111111111111',
194
+ },
195
+ startTime: hrtime.now(),
196
+ endTime: hrtime.now(),
197
+ attributes: {},
198
+ events: [],
199
+ instrumentationScope: {
200
+ name: 'scope1',
201
+ version: '1.0.0',
202
+ },
203
+ resource: {
204
+ attributes: {},
205
+ },
206
+ };
207
+
208
+ const span2 = {
209
+ name: 'span2',
210
+ spanContext: {
211
+ traceId: 'abcdef1234567890abcdef1234567890',
212
+ spanId: '2222222222222222',
213
+ },
214
+ startTime: hrtime.now(),
215
+ endTime: hrtime.now(),
216
+ attributes: {},
217
+ events: [],
218
+ instrumentationScope: {
219
+ name: 'scope2',
220
+ version: '1.0.0',
221
+ },
222
+ resource: {
223
+ attributes: {},
224
+ },
225
+ };
226
+
227
+ const span3 = {
228
+ name: 'span3',
229
+ spanContext: {
230
+ traceId: 'abcdef1234567890abcdef1234567890',
231
+ spanId: '3333333333333333',
232
+ },
233
+ startTime: hrtime.now(),
234
+ endTime: hrtime.now(),
235
+ attributes: {},
236
+ events: [],
237
+ instrumentationScope: {
238
+ name: 'scope1',
239
+ version: '1.0.0',
240
+ },
241
+ resource: {
242
+ attributes: {},
243
+ },
244
+ };
245
+
246
+ exporter.export([span1, span2, span3]);
247
+ const payload = exporter.toPayload();
248
+
249
+ expect(payload.resourceSpans).to.have.lengthOf(1);
250
+ expect(payload.resourceSpans[0].scopeSpans).to.have.lengthOf(2);
251
+
252
+ const scope1 = payload.resourceSpans[0].scopeSpans.find(
253
+ (s) => s.scope.name === 'scope1',
254
+ );
255
+ const scope2 = payload.resourceSpans[0].scopeSpans.find(
256
+ (s) => s.scope.name === 'scope2',
257
+ );
258
+
259
+ expect(scope1).to.exist;
260
+ expect(scope2).to.exist;
261
+
262
+ expect(scope1.spans).to.have.lengthOf(2);
263
+ expect(scope2.spans).to.have.lengthOf(1);
264
+ });
265
+
266
+ it('should use default scope for spans without instrumentation scope', function () {
267
+ const mockSpan = {
268
+ name: 'no-scope-span',
269
+ spanContext: {
270
+ traceId: 'abcdef1234567890abcdef1234567890',
271
+ spanId: '1234567890abcdef',
272
+ },
273
+ startTime: hrtime.now(),
274
+ endTime: hrtime.now(),
275
+ attributes: {},
276
+ events: [],
277
+ resource: {
278
+ attributes: {},
279
+ },
280
+ // No instrumentationScope property
281
+ };
282
+
283
+ exporter.export([mockSpan]);
284
+ const payload = exporter.toPayload();
285
+
286
+ const scope = payload.resourceSpans[0].scopeSpans[0].scope;
287
+ expect(scope).to.deep.equal({
288
+ name: 'default',
289
+ version: '1.0.0',
290
+ attributes: [],
291
+ });
292
+ });
293
+
294
+ it('should produce a payload compatible with standardPayload fixture', function () {
295
+ const span = {
296
+ name: 'rrweb-replay-recording',
297
+ spanContext: {
298
+ traceId: 'abcdef1234567890abcdef1234567890',
299
+ spanId: '1234567890abcdef',
300
+ },
301
+ startTime: hrtime.now(),
302
+ endTime: hrtime.now(),
303
+ attributes: {
304
+ 'rollbar.replay.id': 'test-replay-id',
305
+ },
306
+ events: [
307
+ {
308
+ name: 'rrweb-replay-events',
309
+ time: hrtime.now(),
310
+ attributes: {
311
+ eventType: String(EventType.Meta),
312
+ json: '{}',
313
+ },
314
+ },
315
+ {
316
+ name: 'rrweb-replay-events',
317
+ time: hrtime.now(),
318
+ attributes: {
319
+ eventType: String(EventType.FullSnapshot),
320
+ json: '{}',
321
+ },
322
+ },
323
+ ],
324
+ instrumentationScope: {
325
+ name: 'rollbar.js',
326
+ version: '1.0.0',
327
+ },
328
+ resource: {
329
+ attributes: {
330
+ 'service.name': 'test_service',
331
+ 'rollbar.environment': 'testenv',
332
+ },
333
+ },
334
+ };
335
+
336
+ exporter.export([span]);
337
+ const payload = exporter.toPayload();
338
+
339
+ const expected = JSON.parse(JSON.stringify(standardPayload));
340
+ const actualSpan = payload.resourceSpans[0].scopeSpans[0].spans[0];
341
+
342
+ expect(actualSpan.traceId).to.equal('abcdef1234567890abcdef1234567890');
343
+ expect(actualSpan.spanId).to.equal('1234567890abcdef');
344
+
345
+ expect(actualSpan.startTimeUnixNano).to.equal(1000000000);
346
+ expect(actualSpan.endTimeUnixNano).to.equal(1000000000);
347
+
348
+ actualSpan.events.forEach((event) => {
349
+ expect(event.timeUnixNano).to.equal(1000000000);
350
+ });
351
+
352
+ function createComparablePayload(payload) {
353
+ const clone = JSON.parse(JSON.stringify(payload));
354
+
355
+ // Remove fields with dynamic values that we verified separately
356
+ const span = clone.resourceSpans[0].scopeSpans[0].spans[0];
357
+ delete span.traceId;
358
+ delete span.spanId;
359
+ delete span.parentSpanId;
360
+ delete span.startTimeUnixNano;
361
+ delete span.endTimeUnixNano;
362
+ delete span.kind;
363
+
364
+ span.events.forEach((event) => {
365
+ delete event.timeUnixNano;
366
+ });
367
+
368
+ return clone;
369
+ }
370
+
371
+ const comparablePayload = createComparablePayload(payload);
372
+ const comparableStandard = createComparablePayload(standardPayload);
373
+
374
+ expect(
375
+ comparablePayload.resourceSpans[0].scopeSpans[0].spans[0].name,
376
+ ).to.deep.equal(
377
+ comparableStandard.resourceSpans[0].scopeSpans[0].spans[0].name,
378
+ 'Span names should match',
379
+ );
380
+
381
+ expect(
382
+ comparablePayload.resourceSpans[0].scopeSpans[0].spans[0].attributes,
383
+ ).to.deep.equal(
384
+ comparableStandard.resourceSpans[0].scopeSpans[0].spans[0].attributes,
385
+ 'Span attributes should match',
386
+ );
387
+
388
+ expect(
389
+ comparablePayload.resourceSpans[0].scopeSpans[0].spans[0].events,
390
+ ).to.deep.equal(
391
+ comparableStandard.resourceSpans[0].scopeSpans[0].spans[0].events,
392
+ 'Span events should match',
393
+ );
394
+
395
+ expect(comparablePayload).to.deep.equal(
396
+ comparableStandard,
397
+ 'Complete payload structure should match',
398
+ );
399
+ });
400
+ });
@@ -0,0 +1,24 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+ import id from '../../src/tracing/id.js';
6
+
7
+ describe('id', function () {
8
+ it('should generate random hex id of specified byte length', function (done) {
9
+ const id8 = id.gen(8);
10
+ const id16 = id.gen(16);
11
+ const id32 = id.gen(32);
12
+
13
+ expect(id8).to.match(/^[a-f0-9]{16}$/);
14
+ expect(id16).to.match(/^[a-f0-9]{32}$/);
15
+ expect(id32).to.match(/^[a-f0-9]{64}$/);
16
+
17
+ const defaultId = id.gen();
18
+ expect(defaultId).to.match(/^[a-f0-9]{32}$/);
19
+
20
+ expect(id.gen()).to.not.equal(id.gen());
21
+
22
+ done();
23
+ });
24
+ });
@@ -0,0 +1,183 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+
6
+ import { Span } from '../../src/tracing/span.js';
7
+ import { SpanExporter, spanExportQueue } from '../../src/tracing/exporter.js';
8
+ import { SpanProcessor } from '../../src/tracing/spanProcessor.js';
9
+ import { ROOT_CONTEXT } from '../../src/tracing/context.js';
10
+ import hrtime from '../../src/tracing/hrtime.js';
11
+
12
+ const spanOptions = function (options = {}) {
13
+ const exporter = new SpanExporter();
14
+ const spanProcessor = new SpanProcessor(exporter);
15
+
16
+ return {
17
+ resource: {
18
+ attributes: {
19
+ 'service.name': 'unknown_service',
20
+ 'telemetry.sdk.language': 'webjs',
21
+ 'telemetry.sdk.name': 'rollbar',
22
+ 'telemetry.sdk.version': '0.1.0',
23
+ },
24
+ },
25
+ scope: {
26
+ name: 'testScope',
27
+ version: '0.1.0',
28
+ },
29
+ session: {
30
+ id: 'ffd1f2e0e3ecaf2eaf0ac4c6c68154eb',
31
+ },
32
+ context: ROOT_CONTEXT,
33
+ spanContext: {
34
+ traceId: 'c7ab72d0c1457d0162d12dbed77a6cb4',
35
+ spanId: 'f51f610441990163',
36
+ traceFlags: 0,
37
+ traceState: '',
38
+ },
39
+ name: 'testSpan',
40
+ kind: 'internal',
41
+ parentSpanId: 'c44638e654c52eff',
42
+ spanProcessor: spanProcessor,
43
+ ...options,
44
+ };
45
+ };
46
+
47
+ const expectReadableSpan = function (span, overrides = {}) {
48
+ const expected = {
49
+ name: 'testSpan',
50
+ kind: 'internal',
51
+ spanContext: {
52
+ traceId: 'c7ab72d0c1457d0162d12dbed77a6cb4',
53
+ spanId: 'f51f610441990163',
54
+ traceFlags: 0,
55
+ traceState: '',
56
+ },
57
+ parentSpanId: 'c44638e654c52eff',
58
+ startTime: [0, 0],
59
+ endTime: [0, 0],
60
+ status: { code: 0, message: '' },
61
+ attributes: { 'session.id': 'ffd1f2e0e3ecaf2eaf0ac4c6c68154eb' },
62
+ links: [],
63
+ events: [],
64
+ duration: 0,
65
+ ended: false,
66
+ resource: {
67
+ attributes: {
68
+ 'service.name': 'unknown_service',
69
+ 'telemetry.sdk.language': 'webjs',
70
+ 'telemetry.sdk.name': 'rollbar',
71
+ 'telemetry.sdk.version': '0.1.0',
72
+ },
73
+ },
74
+ instrumentationScope: {
75
+ name: 'testScope',
76
+ version: '0.1.0',
77
+ },
78
+ droppedAttributesCount: 0,
79
+ droppedEventsCount: 0,
80
+ droppedLinksCount: 0,
81
+ ...overrides,
82
+ };
83
+
84
+ expect(span.span.name).to.equal(expected.name);
85
+ expect(span.span.kind).to.equal(expected.kind);
86
+ expect(span.span.spanContext).to.deep.equal(expected.spanContext);
87
+ expect(span.span.parentSpanId).to.equal(expected.parentSpanId);
88
+ expect(span.span.endTime).to.deep.equal(expected.endTime);
89
+ expect(span.span.status).to.deep.equal(expected.status);
90
+ expect(span.span.attributes).to.deep.equal(expected.attributes);
91
+ expect(span.span.links).to.deep.equal(expected.links);
92
+ expect(span.span.events).to.deep.equal(expected.events);
93
+ expect(span.span.duration).to.equal(expected.duration);
94
+ expect(span.span.ended).to.equal(expected.ended);
95
+ expect(span.span.resource).to.deep.equal(expected.resource);
96
+ expect(span.span.instrumentationScope).to.deep.equal(
97
+ expected.instrumentationScope,
98
+ );
99
+ expect(span.span.droppedAttributesCount).to.equal(
100
+ expected.droppedAttributesCount,
101
+ );
102
+ expect(span.span.droppedEventsCount).to.equal(expected.droppedEventsCount);
103
+ expect(span.span.droppedLinksCount).to.equal(expected.droppedLinksCount);
104
+ };
105
+
106
+ describe('Span()', function () {
107
+ it('should create a readable span', function (done) {
108
+ const overrides = [];
109
+ const span = new Span(spanOptions());
110
+
111
+ expectReadableSpan(span);
112
+
113
+ const attributes = {
114
+ key1: 'value1',
115
+ key2: 'value2',
116
+ };
117
+ span.setAttributes(attributes);
118
+
119
+ const eventTime = hrtime.now();
120
+ const events = [
121
+ {
122
+ name: 'event1',
123
+ attributes: {
124
+ key1: 'value1',
125
+ key2: 'value2',
126
+ },
127
+ },
128
+ {
129
+ name: 'event2',
130
+ attributes: {
131
+ key1: 'value1',
132
+ key2: 'value2',
133
+ },
134
+ },
135
+ ];
136
+
137
+ for (const event of events) {
138
+ span.addEvent(event.name, event.attributes, eventTime);
139
+ Object.assign(event, { time: eventTime, droppedAttributesCount: 0 });
140
+ }
141
+ Object.assign(overrides, {
142
+ attributes: {
143
+ 'session.id': 'ffd1f2e0e3ecaf2eaf0ac4c6c68154eb',
144
+ ...attributes,
145
+ },
146
+ events: events,
147
+ });
148
+
149
+ expectReadableSpan(span, overrides);
150
+
151
+ const endTime = hrtime.now();
152
+ const endAttributes = {
153
+ key3: 'value3',
154
+ key4: 'value4',
155
+ };
156
+ span.end(endAttributes, endTime);
157
+
158
+ Object.assign(overrides, {
159
+ attributes: {
160
+ ...overrides.attributes,
161
+ ...endAttributes,
162
+ },
163
+ endTime: endTime,
164
+ ended: true,
165
+ });
166
+ expectReadableSpan(span, overrides);
167
+
168
+ done();
169
+ });
170
+
171
+ it('should keep valid state', function (done) {
172
+ const span = new Span(spanOptions());
173
+ expect(span.isRecording()).to.equal(true);
174
+ expect(span.spanId).to.match(/^[a-f0-9]{16}$/);
175
+ expect(span.traceId).to.match(/^[a-f0-9]{32}$/);
176
+
177
+ span.end();
178
+ expect(span.isRecording()).to.equal(false);
179
+ expect(span.export()).to.deep.equal(span.span);
180
+
181
+ done();
182
+ });
183
+ });
@@ -0,0 +1,73 @@
1
+ /* globals describe */
2
+ /* globals it */
3
+
4
+ import { expect } from 'chai';
5
+
6
+ import { Span } from '../../src/tracing/span.js';
7
+ import { SpanExporter, spanExportQueue } from '../../src/tracing/exporter.js';
8
+ import { SpanProcessor } from '../../src/tracing/spanProcessor.js';
9
+ import { ROOT_CONTEXT } from '../../src/tracing/context.js';
10
+
11
+ const spanOptions = function (options = {}) {
12
+ return {
13
+ resource: {
14
+ attributes: {
15
+ 'service.name': 'unknown_service',
16
+ 'telemetry.sdk.language': 'webjs',
17
+ 'telemetry.sdk.name': 'rollbar',
18
+ 'telemetry.sdk.version': '0.1.0',
19
+ },
20
+ },
21
+ scope: {
22
+ name: 'testScope',
23
+ version: '0.1.0',
24
+ },
25
+ session: {
26
+ id: 'ffd1f2e0e3ecaf2eaf0ac4c6c68154eb',
27
+ },
28
+ context: ROOT_CONTEXT,
29
+ spanContext: {
30
+ traceId: 'c7ab72d0c1457d0162d12dbed77a6cb4',
31
+ spanId: 'f51f610441990163',
32
+ traceFlags: 0,
33
+ traceState: '',
34
+ },
35
+ name: 'testSpan',
36
+ kind: 'internal',
37
+ parentSpanId: 'c44638e654c52eff',
38
+ ...options,
39
+ };
40
+ }
41
+
42
+ describe('SpanProcessor()', function () {
43
+ it('should process pending spans', function (done) {
44
+ const exporter = new SpanExporter();
45
+ const spanProcessor = new SpanProcessor(exporter);
46
+
47
+ expect(spanProcessor.pendingSpans.size).to.equal(0);
48
+
49
+ const overrides = {
50
+ spanProcessor: spanProcessor,
51
+ };
52
+ const span1 = new Span(spanOptions(overrides));
53
+
54
+ Object.assign(overrides, {
55
+ spanContext: {
56
+ traceId: 'c7ab72d0c1457d0162d12dbed77a6cb5',
57
+ spanId: '41990164f51f6104',
58
+ traceFlags: 0,
59
+ traceState: '',
60
+ },
61
+ });
62
+ const span2 = new Span(spanOptions(overrides));
63
+
64
+ expect(spanProcessor.pendingSpans.size).to.equal(2);
65
+
66
+ span2.end();
67
+ span1.end();
68
+
69
+ expect(spanProcessor.pendingSpans.size).to.equal(0);
70
+
71
+ done();
72
+ });
73
+ });