@dxos/observability 0.3.11-main.2f1331c → 0.3.11-main.463e5cd

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 (89) hide show
  1. package/README.md +9 -4
  2. package/dist/lib/browser/{chunk-2J5YS65Q.mjs → chunk-6ZTMPKEG.mjs} +3 -3
  3. package/dist/lib/browser/{chunk-2J5YS65Q.mjs.map → chunk-6ZTMPKEG.mjs.map} +3 -3
  4. package/dist/lib/browser/chunk-HPGC4HC7.mjs +83 -0
  5. package/dist/lib/browser/chunk-HPGC4HC7.mjs.map +7 -0
  6. package/dist/lib/browser/chunk-OCQVYNE3.mjs +842 -0
  7. package/dist/lib/browser/chunk-OCQVYNE3.mjs.map +7 -0
  8. package/dist/lib/browser/datadog/index.mjs +1 -1
  9. package/dist/lib/browser/index.mjs +32 -553
  10. package/dist/lib/browser/index.mjs.map +4 -4
  11. package/dist/lib/browser/meta.json +1 -1
  12. package/dist/lib/browser/observability-QNG5QON4.mjs +10 -0
  13. package/dist/lib/browser/observability-QNG5QON4.mjs.map +7 -0
  14. package/dist/lib/browser/segment/index.mjs +8 -0
  15. package/dist/lib/browser/segment/index.mjs.map +7 -0
  16. package/dist/lib/node/chunk-BHF4PKAP.cjs +170 -0
  17. package/dist/lib/node/chunk-BHF4PKAP.cjs.map +7 -0
  18. package/dist/lib/node/chunk-CBMRJXSL.cjs +861 -0
  19. package/dist/lib/node/chunk-CBMRJXSL.cjs.map +7 -0
  20. package/dist/lib/node/{chunk-VDBBIYH6.cjs → chunk-ZJRKFREX.cjs} +6 -6
  21. package/dist/lib/node/{chunk-VDBBIYH6.cjs.map → chunk-ZJRKFREX.cjs.map} +3 -3
  22. package/dist/lib/node/datadog/index.cjs +3 -3
  23. package/dist/lib/node/datadog/index.cjs.map +1 -1
  24. package/dist/lib/node/index.cjs +31 -603
  25. package/dist/lib/node/index.cjs.map +4 -4
  26. package/dist/lib/node/meta.json +1 -1
  27. package/dist/lib/node/observability-JMKBRR4L.cjs +32 -0
  28. package/dist/lib/node/observability-JMKBRR4L.cjs.map +7 -0
  29. package/dist/lib/node/segment/index.cjs +30 -0
  30. package/dist/lib/node/segment/index.cjs.map +7 -0
  31. package/dist/types/src/datadog/metrics.d.ts +3 -1
  32. package/dist/types/src/datadog/metrics.d.ts.map +1 -1
  33. package/dist/types/src/datadog/metrics.js +1 -1
  34. package/dist/types/src/datadog/metrics.js.map +1 -1
  35. package/dist/types/src/helpers/browser-observability.d.ts +17 -0
  36. package/dist/types/src/helpers/browser-observability.d.ts.map +1 -0
  37. package/dist/types/src/helpers/browser-observability.js +166 -0
  38. package/dist/types/src/helpers/browser-observability.js.map +1 -0
  39. package/dist/types/src/helpers/common.d.ts +10 -0
  40. package/dist/types/src/helpers/common.d.ts.map +1 -0
  41. package/dist/types/src/helpers/common.js +23 -0
  42. package/dist/types/src/helpers/common.js.map +1 -0
  43. package/dist/types/src/helpers/index.d.ts +6 -0
  44. package/dist/types/src/helpers/index.d.ts.map +1 -0
  45. package/dist/types/src/helpers/index.js +25 -0
  46. package/dist/types/src/helpers/index.js.map +1 -0
  47. package/dist/types/src/{util.d.ts → helpers/map-spaces.d.ts} +2 -3
  48. package/dist/types/src/helpers/map-spaces.d.ts.map +1 -0
  49. package/dist/types/src/{util.js → helpers/map-spaces.js} +1 -2
  50. package/dist/types/src/helpers/map-spaces.js.map +1 -0
  51. package/dist/types/src/helpers/node-observability.d.ts +34 -0
  52. package/dist/types/src/helpers/node-observability.d.ts.map +1 -0
  53. package/dist/types/src/helpers/node-observability.js +106 -0
  54. package/dist/types/src/helpers/node-observability.js.map +1 -0
  55. package/dist/types/src/helpers/setup-telemetry-listeners.d.ts +4 -0
  56. package/dist/types/src/helpers/setup-telemetry-listeners.d.ts.map +1 -0
  57. package/dist/types/src/helpers/setup-telemetry-listeners.js +91 -0
  58. package/dist/types/src/helpers/setup-telemetry-listeners.js.map +1 -0
  59. package/dist/types/src/index.d.ts +1 -0
  60. package/dist/types/src/index.d.ts.map +1 -1
  61. package/dist/types/src/index.js +1 -0
  62. package/dist/types/src/index.js.map +1 -1
  63. package/dist/types/src/observability.d.ts +8 -8
  64. package/dist/types/src/observability.d.ts.map +1 -1
  65. package/dist/types/src/observability.js +51 -37
  66. package/dist/types/src/observability.js.map +1 -1
  67. package/dist/types/src/segment/browser.d.ts.map +1 -1
  68. package/dist/types/src/segment/browser.js +2 -2
  69. package/dist/types/src/segment/browser.js.map +1 -1
  70. package/dist/types/src/segment/node.d.ts.map +1 -1
  71. package/dist/types/src/segment/node.js +2 -2
  72. package/dist/types/src/segment/node.js.map +1 -1
  73. package/dist/types/src/segment/types.d.ts +3 -1
  74. package/dist/types/src/segment/types.d.ts.map +1 -1
  75. package/package.json +18 -14
  76. package/src/datadog/metrics.ts +3 -5
  77. package/src/helpers/browser-observability.ts +177 -0
  78. package/src/helpers/common.ts +25 -0
  79. package/src/helpers/index.ts +9 -0
  80. package/src/{util.ts → helpers/map-spaces.ts} +1 -2
  81. package/src/helpers/node-observability.ts +151 -0
  82. package/src/helpers/setup-telemetry-listeners.ts +110 -0
  83. package/src/index.ts +1 -0
  84. package/src/observability.ts +61 -39
  85. package/src/segment/browser.ts +3 -3
  86. package/src/segment/node.ts +3 -3
  87. package/src/segment/types.ts +1 -1
  88. package/dist/types/src/util.d.ts.map +0 -1
  89. package/dist/types/src/util.js.map +0 -1
