@dxos/observability 0.3.11-main.fc97a54 → 0.3.11-next.e28df4f
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.
- package/README.md +4 -9
- package/dist/lib/browser/{chunk-6ZTMPKEG.mjs → chunk-2J5YS65Q.mjs} +3 -3
- package/dist/lib/browser/{chunk-6ZTMPKEG.mjs.map → chunk-2J5YS65Q.mjs.map} +3 -3
- package/dist/lib/browser/{chunk-HPGC4HC7.mjs → chunk-ROSK6U3K.mjs} +3 -3
- package/dist/lib/browser/chunk-ROSK6U3K.mjs.map +7 -0
- package/dist/lib/browser/{chunk-JVT4FOCC.mjs → chunk-STWCJK52.mjs} +130 -238
- package/dist/lib/browser/chunk-STWCJK52.mjs.map +7 -0
- package/dist/lib/browser/datadog/index.mjs +1 -1
- package/dist/lib/browser/index.mjs +5 -9
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/observability-XIDFB6PJ.mjs +10 -0
- package/dist/lib/browser/segment/index.mjs +1 -1
- package/dist/lib/node/{chunk-BHF4PKAP.cjs → chunk-ARUBPK2W.cjs} +6 -6
- package/dist/lib/node/chunk-ARUBPK2W.cjs.map +7 -0
- package/dist/lib/node/{chunk-ED364ZBZ.cjs → chunk-DE7OVD7T.cjs} +120 -226
- package/dist/lib/node/chunk-DE7OVD7T.cjs.map +7 -0
- package/dist/lib/node/{chunk-ZJRKFREX.cjs → chunk-VDBBIYH6.cjs} +6 -6
- package/dist/lib/node/{chunk-ZJRKFREX.cjs.map → chunk-VDBBIYH6.cjs.map} +3 -3
- package/dist/lib/node/datadog/index.cjs +3 -3
- package/dist/lib/node/datadog/index.cjs.map +1 -1
- package/dist/lib/node/index.cjs +16 -20
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/{observability-MN7SJ7NA.cjs → observability-YMIQHLME.cjs} +8 -8
- package/dist/lib/node/observability-YMIQHLME.cjs.map +7 -0
- package/dist/lib/node/segment/index.cjs +2 -2
- package/dist/lib/node/segment/index.cjs.map +1 -1
- package/dist/types/src/datadog/metrics.d.ts +1 -3
- package/dist/types/src/datadog/metrics.d.ts.map +1 -1
- package/dist/types/src/datadog/metrics.js +1 -1
- package/dist/types/src/datadog/metrics.js.map +1 -1
- package/dist/types/src/helpers/{browser-observability.d.ts → app-observability.d.ts} +6 -4
- package/dist/types/src/helpers/app-observability.d.ts.map +1 -0
- package/dist/types/src/helpers/{browser-observability.js → app-observability.js} +51 -22
- package/dist/types/src/helpers/app-observability.js.map +1 -0
- package/dist/types/src/helpers/index.d.ts +1 -3
- package/dist/types/src/helpers/index.d.ts.map +1 -1
- package/dist/types/src/helpers/index.js +1 -3
- package/dist/types/src/helpers/index.js.map +1 -1
- package/dist/types/src/helpers/setup-telemetry-listeners.d.ts.map +1 -1
- package/dist/types/src/helpers/setup-telemetry-listeners.js +12 -7
- package/dist/types/src/helpers/setup-telemetry-listeners.js.map +1 -1
- package/dist/types/src/observability.d.ts +4 -9
- package/dist/types/src/observability.d.ts.map +1 -1
- package/dist/types/src/observability.js +17 -44
- package/dist/types/src/observability.js.map +1 -1
- package/dist/types/src/segment/browser.d.ts.map +1 -1
- package/dist/types/src/segment/browser.js +2 -2
- package/dist/types/src/segment/browser.js.map +1 -1
- package/dist/types/src/segment/node.d.ts.map +1 -1
- package/dist/types/src/segment/node.js +2 -2
- package/dist/types/src/segment/node.js.map +1 -1
- package/dist/types/src/segment/types.d.ts +1 -3
- package/dist/types/src/segment/types.d.ts.map +1 -1
- package/package.json +14 -17
- package/src/datadog/metrics.ts +5 -3
- package/src/helpers/{browser-observability.ts → app-observability.ts} +54 -26
- package/src/helpers/index.ts +1 -3
- package/src/helpers/setup-telemetry-listeners.ts +7 -2
- package/src/observability.ts +20 -57
- package/src/segment/browser.ts +3 -3
- package/src/segment/node.ts +3 -3
- package/src/segment/types.ts +1 -1
- package/dist/lib/browser/chunk-HPGC4HC7.mjs.map +0 -7
- package/dist/lib/browser/chunk-JVT4FOCC.mjs.map +0 -7
- package/dist/lib/browser/observability-3UVQW6ZL.mjs +0 -10
- package/dist/lib/node/chunk-BHF4PKAP.cjs.map +0 -7
- package/dist/lib/node/chunk-ED364ZBZ.cjs.map +0 -7
- package/dist/lib/node/observability-MN7SJ7NA.cjs.map +0 -7
- package/dist/types/src/helpers/browser-observability.d.ts.map +0 -1
- package/dist/types/src/helpers/browser-observability.js.map +0 -1
- package/dist/types/src/helpers/common.d.ts +0 -10
- package/dist/types/src/helpers/common.d.ts.map +0 -1
- package/dist/types/src/helpers/common.js +0 -23
- package/dist/types/src/helpers/common.js.map +0 -1
- package/dist/types/src/helpers/node-observability.d.ts +0 -34
- package/dist/types/src/helpers/node-observability.d.ts.map +0 -1
- package/dist/types/src/helpers/node-observability.js +0 -106
- package/dist/types/src/helpers/node-observability.js.map +0 -1
- package/src/helpers/common.ts +0 -25
- package/src/helpers/node-observability.ts +0 -151
- /package/dist/lib/browser/{observability-3UVQW6ZL.mjs.map → observability-XIDFB6PJ.mjs.map} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/observability",
|
|
3
|
-
"version": "0.3.11-
|
|
3
|
+
"version": "0.3.11-next.e28df4f",
|
|
4
4
|
"description": "Provides a common interface for app and platform observability",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -65,28 +65,25 @@
|
|
|
65
65
|
"analytics-node": "^6.2.0",
|
|
66
66
|
"datadog-metrics": "^0.11.1",
|
|
67
67
|
"debug": "^4.3.4",
|
|
68
|
-
"js-yaml": "^4.1.0",
|
|
69
68
|
"localforage": "^1.10.0",
|
|
70
|
-
"
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/client-services": "0.3.11-
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/invariant": "0.3.11-
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/node-std": "0.3.11-
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/util": "0.3.11-main.fc97a54"
|
|
69
|
+
"@dxos/async": "0.3.11-next.e28df4f",
|
|
70
|
+
"@dxos/client-protocol": "0.3.11-next.e28df4f",
|
|
71
|
+
"@dxos/client-services": "0.3.11-next.e28df4f",
|
|
72
|
+
"@dxos/context": "0.3.11-next.e28df4f",
|
|
73
|
+
"@dxos/debug": "0.3.11-next.e28df4f",
|
|
74
|
+
"@dxos/invariant": "0.3.11-next.e28df4f",
|
|
75
|
+
"@dxos/config": "0.3.11-next.e28df4f",
|
|
76
|
+
"@dxos/log": "0.3.11-next.e28df4f",
|
|
77
|
+
"@dxos/node-std": "0.3.11-next.e28df4f",
|
|
78
|
+
"@dxos/network-manager": "0.3.11-next.e28df4f",
|
|
79
|
+
"@dxos/protocols": "0.3.11-next.e28df4f",
|
|
80
|
+
"@dxos/react-client": "0.3.11-next.e28df4f",
|
|
81
|
+
"@dxos/util": "0.3.11-next.e28df4f"
|
|
84
82
|
},
|
|
85
83
|
"devDependencies": {
|
|
86
84
|
"@sentry/types": "^7.98.0",
|
|
87
85
|
"@types/analytics-node": "^3.1.9",
|
|
88
86
|
"@types/debug": "^4.1.10",
|
|
89
|
-
"@types/js-yaml": "^4.0.5",
|
|
90
87
|
"sentry-testkit": "^5.0.5"
|
|
91
88
|
},
|
|
92
89
|
"publishConfig": {
|
package/src/datadog/metrics.ts
CHANGED
|
@@ -16,7 +16,7 @@ export type DatadogOptions = {
|
|
|
16
16
|
apiKey: string;
|
|
17
17
|
host?: string;
|
|
18
18
|
site?: String;
|
|
19
|
-
getTags: () =>
|
|
19
|
+
getTags: () => Map<string, string>;
|
|
20
20
|
// needed to read CORS proxy
|
|
21
21
|
config: Config;
|
|
22
22
|
};
|
|
@@ -25,7 +25,7 @@ debug.enable('metrics');
|
|
|
25
25
|
|
|
26
26
|
export class DatadogMetrics {
|
|
27
27
|
private _datadogmetrics?: any;
|
|
28
|
-
private _getTags: () =>
|
|
28
|
+
private _getTags: () => Map<string, string>;
|
|
29
29
|
|
|
30
30
|
constructor(options: DatadogOptions) {
|
|
31
31
|
this._getTags = options.getTags;
|
|
@@ -36,7 +36,9 @@ export class DatadogMetrics {
|
|
|
36
36
|
|
|
37
37
|
gauge(name: string, value: number, tags?: any) {
|
|
38
38
|
invariant(this._datadogmetrics, 'Datadog not initialized');
|
|
39
|
-
const mergedTags = Object.entries({ ...this._getTags(), ...tags }).map(
|
|
39
|
+
const mergedTags = Object.entries({ ...Object.fromEntries(this._getTags().entries()), ...tags }).map(
|
|
40
|
+
([key, value]) => `${key}:${value}`,
|
|
41
|
+
);
|
|
40
42
|
log('datadog gauge', { name, value, tags: mergedTags });
|
|
41
43
|
this._datadogmetrics.gauge(name, value, mergedTags);
|
|
42
44
|
}
|
|
@@ -6,16 +6,46 @@
|
|
|
6
6
|
import * as localForage from 'localforage';
|
|
7
7
|
|
|
8
8
|
// import { type Platform } from '@dxos/client-services';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import type { Config } from '@dxos/client';
|
|
9
|
+
import type { Client, Config } from '@dxos/client';
|
|
12
10
|
import { log } from '@dxos/log';
|
|
11
|
+
// import { type InitOptions as TelemetryInitOptions } from '@dxos/telemetry';
|
|
13
12
|
|
|
14
|
-
import type { IPData } from './common';
|
|
15
13
|
import type { Mode, Observability } from '../observability';
|
|
16
14
|
|
|
17
|
-
export const
|
|
18
|
-
|
|
15
|
+
export const BASE_TELEMETRY_PROPERTIES: any = {};
|
|
16
|
+
// item name is 'telemetry' for backwards compatibility
|
|
17
|
+
export const OBSERVABILITY_DISABLED_KEY = 'telemetry-disabled';
|
|
18
|
+
export const OBSERVABILITY_GROUP_KEY = 'telemetry-group';
|
|
19
|
+
|
|
20
|
+
if (typeof navigator !== 'undefined' && navigator.storage?.estimate) {
|
|
21
|
+
setInterval(async () => {
|
|
22
|
+
try {
|
|
23
|
+
const storageEstimate = await navigator.storage.estimate();
|
|
24
|
+
BASE_TELEMETRY_PROPERTIES.storageUsage = storageEstimate.usage;
|
|
25
|
+
BASE_TELEMETRY_PROPERTIES.storageQuota = storageEstimate.quota;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
log.warn('Failed to run estimate()', error);
|
|
28
|
+
}
|
|
29
|
+
}, 10e3);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// TODO(wittjosiah): Improve privacy of telemetry identifiers.
|
|
33
|
+
// - Identifier should be generated client-side with no attachment to identity.
|
|
34
|
+
// - Identifier can then be reset by user.
|
|
35
|
+
// - Identifier can be synced via HALO to allow for correlation of events bewteen devices.
|
|
36
|
+
// - Identifier should also be stored outside of HALO such that it is available immediately on startup.
|
|
37
|
+
export const getTelemetryIdentifier = (client: Client) => {
|
|
38
|
+
if (!client?.initialized) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const identity = client.halo.identity.get();
|
|
43
|
+
if (identity) {
|
|
44
|
+
return identity.identityKey.truncate();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
19
49
|
|
|
20
50
|
export const isObservabilityDisabled = async (namespace: string): Promise<boolean> => {
|
|
21
51
|
try {
|
|
@@ -59,7 +89,9 @@ export type AppObservabilityOptions = {
|
|
|
59
89
|
// TODO(nf): options for providers?
|
|
60
90
|
};
|
|
61
91
|
|
|
62
|
-
|
|
92
|
+
type IPData = { city: string; region: string; country: string; latitude: number; longitude: number };
|
|
93
|
+
|
|
94
|
+
// TODO(wittjosiah): Store preference for disabling telemetry.
|
|
63
95
|
// At minimum should be stored locally (i.e., localstorage), possibly in halo preference.
|
|
64
96
|
// Needs to be hooked up to settings page for user visibility.
|
|
65
97
|
export const initializeAppObservability = async ({
|
|
@@ -82,7 +114,9 @@ export const initializeAppObservability = async ({
|
|
|
82
114
|
const group = (await getObservabilityGroup(namespace)) ?? undefined;
|
|
83
115
|
const release = `${namespace}@${config.get('runtime.app.build.version')}`;
|
|
84
116
|
const environment = config.get('runtime.app.env.DX_ENVIRONMENT');
|
|
85
|
-
|
|
117
|
+
BASE_TELEMETRY_PROPERTIES.group = group;
|
|
118
|
+
BASE_TELEMETRY_PROPERTIES.release = release;
|
|
119
|
+
BASE_TELEMETRY_PROPERTIES.environment = environment;
|
|
86
120
|
const observabilityDisabled = await isObservabilityDisabled(namespace);
|
|
87
121
|
|
|
88
122
|
const { Observability } = await import('../observability');
|
|
@@ -123,11 +157,12 @@ export const initializeAppObservability = async ({
|
|
|
123
157
|
};
|
|
124
158
|
|
|
125
159
|
// Check cache first.
|
|
126
|
-
const cachedData: null | CachedIPData = await localForage.getItem('dxos:
|
|
160
|
+
const cachedData: null | CachedIPData = await localForage.getItem('dxos:telemetry:ipdata');
|
|
127
161
|
if (cachedData && cachedData.timestamp > Date.now() - IP_DATA_CACHE_TIMEOUT) {
|
|
128
162
|
return cachedData.data;
|
|
129
163
|
}
|
|
130
164
|
|
|
165
|
+
// TODO(nf): move into observability
|
|
131
166
|
// Fetch data if not cached.
|
|
132
167
|
const IPDATA_API_KEY = config.get('runtime.app.env.DX_IPDATA_API_KEY');
|
|
133
168
|
if (IPDATA_API_KEY) {
|
|
@@ -136,7 +171,7 @@ export const initializeAppObservability = async ({
|
|
|
136
171
|
.then((data) => {
|
|
137
172
|
// Cache data.
|
|
138
173
|
localForage
|
|
139
|
-
.setItem('dxos:
|
|
174
|
+
.setItem('dxos:telemetry:ipdata', {
|
|
140
175
|
data,
|
|
141
176
|
timestamp: Date.now(),
|
|
142
177
|
})
|
|
@@ -148,27 +183,20 @@ export const initializeAppObservability = async ({
|
|
|
148
183
|
}
|
|
149
184
|
};
|
|
150
185
|
|
|
186
|
+
const ipData = await getIPData(config);
|
|
187
|
+
if (ipData && ipData.city) {
|
|
188
|
+
BASE_TELEMETRY_PROPERTIES.city = ipData.city;
|
|
189
|
+
BASE_TELEMETRY_PROPERTIES.region = ipData.region;
|
|
190
|
+
BASE_TELEMETRY_PROPERTIES.country = ipData.country;
|
|
191
|
+
BASE_TELEMETRY_PROPERTIES.latitude = ipData.latitude;
|
|
192
|
+
BASE_TELEMETRY_PROPERTIES.longitude = ipData.longitude;
|
|
193
|
+
}
|
|
194
|
+
|
|
151
195
|
// TODO(nf): plugin state?
|
|
152
196
|
|
|
153
197
|
// TODO(nf): should provide capability to init Sentry earlier in booting process to capture errors during initialization.
|
|
154
198
|
|
|
155
199
|
observability.initialize();
|
|
156
|
-
|
|
157
|
-
const ipData = await getIPData(config);
|
|
158
|
-
|
|
159
|
-
ipData && observability.addIPDataTelemetryTags(ipData);
|
|
160
|
-
|
|
161
|
-
if (typeof navigator !== 'undefined' && navigator.storage?.estimate) {
|
|
162
|
-
setInterval(async () => {
|
|
163
|
-
try {
|
|
164
|
-
const storageEstimate = await navigator.storage.estimate();
|
|
165
|
-
storageEstimate.usage && observability.setTag('storageUsage', storageEstimate.usage.toString(), 'telemetry');
|
|
166
|
-
storageEstimate.quota && observability.setTag('storageQuota', storageEstimate.quota.toString(), 'telemetry');
|
|
167
|
-
} catch (error) {
|
|
168
|
-
log.warn('Failed to run estimate()', error);
|
|
169
|
-
}
|
|
170
|
-
}, 10e3);
|
|
171
|
-
}
|
|
172
200
|
} catch (err: any) {
|
|
173
201
|
log.error('Failed to initialize app observability', err);
|
|
174
202
|
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Client } from '@dxos/client';
|
|
6
6
|
|
|
7
|
-
import { getTelemetryIdentifier } from './
|
|
7
|
+
import { BASE_TELEMETRY_PROPERTIES, getTelemetryIdentifier } from './app-observability';
|
|
8
8
|
import type { Observability } from '../observability';
|
|
9
9
|
|
|
10
10
|
let lastFocusEvent = new Date();
|
|
@@ -12,7 +12,7 @@ let totalTime = 0;
|
|
|
12
12
|
|
|
13
13
|
export const setupTelemetryListeners = (namespace: string, client: Client, observability: Observability) => {
|
|
14
14
|
const clickCallback = (event: any) => {
|
|
15
|
-
if (
|
|
15
|
+
if (BASE_TELEMETRY_PROPERTIES.group === 'dxos' && event.target && !event.target.id) {
|
|
16
16
|
// TODO(wittjosiah): Use @dxos/log so these can be filtered.
|
|
17
17
|
console.warn('Click event on element without id:', event.target);
|
|
18
18
|
}
|
|
@@ -22,6 +22,7 @@ export const setupTelemetryListeners = (namespace: string, client: Client, obser
|
|
|
22
22
|
identityId: getTelemetryIdentifier(client),
|
|
23
23
|
name: `${namespace}.window.click`,
|
|
24
24
|
properties: {
|
|
25
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
25
26
|
href: window.location.href,
|
|
26
27
|
id: (event.target as HTMLElement)?.id,
|
|
27
28
|
path: (event.path as HTMLElement[])
|
|
@@ -41,6 +42,7 @@ export const setupTelemetryListeners = (namespace: string, client: Client, obser
|
|
|
41
42
|
identityId: getTelemetryIdentifier(client),
|
|
42
43
|
name: `${namespace}.window.focus`,
|
|
43
44
|
properties: {
|
|
45
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
44
46
|
href: window.location.href,
|
|
45
47
|
timeAway: now.getTime() - lastFocusEvent.getTime(),
|
|
46
48
|
},
|
|
@@ -57,6 +59,7 @@ export const setupTelemetryListeners = (namespace: string, client: Client, obser
|
|
|
57
59
|
identityId: getTelemetryIdentifier(client),
|
|
58
60
|
name: `${namespace}.window.blur`,
|
|
59
61
|
properties: {
|
|
62
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
60
63
|
href: window.location.href,
|
|
61
64
|
timeSpent,
|
|
62
65
|
},
|
|
@@ -72,6 +75,7 @@ export const setupTelemetryListeners = (namespace: string, client: Client, obser
|
|
|
72
75
|
identityId: getTelemetryIdentifier(client),
|
|
73
76
|
name: `${namespace}.page.unload`,
|
|
74
77
|
properties: {
|
|
78
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
75
79
|
href: window.location.href,
|
|
76
80
|
timeSpent: totalTime,
|
|
77
81
|
},
|
|
@@ -85,6 +89,7 @@ export const setupTelemetryListeners = (namespace: string, client: Client, obser
|
|
|
85
89
|
identityId: getTelemetryIdentifier(client),
|
|
86
90
|
name: `${namespace}.window.error`,
|
|
87
91
|
properties: {
|
|
92
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
88
93
|
href: window.location.href,
|
|
89
94
|
message: event.message,
|
|
90
95
|
filename: event.filename,
|
package/src/observability.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { isNode } from '@dxos/util';
|
|
|
14
14
|
|
|
15
15
|
import buildSecrets from './cli-observability-secrets.json';
|
|
16
16
|
import { DatadogMetrics } from './datadog';
|
|
17
|
-
import {
|
|
17
|
+
import { BASE_TELEMETRY_PROPERTIES, getTelemetryIdentifier, mapSpaces } from './helpers';
|
|
18
18
|
import { SegmentTelemetry, type EventOptions, type PageOptions } from './segment';
|
|
19
19
|
import {
|
|
20
20
|
captureException as sentryCaptureException,
|
|
@@ -29,6 +29,9 @@ const SPACE_METRICS_MIN_INTERVAL = 1000 * 60;
|
|
|
29
29
|
// const DATADOG_IDLE_INTERVAL = 1000 * 60 * 5;
|
|
30
30
|
const DATADOG_IDLE_INTERVAL = 1000 * 60 * 1;
|
|
31
31
|
|
|
32
|
+
// TODO(nf): add allowlist for telemetry tags?
|
|
33
|
+
const ERROR_TAGS = ['identityKey', 'username', 'deviceKey', 'group'];
|
|
34
|
+
|
|
32
35
|
// Secrets? EnvironmentConfig?
|
|
33
36
|
|
|
34
37
|
export type ObservabilitySecrets = {
|
|
@@ -42,7 +45,6 @@ export type ObservabilitySecrets = {
|
|
|
42
45
|
};
|
|
43
46
|
|
|
44
47
|
export type Mode = 'basic' | 'full' | 'disabled';
|
|
45
|
-
export type TagScope = 'errors' | 'telemetry' | 'metrics' | 'all';
|
|
46
48
|
|
|
47
49
|
export type ObservabilityOptions = {
|
|
48
50
|
/// The webapp (e.g. 'composer.dxos.org'), 'cli', or 'agent'.
|
|
@@ -85,7 +87,7 @@ export class Observability {
|
|
|
85
87
|
private _group?: string;
|
|
86
88
|
// TODO(nf): accept upstream context?
|
|
87
89
|
private _ctx = new Context();
|
|
88
|
-
private _tags = new Map<string,
|
|
90
|
+
private _tags = new Map<string, string>();
|
|
89
91
|
|
|
90
92
|
// TODO(nf): make platform a required extension?
|
|
91
93
|
constructor({ namespace, config, secrets, group, mode, telemetry, errorLog }: ObservabilityOptions) {
|
|
@@ -110,20 +112,7 @@ export class Observability {
|
|
|
110
112
|
|
|
111
113
|
private _loadSecrets(config: Config | undefined, secrets?: Record<string, string>) {
|
|
112
114
|
if (isNode()) {
|
|
113
|
-
|
|
114
|
-
...(buildSecrets as ObservabilitySecrets),
|
|
115
|
-
...secrets,
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
process.env.DX_ENVIRONMENT && (mergedSecrets.DX_ENVIRONMENT = process.env.DX_ENVIRONMENT);
|
|
119
|
-
process.env.DX_RELEASE && (mergedSecrets.DX_RELEASE = process.env.DX_RELEASE);
|
|
120
|
-
process.env.SENTRY_DESTINATION && (mergedSecrets.SENTRY_DESTINATION = process.env.SENTRY_DESTINATION);
|
|
121
|
-
process.env.TELEMETRY_API_KEY && (mergedSecrets.TELEMETRY_API_KEY = process.env.TELEMETRY_API_KEY);
|
|
122
|
-
process.env.IPDATA_API_KEY && (mergedSecrets.IPDATA_API_KEY = process.env.IPDATA_API_KEY);
|
|
123
|
-
process.env.DATADOG_API_KEY && (mergedSecrets.DATADOG_API_KEY = process.env.DATADOG_API_KEY);
|
|
124
|
-
process.env.DATADOG_APP_KEY && (mergedSecrets.DATADOG_APP_KEY = process.env.DATADOG_APP_KEY);
|
|
125
|
-
|
|
126
|
-
return mergedSecrets;
|
|
115
|
+
return buildSecrets as ObservabilitySecrets;
|
|
127
116
|
} else {
|
|
128
117
|
invariant(config, 'runtime config is required');
|
|
129
118
|
log('config', { rtc: this._secrets, config });
|
|
@@ -160,10 +149,6 @@ export class Observability {
|
|
|
160
149
|
return this._mode;
|
|
161
150
|
}
|
|
162
151
|
|
|
163
|
-
get group() {
|
|
164
|
-
return this._group;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
152
|
get enabled() {
|
|
168
153
|
return this._mode !== 'disabled';
|
|
169
154
|
}
|
|
@@ -172,28 +157,17 @@ export class Observability {
|
|
|
172
157
|
// Tags
|
|
173
158
|
//
|
|
174
159
|
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Set a tag for all observability services.
|
|
162
|
+
*/
|
|
163
|
+
setTag(key: string, value: string) {
|
|
164
|
+
if (this.enabled && ERROR_TAGS.includes(key)) {
|
|
177
165
|
sentrySetTag(key, value);
|
|
178
166
|
}
|
|
179
|
-
if (!scope) {
|
|
180
|
-
scope = 'all';
|
|
181
|
-
}
|
|
182
|
-
this._tags.set(key, { value, scope });
|
|
183
|
-
}
|
|
184
167
|
|
|
185
|
-
|
|
186
|
-
return this._tags.get(key);
|
|
168
|
+
this._tags.set(key, value);
|
|
187
169
|
}
|
|
188
170
|
|
|
189
|
-
addIPDataTelemetryTags = (ipData: IPData) => {
|
|
190
|
-
this.setTag('city', ipData.city, 'telemetry');
|
|
191
|
-
this.setTag('region', ipData.region, 'telemetry');
|
|
192
|
-
this.setTag('country', ipData.country, 'telemetry');
|
|
193
|
-
ipData.latitude && this.setTag('latitude', ipData.latitude.toString(), 'telemetry');
|
|
194
|
-
ipData.longitude && this.setTag('longitude', ipData.longitude.toString(), 'telemetry');
|
|
195
|
-
};
|
|
196
|
-
|
|
197
171
|
// TODO(wittjosiah): Improve privacy of telemetry identifiers. See `getTelemetryIdentifier`.
|
|
198
172
|
async setIdentityTags(client: Client) {
|
|
199
173
|
client.services.services.IdentityService!.queryIdentity().subscribe((idqr) => {
|
|
@@ -232,14 +206,7 @@ export class Observability {
|
|
|
232
206
|
if (this.enabled && this._secrets.DATADOG_API_KEY) {
|
|
233
207
|
this._metrics = new DatadogMetrics({
|
|
234
208
|
apiKey: this._secrets.DATADOG_API_KEY,
|
|
235
|
-
getTags: () =>
|
|
236
|
-
Object.fromEntries(
|
|
237
|
-
Array.from(this._tags)
|
|
238
|
-
.filter(([key, value]) => {
|
|
239
|
-
return value.scope === 'all' || value.scope === 'metrics';
|
|
240
|
-
})
|
|
241
|
-
.map(([key, value]) => [key, value.value]),
|
|
242
|
-
),
|
|
209
|
+
getTags: () => this._tags,
|
|
243
210
|
// TODO(nf): move/refactor from telementryContext, needed to read CORS proxy
|
|
244
211
|
config: this._config!,
|
|
245
212
|
});
|
|
@@ -328,7 +295,10 @@ export class Observability {
|
|
|
328
295
|
this.event({
|
|
329
296
|
identityId: getTelemetryIdentifier(client),
|
|
330
297
|
name: `${namespace}.space.update`,
|
|
331
|
-
properties:
|
|
298
|
+
properties: {
|
|
299
|
+
...BASE_TELEMETRY_PROPERTIES,
|
|
300
|
+
...data,
|
|
301
|
+
},
|
|
332
302
|
});
|
|
333
303
|
}
|
|
334
304
|
});
|
|
@@ -402,14 +372,7 @@ export class Observability {
|
|
|
402
372
|
this._telemetry = new SegmentTelemetry({
|
|
403
373
|
apiKey: this._secrets.TELEMETRY_API_KEY,
|
|
404
374
|
batchSize: this._telemetryBatchSize,
|
|
405
|
-
getTags: () =>
|
|
406
|
-
Object.fromEntries(
|
|
407
|
-
Array.from(this._tags)
|
|
408
|
-
.filter(([key, value]) => {
|
|
409
|
-
return value.scope === 'all' || value.scope === 'telemetry';
|
|
410
|
-
})
|
|
411
|
-
.map(([key, value]) => [key, value.value]),
|
|
412
|
-
),
|
|
375
|
+
getTags: () => this._tags,
|
|
413
376
|
});
|
|
414
377
|
} else {
|
|
415
378
|
log('segment disabled');
|
|
@@ -459,8 +422,8 @@ export class Observability {
|
|
|
459
422
|
}
|
|
460
423
|
// TODO(nf): is this different than passing as properties in options?
|
|
461
424
|
this._tags.forEach((v, k) => {
|
|
462
|
-
if (
|
|
463
|
-
sentrySetTag(k, v
|
|
425
|
+
if (ERROR_TAGS.includes(k)) {
|
|
426
|
+
sentrySetTag(k, v);
|
|
464
427
|
}
|
|
465
428
|
});
|
|
466
429
|
} else {
|
package/src/segment/browser.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { EventOptions, SegmentTelemetryOptions, PageOptions } from './types
|
|
|
10
10
|
import { captureException } from '../sentry';
|
|
11
11
|
|
|
12
12
|
export class SegmentTelemetry {
|
|
13
|
-
private _getTags: () =>
|
|
13
|
+
private _getTags: () => Map<string, string>;
|
|
14
14
|
|
|
15
15
|
constructor({ apiKey, batchSize, getTags }: SegmentTelemetryOptions) {
|
|
16
16
|
this._getTags = getTags;
|
|
@@ -31,7 +31,7 @@ export class SegmentTelemetry {
|
|
|
31
31
|
page({ identityId: userId, ...options }: PageOptions = {}) {
|
|
32
32
|
try {
|
|
33
33
|
(window as any).analytics?.page({
|
|
34
|
-
context: this._getTags(),
|
|
34
|
+
context: Object.fromEntries(this._getTags().entries()),
|
|
35
35
|
...options,
|
|
36
36
|
userId,
|
|
37
37
|
});
|
|
@@ -44,7 +44,7 @@ export class SegmentTelemetry {
|
|
|
44
44
|
try {
|
|
45
45
|
options.properties = {
|
|
46
46
|
...options.properties,
|
|
47
|
-
...this._getTags(),
|
|
47
|
+
...Object.fromEntries(this._getTags().entries()),
|
|
48
48
|
};
|
|
49
49
|
(window as any).analytics?.track({
|
|
50
50
|
...options,
|
package/src/segment/node.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { captureException } from '../sentry';
|
|
|
12
12
|
|
|
13
13
|
export class SegmentTelemetry {
|
|
14
14
|
private _analytics?: Analytics;
|
|
15
|
-
private _getTags: () =>
|
|
15
|
+
private _getTags: () => Map<string, string>;
|
|
16
16
|
|
|
17
17
|
constructor({ apiKey, batchSize, getTags }: SegmentTelemetryOptions) {
|
|
18
18
|
this._getTags = getTags;
|
|
@@ -52,7 +52,7 @@ export class SegmentTelemetry {
|
|
|
52
52
|
* Track an event.
|
|
53
53
|
*/
|
|
54
54
|
event({ installationId: anonymousId, identityId: userId, name: event, ...options }: EventOptions) {
|
|
55
|
-
log('sending event to telemetry', { event, options, tags: this._getTags() });
|
|
55
|
+
log('sending event to telemetry', { event, options, tags: Object.fromEntries(this._getTags().entries()) });
|
|
56
56
|
if (!this._analytics) {
|
|
57
57
|
log('Analytics not initialized', { action: 'event' });
|
|
58
58
|
return;
|
|
@@ -61,7 +61,7 @@ export class SegmentTelemetry {
|
|
|
61
61
|
try {
|
|
62
62
|
this._analytics.track({
|
|
63
63
|
...options,
|
|
64
|
-
context: this._getTags(),
|
|
64
|
+
context: Object.fromEntries(this._getTags().entries()),
|
|
65
65
|
userId,
|
|
66
66
|
anonymousId: anonymousId!,
|
|
67
67
|
event,
|
package/src/segment/types.ts
CHANGED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/segment/browser.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2022 DXOS.org\n//\n\nimport snippet from '@segment/snippet';\n\nimport { log } from '@dxos/log';\n\nimport type { EventOptions, SegmentTelemetryOptions, PageOptions } from './types';\nimport { captureException } from '../sentry';\n\nexport class SegmentTelemetry {\n private _getTags: () => { [key: string]: string };\n\n constructor({ apiKey, batchSize, getTags }: SegmentTelemetryOptions) {\n this._getTags = getTags;\n try {\n const contents = snippet.min({\n apiKey,\n page: false,\n });\n\n const script = document.createElement('script');\n script.innerHTML = contents;\n document.body.append(script);\n } catch (err) {\n log.catch('Failed to initialize telemetry', err);\n }\n }\n\n page({ identityId: userId, ...options }: PageOptions = {}) {\n try {\n (window as any).analytics?.page({\n context: this._getTags(),\n ...options,\n userId,\n });\n } catch (err) {\n log.catch('Failed to track page', err);\n }\n }\n\n event({ identityId: userId, name: event, ...options }: EventOptions) {\n try {\n options.properties = {\n ...options.properties,\n ...this._getTags(),\n };\n (window as any).analytics?.track({\n ...options,\n event,\n });\n } catch (err) {\n log.catch('Failed to track event', err);\n }\n }\n\n async flush() {\n try {\n await (window as any).analytics?.flush((err: any) => {\n captureException(err);\n });\n } catch (err) {\n log.catch('Failed to flush telemetry', err);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;AAIA,OAAOA,aAAa;AAEpB,SAASC,WAAW;;AAKb,IAAMC,mBAAN,MAAMA;EAGXC,YAAY,EAAEC,QAAQC,WAAWC,QAAO,GAA6B;AACnE,SAAKC,WAAWD;AAChB,QAAI;AACF,YAAME,WAAWC,QAAQC,IAAI;QAC3BN;QACAO,MAAM;MACR,CAAA;AAEA,YAAMC,SAASC,SAASC,cAAc,QAAA;AACtCF,aAAOG,YAAYP;AACnBK,eAASG,KAAKC,OAAOL,MAAAA;IACvB,SAASM,KAAK;AACZC,UAAIC,MAAM,kCAAkCF,KAAAA;;;;;;IAC9C;EACF;EAEAP,KAAK,EAAEU,YAAYC,QAAQ,GAAGC,QAAAA,IAAyB,CAAC,GAAG;AACzD,QAAI;AACDC,aAAeC,WAAWd,KAAK;QAC9Be,SAAS,KAAKnB,SAAQ;QACtB,GAAGgB;QACHD;MACF,CAAA;IACF,SAASJ,KAAK;AACZC,UAAIC,MAAM,wBAAwBF,KAAAA;;;;;;IACpC;EACF;EAEAS,MAAM,EAAEN,YAAYC,QAAQM,MAAMD,OAAO,GAAGJ,QAAAA,GAAyB;AACnE,QAAI;AACFA,cAAQM,aAAa;QACnB,GAAGN,QAAQM;QACX,GAAG,KAAKtB,SAAQ;MAClB;AACCiB,aAAeC,WAAWK,MAAM;QAC/B,GAAGP;QACHI;MACF,CAAA;IACF,SAAST,KAAK;AACZC,UAAIC,MAAM,yBAAyBF,KAAAA;;;;;;IACrC;EACF;EAEA,MAAMa,QAAQ;AACZ,QAAI;AACF,YAAOP,OAAeC,WAAWM,MAAM,CAACb,QAAAA;AACtCc,yBAAiBd,GAAAA;MACnB,CAAA;IACF,SAASA,KAAK;AACZC,UAAIC,MAAM,6BAA6BF,KAAAA;;;;;;IACzC;EACF;AACF;",
|
|
6
|
-
"names": ["snippet", "log", "SegmentTelemetry", "constructor", "apiKey", "batchSize", "getTags", "_getTags", "contents", "snippet", "min", "page", "script", "document", "createElement", "innerHTML", "body", "append", "err", "log", "catch", "identityId", "userId", "options", "window", "analytics", "context", "event", "name", "properties", "track", "flush", "captureException"]
|
|
7
|
-
}
|