-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* 🐛 shallow clone of user object * ✨ Add setUser and related functions to logs SDK * 📝 Updated user management documentation for logs SDK * 👌♻️ Mutualize checkUser
- Loading branch information
1 parent
4521a9d
commit 0ab07cb
Showing
15 changed files
with
431 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './user.types' | ||
export * from './user' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { checkUser, sanitizeUser } from './user' | ||
import type { User } from './user.types' | ||
|
||
describe('sanitize user function', () => { | ||
it('should sanitize a user object', () => { | ||
const obj = { id: 42, name: true, email: null } | ||
const user = sanitizeUser(obj) | ||
|
||
expect(user).toEqual({ id: '42', name: 'true', email: 'null' }) | ||
}) | ||
|
||
it('should not mutate the original data', () => { | ||
const obj = { id: 42, name: 'test', email: null } | ||
const user = sanitizeUser(obj) | ||
|
||
expect(user.id).toEqual('42') | ||
expect(obj.id).toEqual(42) | ||
}) | ||
}) | ||
|
||
describe('check user function', () => { | ||
it('should only accept valid user objects', () => { | ||
const obj: any = { id: 42, name: true, email: null } // Valid, even though not sanitized | ||
const user: User = { id: '42', name: 'John', email: '[email protected]' } | ||
const undefUser: any = undefined | ||
const nullUser: any = null | ||
const invalidUser: any = 42 | ||
|
||
expect(checkUser(obj)).toBe(true) | ||
expect(checkUser(user)).toBe(true) | ||
expect(checkUser(undefUser)).toBe(false) | ||
expect(checkUser(nullUser)).toBe(false) | ||
expect(checkUser(invalidUser)).toBe(false) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import type { Context } from '../../tools/context' | ||
import { display } from '../../tools/display' | ||
import { assign, getType } from '../../tools/utils' | ||
import type { User } from './user.types' | ||
|
||
/** | ||
* Clone input data and ensure known user properties (id, name, email) | ||
* are strings, as defined here: | ||
* https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#user-related-attributes | ||
*/ | ||
export function sanitizeUser(newUser: Context): Context { | ||
// We shallow clone only to prevent mutation of user data. | ||
const user = assign({}, newUser) | ||
const keys = ['id', 'name', 'email'] | ||
keys.forEach((key) => { | ||
if (key in user) { | ||
user[key] = String(user[key]) | ||
} | ||
}) | ||
return user | ||
} | ||
|
||
/** | ||
* Simple check to ensure user is valid | ||
*/ | ||
export function checkUser(newUser: User): boolean { | ||
const isValid = getType(newUser) === 'object' | ||
if (!isValid) { | ||
display.error('Unsupported user:', newUser) | ||
} | ||
return isValid | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export interface User { | ||
id?: string | undefined | ||
email?: string | undefined | ||
name?: string | undefined | ||
[key: string]: unknown | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,7 +94,7 @@ To receive all logs and errors, load and configure the SDK at the beginning of t | |
</html> | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
### TypeScript | ||
|
||
|
@@ -174,7 +174,7 @@ DD_LOGS.onReady(function () { | |
window.DD_LOGS && DD_LOGS.logger.info('Button clicked', { name: 'buttonName', id: 123 }) | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
#### Results | ||
|
||
|
@@ -471,7 +471,7 @@ if (window.DD_LOGS) { | |
} | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
### Overwrite context | ||
|
||
|
@@ -570,7 +570,95 @@ window.DD_LOGS && DD_LOGS.clearGlobalContext() | |
window.DD_LOGS && DD_LOGS.getGlobalContext() // => {} | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
#### User context | ||
|
||
The Datadog logs SDK provides convenient functions to associate a `User` with generated logs. | ||
|
||
- Set the user for all your loggers with the `setUser (newUser: User)` API. | ||
- Add or modify a user property to all your loggers with the `setUserProperty (key: string, value: any)` API. | ||
- Get the currently stored user with the `getUser ()` API. | ||
- Remove a user property with the `removeUserProperty (key: string)` API. | ||
- Clear all existing user properties with the `clearUser ()` API. | ||
|
||
**Note**: The user context is applied before the global context. Hence, every user property included in the global context will override the user context when generating logs. | ||
|
||
##### NPM | ||
|
||
For NPM, use: | ||
|
||
```javascript | ||
import { datadogLogs } from '@datadog/browser-logs' | ||
|
||
datadogLogs.setUser({ id: '1234', name: 'John Doe', email: '[email protected]' }) | ||
datadogLogs.setUserProperty('type', 'customer') | ||
datadogLogs.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]', type: 'customer'} | ||
|
||
datadogLogs.removeUserProperty('type') | ||
datadogLogs.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]'} | ||
|
||
datadogLogs.clearUser() | ||
datadogLogs.getUser() // => {} | ||
``` | ||
|
||
#### CDN async | ||
|
||
For CDN async, use: | ||
|
||
```javascript | ||
DD_LOGS.onReady(function () { | ||
DD_LOGS.setUser({ id: '1234', name: 'John Doe', email: '[email protected]' }) | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.setUserProperty('type', 'customer') | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]', type: 'customer'} | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.removeUserProperty('type') | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]'} | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.clearUser() | ||
}) | ||
|
||
DD_LOGS.onReady(function () { | ||
DD_LOGS.getUser() // => {} | ||
}) | ||
``` | ||
|
||
**Note:** Early API calls must be wrapped in the `DD_LOGS.onReady()` callback. This ensures the code only gets executed once the SDK is properly loaded. | ||
|
||
##### CDN sync | ||
|
||
For CDN sync, use: | ||
|
||
```javascript | ||
window.DD_LOGS && DD_LOGS.setUser({ id: '1234', name: 'John Doe', email: '[email protected]' }) | ||
|
||
window.DD_LOGS && DD_LOGS.setUserProperty('type', 'customer') | ||
|
||
window.DD_LOGS && DD_LOGS.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]', type: 'customer'} | ||
|
||
window.DD_LOGS && DD_LOGS.removeUserProperty('type') | ||
|
||
window.DD_LOGS && DD_LOGS.getUser() // => {id: '1234', name: 'John Doe', email: '[email protected]'} | ||
|
||
window.DD_LOGS && DD_LOGS.clearUser() | ||
|
||
window.DD_LOGS && DD_LOGS.getUser() // => {} | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
#### Logger context | ||
|
||
|
@@ -617,7 +705,7 @@ window.DD_LOGS && DD_LOGS.setContext("{'env': 'staging'}") | |
window.DD_LOGS && DD_LOGS.addContext('referrer', document.referrer) | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
### Filter by status | ||
|
||
|
@@ -659,7 +747,7 @@ For CDN sync, use: | |
window.DD_LOGS && DD_LOGS.logger.setLevel('<LEVEL>') | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
### Change the destination | ||
|
||
|
@@ -706,7 +794,7 @@ window.DD_LOGS && DD_LOGS.logger.setHandler('<HANDLER>') | |
window.DD_LOGS && DD_LOGS.logger.setHandler(['<HANDLER1>', '<HANDLER2>']) | ||
``` | ||
|
||
**Note**: The `window.DD_LOGS` check is used to prevent issues if a loading failure occurs with the SDK. | ||
**Note**: The `window.DD_LOGS` check prevents issues when a loading failure occurs with the SDK. | ||
|
||
### Access internal context | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.