@@ -14,6 +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 { type IPData, getTelemetryIdentifier, mapSpaces } from './helpers';
17
18
  import { SegmentTelemetry, type EventOptions, type PageOptions } from './segment';
18
19
  import {
19
20
  captureException as sentryCaptureException,
@@ -23,15 +24,11 @@ import {
23
24
  type InitOptions,
24
25
  setTag as sentrySetTag,
25
26
  } from './sentry';
26
- import { mapSpaces } from './util';
27
27
 
28
28
  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
-
35
32
  // Secrets? EnvironmentConfig?
36
33
 
37
34
  export type ObservabilitySecrets = {
@@ -45,6 +42,7 @@ export type ObservabilitySecrets = {
45
42
  };
46
43
 
47
44
  export type Mode = 'basic' | 'full' | 'disabled';
45
+ export type TagScope = 'errors' | 'telemetry' | 'metrics' | 'all';
48
46
 
49
47
  export type ObservabilityOptions = {
50
48
  /// The webapp (e.g. 'composer.dxos.org'), 'cli', or 'agent'.
@@ -62,8 +60,8 @@ export type ObservabilityOptions = {
62
60
 
63
61
  errorLog?: {
64
62
  sentryInitOptions?: InitOptions;
63
+ logProcessor?: boolean;
65
64
  };
66
- logProcessor?: boolean;
67
65
  };
68
66
 
69
67
  /*
@@ -87,10 +85,10 @@ export class Observability {
87
85
  private _group?: string;
88
86
  // TODO(nf): accept upstream context?
89
87
  private _ctx = new Context();
90
- private _tags = new Map<string, string>();
88
+ private _tags = new Map<string, { value: string; scope: TagScope }>();
91
89
 
92
90
  // TODO(nf): make platform a required extension?
93
- constructor({ namespace, config, secrets, group, mode, telemetry, errorLog, logProcessor }: ObservabilityOptions) {
91
+ constructor({ namespace, config, secrets, group, mode, telemetry, errorLog }: ObservabilityOptions) {
94
92
  this._namespace = namespace;
95
93
  this._config = config;
96
94
  this._mode = mode ?? 'disabled';
@@ -98,7 +96,7 @@ export class Observability {
98
96
  this._secrets = this._loadSecrets(config, secrets);
99
97
  this._telemetryBatchSize = telemetry?.batchSize ?? 30;
100
98
  this._errorReportingOptions = errorLog?.sentryInitOptions;
101
- this._errorLogProcessor = logProcessor ?? false;
99
+ this._errorLogProcessor = errorLog?.logProcessor ?? false;
102
100
 
103
101
  if (this._group) {
104
102
  this.setTag('group', this._group);
@@ -149,6 +147,10 @@ export class Observability {
149
147
  return this._mode;
150
148
  }
151
149
 
150
+ get group() {
151
+ return this._group;
152
+ }
153
+
152
154
  get enabled() {
153
155
  return this._mode !== 'disabled';
154
156
  }
@@ -157,18 +159,25 @@ export class Observability {
157
159
  // Tags
158
160
  //
159
161
 
160
- /**
161
- * Set a tag for all observability services.
162
- */
163
- setTag(key: string, value: string) {
164
- if (this.enabled && ERROR_TAGS.includes(key)) {
162
+ setTag(key: string, value: string, scope?: TagScope) {
163
+ if (this.enabled && (scope === undefined || scope === 'all' || scope === 'errors')) {
165
164
  sentrySetTag(key, value);
166
165
  }
167
-
168
- this._tags.set(key, value);
166
+ if (!scope) {
167
+ scope = 'all';
168
+ }
169
+ this._tags.set(key, { value, scope });
169
170
  }
170
171
 
171
- // TODO(nf): Combine with setDeviceTags.
172
+ addIPDataTelemetryTags = (ipData: IPData) => {
173
+ this.setTag('city', ipData.city, 'telemetry');
174
+ this.setTag('region', ipData.region, 'telemetry');
175
+ this.setTag('country', ipData.country, 'telemetry');
176
+ ipData.latitude && this.setTag('latitude', ipData.latitude.toString(), 'telemetry');
177
+ ipData.longitude && this.setTag('longitude', ipData.longitude.toString(), 'telemetry');
178
+ };
179
+
180
+ // TODO(wittjosiah): Improve privacy of telemetry identifiers. See `getTelemetryIdentifier`.
172
181
  async setIdentityTags(client: Client) {
173
182
  client.services.services.IdentityService!.queryIdentity().subscribe((idqr) => {
174
183
  if (!idqr?.identity?.identityKey) {
@@ -176,16 +185,9 @@ export class Observability {
176
185
  return;
177
186
  }
178
187
 
179
- // TODO(nf): check mode
180
- // TODO(nf): cardinality
181
- this.setTag('identityKey', idqr?.identity?.identityKey.truncate());
182
- if (idqr?.identity?.profile?.displayName) {
183
- this.setTag('username', idqr?.identity?.profile?.displayName);
184
- }
188
+ this.setTag('identityKey', idqr.identity.identityKey.truncate());
185
189
  });
186
- }
187
190
 
188
- async setDeviceTags(client: Client) {
189
191
  client.services.services.DevicesService!.queryDevices().subscribe((dqr) => {
190
192
  if (!dqr || !dqr.devices || dqr.devices.length === 0) {
191
193
  log('empty response from device service', { device: dqr });
@@ -213,7 +215,14 @@ export class Observability {
213
215
  if (this.enabled && this._secrets.DATADOG_API_KEY) {
214
216
  this._metrics = new DatadogMetrics({
215
217
  apiKey: this._secrets.DATADOG_API_KEY,
216
- getTags: () => this._tags,
218
+ getTags: () =>
219
+ Object.fromEntries(
220
+ Array.from(this._tags)
221
+ .filter(([key, value]) => {
222
+ return value.scope === 'all' || value.scope === 'metrics';
223
+ })
224
+ .map(([key, value]) => [key, value.value]),
225
+ ),
217
226
  // TODO(nf): move/refactor from telementryContext, needed to read CORS proxy
218
227
  config: this._config!,
219
228
  });
@@ -283,19 +292,27 @@ export class Observability {
283
292
  // scheduleTaskInterval(ctx, async () => updateSignalMetrics.emit(), DATADOG_IDLE_INTERVAL);
284
293
  }
285
294
 
286
- startSpacesMetrics(client: Client) {
287
- let spaces = client.spaces.get();
295
+ startSpacesMetrics(client: Client, namespace: string) {
296
+ const spaces = client.spaces.get();
288
297
  const subscriptions = new Map<string, { unsubscribe: () => void }>();
289
298
  this._ctx.onDispose(() => subscriptions.forEach((subscription) => subscription.unsubscribe()));
290
299
 
291
300
  const updateSpaceMetrics = new Event<Space>().debounce(SPACE_METRICS_MIN_INTERVAL);
292
- updateSpaceMetrics.on(this._ctx, async (space) => {
301
+ updateSpaceMetrics.on(this._ctx, async () => {
293
302
  log('send space update');
294
- for (const sp of mapSpaces(spaces, { truncateKeys: true })) {
295
- this.gauge('dxos.client.space.members', sp.members, { key: sp.key });
296
- this.gauge('dxos.client.space.objects', sp.objects, { key: sp.key });
297
- this.gauge('dxos.client.space.epoch', sp.epoch, { key: sp.key });
298
- this.gauge('dxos.client.space.currentDataMutations', sp.currentDataMutations, { key: sp.key });
303
+ for (const data of mapSpaces(spaces, { truncateKeys: true })) {
304
+ // Metrics
305
+ this.gauge('dxos.client.space.members', data.members, { key: data.key });
306
+ this.gauge('dxos.client.space.objects', data.objects, { key: data.key });
307
+ this.gauge('dxos.client.space.epoch', data.epoch, { key: data.key });
308
+ this.gauge('dxos.client.space.currentDataMutations', data.currentDataMutations, { key: data.key });
309
+
310
+ // Telemetry
311
+ this.event({
312
+ identityId: getTelemetryIdentifier(client),
313
+ name: `${namespace}.space.update`,
314
+ properties: data,
315
+ });
299
316
  }
300
317
  });
301
318
 
@@ -311,9 +328,7 @@ export class Observability {
311
328
  });
312
329
 
313
330
  client.spaces.subscribe({
314
- next: async () => {
315
- spaces = client.spaces.get();
316
- // spaces = await this.getSpaces(this._agent.client);
331
+ next: async (spaces) => {
317
332
  spaces
318
333
  .filter((space) => !subscriptions.has(space.key.toHex()))
319
334
  .forEach((space) => {
@@ -370,7 +385,14 @@ export class Observability {
370
385
  this._telemetry = new SegmentTelemetry({
371
386
  apiKey: this._secrets.TELEMETRY_API_KEY,
372
387
  batchSize: this._telemetryBatchSize,
373
- getTags: () => this._tags,
388
+ getTags: () =>
389
+ Object.fromEntries(
390
+ Array.from(this._tags)
391
+ .filter(([key, value]) => {
392
+ return value.scope === 'all' || value.scope === 'telemetry';
393
+ })
394
+ .map(([key, value]) => [key, value.value]),
395
+ ),
374
396
  });
375
397
  } else {
376
398
  log('segment disabled');
@@ -420,8 +442,8 @@ export class Observability {
420
442
  }
421
443
  // TODO(nf): is this different than passing as properties in options?
422
444
  this._tags.forEach((v, k) => {
423
- if (ERROR_TAGS.includes(k)) {
424
- sentrySetTag(k, v);
445
+ if (v.scope === 'all' || v.scope === 'errors') {
446
+ sentrySetTag(k, v.value);
425
447
  }
426
448
  });
427
449
  } else {
@@ -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: () => Map<string, string>;
13
+ private _getTags: () => { [key: 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: Object.fromEntries(this._getTags().entries()),
34
+ context: this._getTags(),
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
- ...Object.fromEntries(this._getTags().entries()),
47
+ ...this._getTags(),
48
48
  };
49
49
  (window as any).analytics?.track({
50
50
  ...options,
@@ -12,7 +12,7 @@ import { captureException } from '../sentry';
12
12
 
13
13
  export class SegmentTelemetry {
14
14
  private _analytics?: Analytics;
15
- private _getTags: () => Map<string, string>;
15
+ private _getTags: () => { [key: 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: Object.fromEntries(this._getTags().entries()) });
55
+ log('sending event to telemetry', { event, options, tags: this._getTags() });
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: Object.fromEntries(this._getTags().entries()),
64
+ context: this._getTags(),
65
65
  userId,
66
66
  anonymousId: anonymousId!,
67
67
  event,
@@ -6,7 +6,7 @@ export type SegmentTelemetryOptions = {
6
6
  apiKey?: string;
7
7
  batchSize?: number;
8
8
  enable?: boolean;
9
- getTags: () => Map<string, string>;
9
+ getTags: () => { [key: string]: string };
10
10
  };
11
11
 
12
12
  type CommonOptions = {
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../../src/util.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,SAAS,WAAY,KAAK,EAAE,YAAW,gBAAgB;;;;;;;;;;;;GAqCnE,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../../../src/util.ts"],"names":[],"mappings":";AAAA,EAAE;AACF,0BAA0B;AAC1B,EAAE;;;AASK,MAAM,SAAS,GAAG,CAAC,MAAe,EAAE,UAA4B,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,EAAE;IAChH,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;;QAC1B,4BAA4B;QAC5B,oEAAoE;QACpE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAA,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,mCAAI,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAElE,kGAAkG;QAClG,uDAAuD;QACvD,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC9C,MAAM,kBAAkB,GAAG,MAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,YAAY,0CAAE,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,aAAa,EAAE,mCAAI,CAAC,CAAC;QACpG,MAAM,KAAK,GAAG,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,YAAY,0CAAE,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC;QAC/D,yEAAyE;QACzE,MAAM,oBAAoB,GAAG,MAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,oBAAoB,0CAAE,aAAa,EAAE,mCAAI,CAAC,CAAC;QAClF,MAAM,kBAAkB,GAAG,MAAA,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,mBAAmB,0CAAE,aAAa,EAAE,mCAAI,CAAC,CAAC;QAE/E,OAAO;YACL,kCAAkC;YAClC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE;YACzB,IAAI,EAAE,KAAK,CAAC,MAAM;YAClB,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI;YAC3B,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM;YACnC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,MAAM;YACxC,OAAO;YACP,KAAK;YACL,gBAAgB;YAChB,kBAAkB;YAClB,oBAAoB;YACpB,kBAAkB;YAElB,0BAA0B;YAC1B,QAAQ,EAAE,CACR,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,oBAAoB,GAAG,kBAAkB,CAAC,GAAG,CAAC,kBAAkB,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC9G,GAAG,CACJ,CAAC,OAAO,CAAC,CAAC,CAAC;SACb,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AArCW,QAAA,SAAS,aAqCpB"}