Skip to content

Commit 439abb1

Browse files
authored
Init session attributes (#1326)
* span init with no session * detect session capable window for session init * init session attributes
1 parent bcb3f2d commit 439abb1

File tree

8 files changed

+154
-6
lines changed

8 files changed

+154
-6
lines changed

src/browser/core.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ class Rollbar {
8787
this.instrumenter.instrument();
8888
}
8989

90+
this.setSessionAttributesFromOptions(options);
91+
9092
// Used with rollbar-react for rollbar-react-native compatibility.
9193
this.rollbar = this;
9294
}
@@ -112,6 +114,7 @@ class Rollbar {
112114
if (options.logLevel) {
113115
logger.init({ logLevel: options.logLevel });
114116
}
117+
this.setSessionAttributesFromOptions(options);
115118
var oldOptions = this.options;
116119
var payload = {};
117120
if (payloadData) {
@@ -434,6 +437,32 @@ class Rollbar {
434437
return this.client.captureEvent(event.type, event.metadata, event.level);
435438
};
436439

440+
setSessionUser(user) {
441+
if (!this.tracing?.session) return;
442+
443+
this.tracing.session.setUser(user);
444+
}
445+
446+
setSessionAttributes(attrs) {
447+
if (!this.tracing?.session) return;
448+
449+
attrs = { ...attrs };
450+
451+
this.tracing.session.setAttributes(attrs);
452+
}
453+
454+
setSessionAttributesFromOptions(options) {
455+
if (options.person) {
456+
this.setSessionUser(options.person);
457+
}
458+
const code_version = options.client?.javascript?.code_version || options.codeVersion || options.code_version;
459+
this.setSessionAttributes({
460+
code_version,
461+
'notifier.name': 'rollbar-browser-js',
462+
'notifier.version': options.version,
463+
});
464+
}
465+
437466
// The following two methods are used internally and are not meant for public use
438467
captureDomContentLoaded(e, ts) {
439468
if (!ts) {

src/tracing/session.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ export class Session {
1313
this._attributes = {};
1414
}
1515

16-
init() {
16+
init(attrs = {}) {
1717
if (this.session) {
1818
return this;
1919
}
20-
return this.getSession() || this.createSession();
20+
this.getSession() || this.createSession();
21+
22+
this.initSessionAttributes(attrs);
23+
24+
return this;
2125
}
2226

2327
getSession() {
@@ -63,4 +67,26 @@ export class Session {
6367
this._attributes = { ...this._attributes, ...attributes };
6468
return this;
6569
}
70+
71+
setUser(user) {
72+
this.setAttributes({
73+
'user.id': user?.id,
74+
'user.email': user?.email,
75+
'user.name': user?.name || user?.username,
76+
});
77+
return this;
78+
}
79+
80+
initSessionAttributes(attrs) {
81+
this.setAttributes({
82+
'session.id': this.session.id,
83+
'browser.brands': navigator.userAgentData?.brands,
84+
'browser.language': navigator.language,
85+
'browser.mobile': navigator.userAgentData?.mobile,
86+
'browser.platform': navigator.userAgentData?.platform,
87+
'user_agent.original': navigator.userAgent,
88+
...attrs,
89+
});
90+
return this;
91+
}
6692
}

src/tracing/span.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class Span {
2323
startTime: options.startTime || hrtime.now(options.usePerformance),
2424
endTime: [0, 0],
2525
status: { code: 0, message: '' },
26-
attributes: { 'session.id': options.session.id },
26+
attributes: { 'session.id': options.session?.id },
2727
links: [],
2828
events: [],
2929
duration: 0,

src/tracing/tracing.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export default class Tracing {
1212
this.options = options;
1313
this.window = gWindow;
1414

15-
this.session = new Session(this, options);
15+
if (this.window.sessionStorage) {
16+
this.session = new Session(this, options);
17+
}
1618
this.createTracer();
1719
}
1820

test/browser.core.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,60 @@ import { loadHtml } from './util/fixtures.js';
66

77
// Use minimal browser package, with no optional components added.
88
import Rollbar from '../src/browser/core.js';
9+
import Tracing from '../src/tracing/tracing.js';
10+
11+
describe('options', function () {
12+
beforeEach(function () {
13+
Rollbar.setComponents({
14+
tracing: Tracing,
15+
});
16+
});
17+
18+
afterEach(function () {
19+
Rollbar.setComponents({});
20+
});
21+
22+
it('should set session attributes from constructor', function () {
23+
const rollbar = new Rollbar({
24+
accessToken: 'POST_CLIENT_ITEM_TOKEN',
25+
captureUnhandledRejections: false,
26+
person: {
27+
id: '12345',
28+
name: 'Test User',
29+
30+
},
31+
codeVersion: 'abc123',
32+
});
33+
const session = rollbar.tracing.session;
34+
expect(session).to.exist;
35+
expect(session.attributes['user.id']).to.equal('12345');
36+
expect(session.attributes['user.name']).to.equal('Test User');
37+
expect(session.attributes['user.email']).to.equal('[email protected]');
38+
expect(session.attributes['code_version']).to.equal('abc123');
39+
});
40+
41+
it('should set session attributes from configure', function () {
42+
const rollbar = new Rollbar({
43+
accessToken: 'POST_CLIENT_ITEM_TOKEN',
44+
captureUnhandledRejections: false,
45+
46+
});
47+
rollbar.configure({
48+
person: {
49+
id: '12345',
50+
name: 'Test User',
51+
52+
},
53+
codeVersion: 'abc123',
54+
});
55+
const session = rollbar.tracing.session;
56+
expect(session).to.exist;
57+
expect(session.attributes['user.id']).to.equal('12345');
58+
expect(session.attributes['user.name']).to.equal('Test User');
59+
expect(session.attributes['user.email']).to.equal('[email protected]');
60+
expect(session.attributes['code_version']).to.equal('abc123');
61+
});
62+
});
963

1064
describe('options.captureUncaught', function () {
1165
let __originalOnError = null;

test/replay/integration/e2e.test.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ describe('Session Replay E2E', function () {
168168
expect(span_r).to.have.property('events');
169169
expect(span_r.events).to.be.an('array');
170170
expect(span_r).to.have.property('attributes').that.is.an('array');
171-
expect(span_r.attributes).to.have.lengthOf(4);
171+
expect(span_r.attributes).to.have.lengthOf(9);
172172

173173
expect(span_r.attributes).to.deep.include({
174174
key: 'rollbar.replay.id',
@@ -182,6 +182,22 @@ describe('Session Replay E2E', function () {
182182
key: 'user.email',
183183
value: { stringValue: '[email protected]' },
184184
});
185+
expect(span_r.attributes).to.deep.include({
186+
key: 'browser.mobile',
187+
value: { boolValue: false },
188+
});
189+
190+
const languageAttr = span_r.attributes.find(
191+
(attr) => attr.key === 'browser.language',
192+
);
193+
expect(languageAttr).to.exist;
194+
expect(languageAttr.value.stringValue).to.match(/en-US/);
195+
196+
const userAgentAttr = span_r.attributes.find(
197+
(attr) => attr.key === 'user_agent.original',
198+
);
199+
expect(userAgentAttr).to.exist;
200+
expect(userAgentAttr.value.stringValue).to.match(/HeadlessChrome/);
185201

186202
const sessionIdAttr = span_r.attributes.find(
187203
(attr) => attr.key === 'session.id',

test/tracing/span.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ describe('Span()', function () {
166166
done();
167167
});
168168

169+
it('should create a readable span without a session present', function (done) {
170+
const span = new Span(spanOptions({ session: undefined }));
171+
172+
expectReadableSpan(span, {attributes: { 'session.id': undefined }});
173+
174+
done();
175+
});
176+
169177
it('should keep valid state', function (done) {
170178
const span = new Span(spanOptions());
171179
expect(span.isRecording()).to.be.true;

test/tracing/tracing.test.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { expect } from 'chai';
2+
import sinon from 'sinon';
23

34
import {
45
ContextManager,
@@ -107,13 +108,25 @@ describe('Tracing()', function () {
107108
'user.email': '[email protected]',
108109
});
109110

110-
expect(t.session.attributes).to.deep.equal({
111+
expect(t.session.attributes).to.deep.include({
111112
codeVersion: 'abc123',
112113
'user.id': '12345',
113114
'user.email': '[email protected]',
114115
});
115116
});
116117

118+
it('should not init session when sessionStorage is not detected', function () {
119+
const stub = sinon.stub(window, 'sessionStorage').value(undefined);
120+
const options = tracingOptions();
121+
const t = new Tracing(window, options);
122+
123+
// calling initSession should be a no-op
124+
t.initSession();
125+
126+
expect(t.session).to.be.undefined;
127+
stub.restore();
128+
});
129+
117130
it('should generate ids', function (done) {
118131
const options = tracingOptions();
119132
const t = new Tracing(window, options);

0 commit comments

Comments
 (0)