@ms-cloudpack/telemetry 0.1.0

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 ADDED
@@ -0,0 +1,61 @@
1
+ # @ms-cloudpack/telemetry
2
+
3
+ A library for telemetry that gets reported to Application Insights by using Open Telemetry standards.
4
+
5
+ Currently, this library only supports [Tracing](https://opentelemetry.io/docs/concepts/signals/traces/). Metrics and Logs can be added later when needed.
6
+
7
+ ## Example usage
8
+
9
+ 1. Create a client:
10
+
11
+ ```ts
12
+ const telemetryClient = new TelemetryClient({
13
+ serviceNamespace: 'cloudpack',
14
+ serviceName: 'cli',
15
+ productVersion: '1.2.3',
16
+ instrumentationKey: '<Application insights Instrumentation Key>',
17
+ logLevel: 'verbose',
18
+ });
19
+ ```
20
+
21
+ 2. Add shared attributes
22
+
23
+ ```ts
24
+ telemetryClient.setSharedSpanAttribute('sessionId', 'my-session-id');
25
+ ```
26
+
27
+ These attributes will get added to all spans.
28
+
29
+ 3. Get the tracer
30
+
31
+ ```ts
32
+ const tracer = telemetryClient.getTracer();
33
+ ```
34
+
35
+ 4. Create a span / add events / end the span
36
+
37
+ ```ts
38
+ const span = tracer.createSpan('my-unit-of-work');
39
+ // do work
40
+ span.addEvent('my-event', { myCustomAttribute: 'my-custom-value' });
41
+
42
+ try {
43
+ // do more work
44
+ } catch (err) {
45
+ span.recordException(err);
46
+ }
47
+
48
+ // end the span when the unit-of-work completed.
49
+ span.end();
50
+ ```
51
+
52
+ [Lear more about spans](https://opentelemetry.io/docs/instrumentation/js/instrumentation/#create-spans)
53
+
54
+ 5. Shutdown TelemetryClient before application exists
55
+
56
+ ```ts
57
+ telemetryClient.shutdown();
58
+ ```
59
+
60
+ - This will force flush all the remaining data to remote servers and prevent more data to be collected.
61
+ - Make sure that all spans have been ended before shuting down the telemetry client
@@ -0,0 +1,12 @@
1
+ import type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';
2
+ /**
3
+ * Span processor that adds shared attributes to all spans.
4
+ */
5
+ export declare class SpanEnrichingProcessor implements SpanProcessor {
6
+ private getSharedSpanAttributes;
7
+ constructor(getSharedSpanAttributes: () => Record<string, string>);
8
+ forceFlush(): Promise<void>;
9
+ shutdown(): Promise<void>;
10
+ onStart(): void;
11
+ onEnd(span: ReadableSpan): void;
12
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Span processor that adds shared attributes to all spans.
3
+ */
4
+ export class SpanEnrichingProcessor {
5
+ constructor(getSharedSpanAttributes) {
6
+ this.getSharedSpanAttributes = getSharedSpanAttributes;
7
+ }
8
+ forceFlush() {
9
+ return Promise.resolve();
10
+ }
11
+ shutdown() {
12
+ return Promise.resolve();
13
+ }
14
+ onStart() {
15
+ // noop
16
+ }
17
+ onEnd(span) {
18
+ for (const [key, value] of Object.entries(this.getSharedSpanAttributes())) {
19
+ span.attributes[key] = value;
20
+ }
21
+ }
22
+ }
23
+ //# sourceMappingURL=SpanEnrichingProcessor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpanEnrichingProcessor.js","sourceRoot":"","sources":["../src/SpanEnrichingProcessor.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,OAAO,sBAAsB;IACjC,YAAoB,uBAAqD;QAArD,4BAAuB,GAAvB,uBAAuB,CAA8B;IAAG,CAAC;IAE7E,UAAU;QACR,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,QAAQ;QACN,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,OAAO;IACT,CAAC;IAED,KAAK,CAAC,IAAkB;QACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,uBAAuB,EAAE,CAAC,EAAE;YACzE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;SAC9B;IACH,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import type { Tracer } from '@opentelemetry/api';
2
+ import type { TelemetryOptions } from './types.js';
3
+ export declare class TelemetryClient {
4
+ private readonly appInsightsClient;
5
+ private readonly sharedSpanAttributes;
6
+ constructor(options: TelemetryOptions);
7
+ /**
8
+ * Sets a shared attribute that will be added to all spans.
9
+ * @param key - key of the attribute
10
+ * @param value - value of the attribute
11
+ */
12
+ setSharedSpanAttribute(key: string, value: string): void;
13
+ /**
14
+ * Flushes all telemetry and shuts down the telemetry client.
15
+ */
16
+ shutdown(): Promise<void>;
17
+ /**
18
+ * Gets the OpenTelemetry tracer.
19
+ * @returns the OpenTelemetry tracer
20
+ */
21
+ getTracer(): Tracer;
22
+ }
@@ -0,0 +1,41 @@
1
+ import { ApplicationInsightsClient } from 'applicationinsights';
2
+ import { convertLogLevel } from './convertLogLevel.js';
3
+ import { getAppInsightsConfig } from './getAppInsightsConfig.js';
4
+ import { SpanEnrichingProcessor } from './SpanEnrichingProcessor.js';
5
+ export class TelemetryClient {
6
+ constructor(options) {
7
+ this.sharedSpanAttributes = {};
8
+ const config = getAppInsightsConfig(options);
9
+ this.appInsightsClient = new ApplicationInsightsClient(config);
10
+ this.appInsightsClient.getLogger().updateLogLevel(convertLogLevel(options.logLevel));
11
+ // Set shared attributes
12
+ this.setSharedSpanAttribute('version', options.productVersion);
13
+ // Set SpanEnrichingProcessor to add shared attributes to all spans
14
+ const nodeTraceProvider = this.appInsightsClient.getTraceHandler().getTracerProvider();
15
+ nodeTraceProvider.addSpanProcessor(new SpanEnrichingProcessor(() => this.sharedSpanAttributes));
16
+ this.appInsightsClient.start();
17
+ }
18
+ /**
19
+ * Sets a shared attribute that will be added to all spans.
20
+ * @param key - key of the attribute
21
+ * @param value - value of the attribute
22
+ */
23
+ setSharedSpanAttribute(key, value) {
24
+ this.sharedSpanAttributes[key] = value;
25
+ }
26
+ /**
27
+ * Flushes all telemetry and shuts down the telemetry client.
28
+ */
29
+ async shutdown() {
30
+ await this.appInsightsClient?.flush();
31
+ await this.appInsightsClient?.shutdown();
32
+ }
33
+ /**
34
+ * Gets the OpenTelemetry tracer.
35
+ * @returns the OpenTelemetry tracer
36
+ */
37
+ getTracer() {
38
+ return this.appInsightsClient?.getTraceHandler().getTracer();
39
+ }
40
+ }
41
+ //# sourceMappingURL=TelemetryClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TelemetryClient.js","sourceRoot":"","sources":["../src/TelemetryClient.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAGrE,MAAM,OAAO,eAAe;IAI1B,YAAY,OAAyB;QAFpB,yBAAoB,GAA2B,EAAE,CAAC;QAGjE,MAAM,MAAM,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAE7C,IAAI,CAAC,iBAAiB,GAAG,IAAI,yBAAyB,CAAC,MAAM,CAAC,CAAC;QAE/D,IAAI,CAAC,iBAAiB,CAAC,SAAS,EAAE,CAAC,cAAc,CAAC,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;QAErF,wBAAwB;QACxB,IAAI,CAAC,sBAAsB,CAAC,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;QAE/D,mEAAmE;QACnE,MAAM,iBAAiB,GAAuB,IAAI,CAAC,iBAAiB,CAAC,eAAe,EAAE,CAAC,iBAAiB,EAAE,CAAC;QAC3G,iBAAiB,CAAC,gBAAgB,CAAC,IAAI,sBAAsB,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAEhG,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACI,sBAAsB,CAAC,GAAW,EAAE,KAAa;QACtD,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACzC,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,QAAQ;QACnB,MAAM,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC;QACtC,MAAM,IAAI,CAAC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACI,SAAS;QACd,OAAO,IAAI,CAAC,iBAAiB,EAAE,eAAe,EAAE,CAAC,SAAS,EAAE,CAAC;IAC/D,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import { DiagLogLevel } from '@opentelemetry/api';
2
+ import type { LogLevel } from './types.js';
3
+ export declare function convertLogLevel(logLevel?: LogLevel): DiagLogLevel;
@@ -0,0 +1,12 @@
1
+ import { DiagLogLevel } from '@opentelemetry/api';
2
+ export function convertLogLevel(logLevel) {
3
+ switch (logLevel) {
4
+ case 'debug':
5
+ return DiagLogLevel.DEBUG;
6
+ case 'verbose':
7
+ return DiagLogLevel.VERBOSE;
8
+ default:
9
+ return DiagLogLevel.WARN;
10
+ }
11
+ }
12
+ //# sourceMappingURL=convertLogLevel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convertLogLevel.js","sourceRoot":"","sources":["../src/convertLogLevel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,MAAM,UAAU,eAAe,CAAC,QAAmB;IACjD,QAAQ,QAAQ,EAAE;QAChB,KAAK,OAAO;YACV,OAAO,YAAY,CAAC,KAAK,CAAC;QAC5B,KAAK,SAAS;YACZ,OAAO,YAAY,CAAC,OAAO,CAAC;QAC9B;YACE,OAAO,YAAY,CAAC,IAAI,CAAC;KAC5B;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ApplicationInsightsConfig } from 'applicationinsights';
2
+ import type { TelemetryOptions } from './types.js';
3
+ export declare function getAppInsightsConfig(options: TelemetryOptions): ApplicationInsightsConfig;
@@ -0,0 +1,43 @@
1
+ import { Resource } from '@opentelemetry/resources';
2
+ import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
3
+ import { ApplicationInsightsConfig } from 'applicationinsights';
4
+ import { createHash } from 'crypto';
5
+ import { getComputerName } from './getComputerName.js';
6
+ function sha256(input) {
7
+ return createHash('sha256').update(input).digest('hex');
8
+ }
9
+ export function getAppInsightsConfig(options) {
10
+ const config = new ApplicationInsightsConfig();
11
+ config.connectionString = `InstrumentationKey=${options.instrumentationKey}`;
12
+ config.enableAutoCollectExceptions = false;
13
+ config.enableAutoCollectHeartbeat = false;
14
+ config.enableAutoCollectPerformance = false;
15
+ config.enableAutoCollectStandardMetrics = false;
16
+ config.instrumentations = {
17
+ http: {
18
+ enabled: false,
19
+ },
20
+ azureSdk: {
21
+ enabled: false,
22
+ },
23
+ };
24
+ config.logInstrumentations = {
25
+ bunyan: {
26
+ enabled: false,
27
+ },
28
+ console: {
29
+ enabled: false,
30
+ },
31
+ winston: {
32
+ enabled: false,
33
+ },
34
+ };
35
+ config.resource = new Resource({
36
+ // Use a hash of the computer name to avoid PII
37
+ [SemanticResourceAttributes.SERVICE_INSTANCE_ID]: sha256(getComputerName()),
38
+ [SemanticResourceAttributes.SERVICE_NAME]: options.serviceName,
39
+ [SemanticResourceAttributes.SERVICE_NAMESPACE]: options.serviceNamespace,
40
+ });
41
+ return config;
42
+ }
43
+ //# sourceMappingURL=getAppInsightsConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAppInsightsConfig.js","sourceRoot":"","sources":["../src/getAppInsightsConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,SAAS,MAAM,CAAC,KAAa;IAC3B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAyB;IAC5D,MAAM,MAAM,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAC/C,MAAM,CAAC,gBAAgB,GAAG,sBAAsB,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAC7E,MAAM,CAAC,2BAA2B,GAAG,KAAK,CAAC;IAC3C,MAAM,CAAC,0BAA0B,GAAG,KAAK,CAAC;IAC1C,MAAM,CAAC,4BAA4B,GAAG,KAAK,CAAC;IAC5C,MAAM,CAAC,gCAAgC,GAAG,KAAK,CAAC;IAChD,MAAM,CAAC,gBAAgB,GAAG;QACxB,IAAI,EAAE;YACJ,OAAO,EAAE,KAAK;SACf;QACD,QAAQ,EAAE;YACR,OAAO,EAAE,KAAK;SACf;KACF,CAAC;IACF,MAAM,CAAC,mBAAmB,GAAG;QAC3B,MAAM,EAAE;YACN,OAAO,EAAE,KAAK;SACf;QACD,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;SACf;QACD,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;SACf;KACF,CAAC;IAEF,MAAM,CAAC,QAAQ,GAAG,IAAI,QAAQ,CAAC;QAC7B,+CAA+C;QAC/C,CAAC,0BAA0B,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3E,CAAC,0BAA0B,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,WAAW;QAC9D,CAAC,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,gBAAgB;KACzE,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Each OS has a different way of getting the computer name.
3
+ * @returns computer name for the current OS, or the hostname if it cannot be determined
4
+ */
5
+ export declare function getComputerName(): string;
@@ -0,0 +1,21 @@
1
+ import { execSync } from 'child_process';
2
+ import { hostname } from 'os';
3
+ /**
4
+ * Each OS has a different way of getting the computer name.
5
+ * @returns computer name for the current OS, or the hostname if it cannot be determined
6
+ */
7
+ export function getComputerName() {
8
+ try {
9
+ const computerNameGetters = {
10
+ win32: () => process.env.COMPUTERNAME,
11
+ darwin: () => execSync('scutil --get ComputerName').toString(),
12
+ };
13
+ const computerName = computerNameGetters[process.platform]?.() || hostname();
14
+ return computerName.trim();
15
+ }
16
+ catch (e) {
17
+ console.warn('Unable to get OS specific computer name for telemetry, falling back to hostname', e);
18
+ return hostname();
19
+ }
20
+ }
21
+ //# sourceMappingURL=getComputerName.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getComputerName.js","sourceRoot":"","sources":["../src/getComputerName.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAE9B;;;GAGG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI;QACF,MAAM,mBAAmB,GAA+D;YACtF,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY;YACrC,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,QAAQ,EAAE;SAC/D,CAAC;QAEF,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,QAAQ,EAAE,CAAC;QAE7E,OAAO,YAAY,CAAC,IAAI,EAAE,CAAC;KAC5B;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,IAAI,CAAC,iFAAiF,EAAE,CAAC,CAAC,CAAC;QAEnG,OAAO,QAAQ,EAAE,CAAC;KACnB;AACH,CAAC"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type { Span, Tracer } from '@opentelemetry/api';
2
+ export { TelemetryClient } from './TelemetryClient.js';
3
+ export type { LogLevel, TelemetryOptions } from './types.js';
package/lib/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { TelemetryClient } from './TelemetryClient.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,11 @@
1
+ // This file is read by tools that parse documentation comments conforming to the TSDoc standard.
2
+ // It should be published with your NPM package. It should not be tracked by Git.
3
+ {
4
+ "tsdocVersion": "0.12",
5
+ "toolPackages": [
6
+ {
7
+ "packageName": "@microsoft/api-extractor",
8
+ "packageVersion": "7.34.4"
9
+ }
10
+ ]
11
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ export type LogLevel = 'debug' | 'verbose' | 'default';
2
+ export interface TelemetryOptions {
3
+ /**
4
+ * The instrumentation key for the Application Insights.
5
+ */
6
+ instrumentationKey: string;
7
+ /**
8
+ * The log level for the telemetry client.
9
+ */
10
+ logLevel?: LogLevel;
11
+ /**
12
+ * The version of the product. This will be added as a shared attribute to all spans.
13
+ */
14
+ productVersion: string;
15
+ /**
16
+ * A string value having a meaning that helps to distinguish a group of services, for example the team name that owns a group of services.
17
+ * Possible values: cloudpack, cloudpack.services, etc.
18
+ */
19
+ serviceNamespace: string;
20
+ /**
21
+ * Logic name of the service
22
+ * Possible values: cli, bundler-service, etc.
23
+ */
24
+ serviceName: string;
25
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@ms-cloudpack/telemetry",
3
+ "version": "0.1.0",
4
+ "description": "Helpers for logging tasks to the console.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "types": "./lib/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./lib/index.d.ts",
12
+ "import": "./lib/index.js"
13
+ }
14
+ },
15
+ "dependencies": {
16
+ "@opentelemetry/api": "^1.4.0",
17
+ "@opentelemetry/resources": "^1.9.1",
18
+ "@opentelemetry/sdk-trace-node": "^1.9.1",
19
+ "@opentelemetry/semantic-conventions": "^1.9.1",
20
+ "applicationinsights": "^3.0.0-beta.2"
21
+ },
22
+ "devDependencies": {
23
+ "@ms-cloudpack/eslint-config-base": "*",
24
+ "@ms-cloudpack/scripts": "*"
25
+ },
26
+ "scripts": {
27
+ "api:update": "cloudpack-scripts api-update",
28
+ "api": "cloudpack-scripts api",
29
+ "build:watch": "cloudpack-scripts build-watch",
30
+ "build": "cloudpack-scripts build",
31
+ "lint:update": "cloudpack-scripts lint-update",
32
+ "lint": "cloudpack-scripts lint"
33
+ },
34
+ "files": [
35
+ "lib/**/!(*.test.*)"
36
+ ]
37
+ }