Skip to content

Commit 8e79342

Browse files
committed
feat: set userId custom attribute for logging service
Adding setCustomAttribute to the logging service’s interface and implementing it in the NewRelicLoggingService. Also using that in the auth service in order to set the userId as a custom attribute for all logged in users.
1 parent f764beb commit 8e79342

File tree

6 files changed

+60
-1
lines changed

6 files changed

+60
-1
lines changed

src/auth/AxiosJwtAuthService.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,13 @@ class AxiosJwtAuthService {
234234
administrator: decodedAccessToken.administrator,
235235
name: decodedAccessToken.name,
236236
});
237+
// Sets userId as a custom attribute that will be included with all subsequent log messages.
238+
// Very helpful for debugging.
239+
this.loggingService.setCustomAttribute('userId', decodedAccessToken.user_id);
237240
} else {
238241
this.setAuthenticatedUser(null);
242+
// Intentionally not setting `userId` in the logging service here because it would be useful
243+
// to know the previously logged in user for debugging refresh issues.
239244
}
240245

241246
return this.getAuthenticatedUser();

src/auth/AxiosJwtAuthService.test.jsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import AxiosJwtAuthService from './AxiosJwtAuthService';
77
const mockLoggingService = {
88
logInfo: jest.fn(),
99
logError: jest.fn(),
10+
setCustomAttribute: jest.fn(),
1011
};
1112

1213
const authOptions = {
@@ -204,6 +205,7 @@ beforeEach(() => {
204205
};
205206
mockLoggingService.logInfo.mockReset();
206207
mockLoggingService.logError.mockReset();
208+
mockLoggingService.setCustomAttribute.mockReset();
207209
service.getCsrfTokenService().clearCsrfTokenCache();
208210
axiosMock.onGet('/unauthorized').reply(401);
209211
axiosMock.onGet('/forbidden').reply(403);
@@ -861,6 +863,7 @@ describe('fetchAuthenticatedUser', () => {
861863
setJwtTokenRefreshResponseTo(200, jwtTokens.valid.encoded);
862864
return service.fetchAuthenticatedUser().then((authenticatedUserAccessToken) => {
863865
expect(authenticatedUserAccessToken).toEqual(jwtTokens.valid.formatted);
866+
expect(mockLoggingService.setCustomAttribute).toHaveBeenCalledWith('userId', jwtTokens.valid.formatted.userId);
864867
expectSingleCallToJwtTokenRefresh();
865868
});
866869
});
@@ -878,6 +881,7 @@ describe('fetchAuthenticatedUser', () => {
878881
setJwtTokenRefreshResponseTo(401, null);
879882
return service.fetchAuthenticatedUser({ forceRefresh: true }).then((authenticatedUserAccessToken) => {
880883
expect(authenticatedUserAccessToken).toEqual(null);
884+
expect(mockLoggingService.setCustomAttribute).not.toHaveBeenCalled();
881885
expectSingleCallToJwtTokenRefresh();
882886
});
883887
});

src/logging/MockLoggingService.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ class MockLoggingService {
1919
* @memberof MockLoggingService
2020
*/
2121
logError = jest.fn();
22+
23+
/**
24+
* Implemented as a jest.fn()
25+
*
26+
* @memberof MockLoggingService
27+
*/
28+
setCustomAttribute = jest.fn();
2229
}
2330

2431
export default MockLoggingService;

src/logging/NewRelicLoggingService.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ function sendError(error, customAttributes) {
4141
}
4242
}
4343

44+
function setCustomAttribute(name, value) {
45+
if (process.env.NODE_ENV === 'development') {
46+
console.log(name, value); // eslint-disable-line
47+
}
48+
if (window && typeof window.newrelic !== 'undefined') {
49+
// https://docs.newrelic.com/docs/browser/new-relic-browser/browser-apis/setcustomattribute/
50+
window.newrelic.setCustomAttribute(name, value);
51+
}
52+
}
53+
4454
/**
4555
* The NewRelicLoggingService is a concrete implementation of the logging service interface that
4656
* sends messages to NewRelic that can be seen in NewRelic Browser and NewRelic Insights. When in
@@ -67,7 +77,8 @@ function sendError(error, customAttributes) {
6777
* ```
6878
*
6979
* You can also add your own custom metrics as an additional argument, or see the code to find
70-
* other standard custom attributes.
80+
* other standard custom attributes. By default, userId is added (via setCustomAttribute) for logged
81+
* in users via the auth service (AuthAxiosJwtService).
7182
*
7283
* Requires the NewRelic Browser JavaScript snippet.
7384
*
@@ -157,4 +168,14 @@ export default class NewRelicLoggingService {
157168
sendError(errorStringOrObject, allCustomAttributes);
158169
}
159170
}
171+
172+
/**
173+
* Sets a custom attribute that will be included with all subsequent log messages.
174+
*
175+
* @param {string} name
176+
* @param {string|number|null} value
177+
*/
178+
setCustomAttribute(name, value) {
179+
setCustomAttribute(name, value);
180+
}
160181
}

src/logging/NewRelicLoggingService.test.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import NewRelicLoggingService, { MAX_ERROR_LENGTH } from './NewRelicLoggingServi
33
global.newrelic = {
44
addPageAction: jest.fn(),
55
noticeError: jest.fn(),
6+
setCustomAttribute: jest.fn(),
67
};
78

89
let service = null;
@@ -140,6 +141,17 @@ describe('NewRelicLoggingService', () => {
140141
});
141142
});
142143

144+
describe('setCustomAttribute', () => {
145+
beforeEach(() => {
146+
global.newrelic.setCustomAttribute.mockReset();
147+
});
148+
149+
it('calls New Relic client with name and value', () => {
150+
service.setCustomAttribute('foo', 'bar');
151+
expect(global.newrelic.setCustomAttribute).toHaveBeenCalledWith('foo', 'bar');
152+
});
153+
});
154+
143155
describe('ignoredErrors', () => {
144156
beforeEach(() => {
145157
global.newrelic.addPageAction.mockReset();

src/logging/interface.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,16 @@ export function logError(errorStringOrObject, customAttributes) {
7171
return service.logError(errorStringOrObject, customAttributes);
7272
}
7373

74+
/**
75+
* Sets a custom attribute that will be included with all subsequent log messages.
76+
*
77+
* @param {string} name
78+
* @param {string|number|null} value
79+
*/
80+
export function setCustomAttribute(name, value) {
81+
return service.setCustomAttribute(name, value);
82+
}
83+
7484
/**
7585
*
7686
* @throws {Error} Thrown if the logging service has not yet been configured via {@link configure}.

0 commit comments

Comments
 (0)