@pagopa/azure-tracing 0.1.0 → 0.2.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 CHANGED
@@ -1,18 +1,109 @@
1
1
  # @pagopa/azure-tracing
2
2
 
3
- This package provides a set of tools to integrate Azure Application Insights with OpenTelemetry for tracing and telemetry in Node.js applications.
3
+ This package provides utilities to seamlessly integrate [Azure Monitor's Application Insights](https://learn.microsoft.com/en-us/azure/azure-monitor/app/app-insights-overview)
4
+ with OpenTelemetry for distributed tracing and telemetry in Node.js applications, especially in Azure Functions environments.
4
5
 
5
- ## Usage
6
- 1. Install the package:
6
+ ## Installation
7
7
 
8
- ```bash
9
- yarn add @pagopa/azure-tracing
10
- ```
8
+ Install the package using:
11
9
 
12
- If you need to enable tracing on Azure Functions, you can simply add the following environment variable:
10
+ ```bash
11
+ yarn add @pagopa/azure-tracing
13
12
  ```
14
- "NODE_OPTIONS": "--import @pagopa/azure-tracing",
13
+
14
+ ## Getting Started
15
+
16
+ ### Instrumenting Azure Functions
17
+
18
+ Currently, [ECMAScript Modules (ESM) support in OpenTelemetry is still experimental](https://github.com/open-telemetry/opentelemetry-js/blob/966ac176af249d86de6cb10feac2306062846768/doc/esm-support.md),
19
+ which makes direct instrumentation of Azure Functions a bit tricky.
20
+ So, if you have a Node.js project ESM based (`"type": "module"` in the `package.json`), to work around this, you have to preload the instrumentation logic at runtime using the `NODE_OPTIONS` environment variable.
21
+
22
+ > [!NOTE]
23
+ > In case you have a CJS project (`"type": "commonjs"` in the `package.json`), you could use the [`opentelemetry-js` library](https://github.com/open-telemetry/opentelemetry-js?tab=readme-ov-file#quick-start)
24
+ > to instrument the application, making sure the OpenTelemetry SDK is initialized before the Azure Functions runtime starts.
25
+ > This package is useful for ESM projects only, where the instrumentation logic needs to be preloaded.
26
+
27
+ This package provides a wrapper that simplifies this setup.
28
+
29
+ #### Step 1: Enable Tracing via NODE_OPTIONS
30
+
31
+ Set the following environment variable to preload the instrumentation:
32
+
33
+ ```bash
34
+ NODE_OPTIONS=--import @pagopa/azure-tracing
35
+ ```
36
+
37
+ This will automatically enable OpenTelemetry tracing and route telemetry to Azure Monitor.
38
+
39
+ For more background on this workaround, see:
40
+
41
+ - [Issue #4845 - OpenTelemetry JS](https://github.com/open-telemetry/opentelemetry-js/issues/4845#issuecomment-2253556217)
42
+ - [Issue #4933 - OpenTelemetry JS](https://github.com/open-telemetry/opentelemetry-js/issues/4933)
43
+
44
+ In order to enable tracing, you also need to set the following environment variables:
45
+
46
+ | **Name** | **Required** | **Default** |
47
+ | ----------------------------------- | ------------ | ----------- |
48
+ | **APPINSIGHTS_SAMPLING_PERCENTAGE** | false | 5 |
49
+ | **APPINSIGHTS_CONNECTION_STRING** | true | - |
50
+
51
+ #### Step 2: Register Azure Function Lifecycle Hooks
52
+
53
+ Due to known limitations with the `azure-functions-nodejs-opentelemetry` library,
54
+ it's necessary to manually register lifecycle hooks to ensure proper dependency correlation in telemetry.
55
+
56
+ For more background on this workaround, see:
57
+
58
+ - [Issue #8 - azure-functions-nodejs-opentelemetry](https://github.com/Azure/azure-functions-nodejs-opentelemetry/issues/8)
59
+ - [Issue #33242 - azure-sdk-for-js](https://github.com/Azure/azure-sdk-for-js/issues/33242)
60
+
61
+ Add the following snippet to your main entry point (e.g., `main.ts`):
62
+
63
+ ```ts
64
+ import { app } from "@azure/functions"; // Replace with your actual app import
65
+ import { registerAzureFunctionHooks } from "@pagopa/azure-tracing/azure-functions";
66
+ ...
67
+ registerAzureFunctionHooks(app);
68
+ ...
69
+ ```
70
+
71
+ ### Enabling Azure Monitor Telemetry
72
+
73
+ If you want to enable Azure Monitor telemetry in your application, and you don't have those issues previously described, you can do so in the following ways:
74
+
75
+ ```ts
76
+ import { initAzureMonitor } from "@pagopa/azure-tracing/azure-monitor";
77
+ import { AzureMonitorOpenTelemetryOptions } from "@azure/monitor-opentelemetry";
78
+ import { ExpressInstrumentation } from "@opentelemetry/instrumentation-express";
79
+ ...
80
+ const config: AzureMonitorOpenTelemetryOptions = {} // A valid AzureMonitorOpenTelemetryOptions object
81
+ const instrumentations = [new ExpressInstrumentation()] // A list of custom OpenTelemetry instrumentations
82
+ initAzureMonitor(instrumentations, config);
83
+ ...
84
+ ```
85
+
86
+ > [!NOTE]
87
+ > the use of `ExpressInstrumentation` is just for example, you can use any other OpenTelemetry instrumentation you want.
88
+
89
+ or, if you want to use the default configuration:
90
+
91
+ ```ts
92
+ import { initAzureMonitor } from "@pagopa/azure-tracing/azure-monitor";
93
+ ...
94
+ initAzureMonitor();
95
+ ```
96
+
97
+ ### Logging Custom Events
98
+
99
+ You can log custom events for additional observability using the `emitCustomEvent` function.
100
+ This utility accepts an event name and an optional payload, returning a logger function that also accepts a component or handler name.
101
+
102
+ ```ts
103
+ import { emitCustomEvent } from "@pagopa/azure-tracing/logger";
104
+ ...
105
+ emitCustomEvent("taskCreated", { id: task.id })("CreateTaskHandler");
106
+ ...
15
107
  ```
16
- This will instrument you Azure Function with OpenTelemetry and Application Insights.
17
108
 
18
- With this approach, you don't need to add any code to your Azure Function. The instrumentation will be automatically applied when the function is executed.
109
+ This is especially useful for tracing domain-specific actions (e.g., resource creation, user actions, error tracking).
@@ -1,4 +1,5 @@
1
- import { app } from "@azure/functions";
1
+ import { app } from '@azure/functions';
2
+
2
3
  /**
3
4
  * Registers Azure Function hooks to enable OpenTelemetry tracing.
4
5
  * These hooks extract trace context from the Azure Function context
@@ -9,7 +10,13 @@ import { app } from "@azure/functions";
9
10
  * to add the [azure-functions-nodejs-opentelemetry](https://github.com/Azure/azure-functions-nodejs-opentelemetry/tree/main)
10
11
  * to the instrumentation package and remove this workaround.
11
12
  *
12
- * @param {Object} options - An object containing the Azure Functions `hook` object.
13
- * @param {Function} options.hook - The Azure Functions `hook` object from the `app` module.
13
+ * @example
14
+ * In your application, where you add the Azure Functions hooks (like `app.http() and so on), you
15
+ * can add the following code:
16
+ *
17
+ * registerAzureFunctionHooks(app);
18
+ *
14
19
  */
15
- export declare const registerAzureFunctionHooks: ({ hook }: typeof app) => void;
20
+ declare const registerAzureFunctionHooks: ({ hook }: typeof app) => void;
21
+
22
+ export { registerAzureFunctionHooks };
@@ -0,0 +1,20 @@
1
+ // src/azure/functions/hooks.ts
2
+ import { context as otelContext, propagation } from "@opentelemetry/api";
3
+ var registerAzureFunctionHooks = ({ hook }) => {
4
+ hook.preInvocation((context) => {
5
+ const traceContext = context.invocationContext.traceContext;
6
+ if (traceContext) {
7
+ context.functionHandler = otelContext.bind(
8
+ propagation.extract(otelContext.active(), {
9
+ traceparent: traceContext.traceParent,
10
+ tracestate: traceContext.traceState
11
+ }),
12
+ context.functionHandler
13
+ );
14
+ }
15
+ });
16
+ };
17
+ export {
18
+ registerAzureFunctionHooks
19
+ };
20
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/azure/functions/hooks.ts"],"sourcesContent":["import { app } from \"@azure/functions\";\nimport { context as otelContext, propagation } from \"@opentelemetry/api\";\n\n/**\n * Registers Azure Function hooks to enable OpenTelemetry tracing.\n * These hooks extract trace context from the Azure Function context\n * and bind it to the function handler, ensuring that traces are\n * properly propagated across function invocations.\n *\n * Once the issues that causes this workaround are resolved, it will be possible\n * to add the [azure-functions-nodejs-opentelemetry](https://github.com/Azure/azure-functions-nodejs-opentelemetry/tree/main)\n * to the instrumentation package and remove this workaround.\n *\n * @example\n * In your application, where you add the Azure Functions hooks (like `app.http() and so on), you\n * can add the following code:\n *\n * registerAzureFunctionHooks(app);\n *\n */\nexport const registerAzureFunctionHooks = ({ hook }: typeof app) => {\n hook.preInvocation((context) => {\n const traceContext = context.invocationContext.traceContext;\n if (traceContext) {\n context.functionHandler = otelContext.bind(\n propagation.extract(otelContext.active(), {\n traceparent: traceContext.traceParent,\n tracestate: traceContext.traceState,\n }),\n context.functionHandler,\n );\n }\n });\n};\n"],"mappings":";AACA,SAAS,WAAW,aAAa,mBAAmB;AAmB7C,IAAM,6BAA6B,CAAC,EAAE,KAAK,MAAkB;AAClE,OAAK,cAAc,CAAC,YAAY;AAC9B,UAAM,eAAe,QAAQ,kBAAkB;AAC/C,QAAI,cAAc;AAChB,cAAQ,kBAAkB,YAAY;AAAA,QACpC,YAAY,QAAQ,YAAY,OAAO,GAAG;AAAA,UACxC,aAAa,aAAa;AAAA,UAC1B,YAAY,aAAa;AAAA,QAC3B,CAAC;AAAA,QACD,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
@@ -0,0 +1,75 @@
1
+ // src/azure/functions/index.mts
2
+ import { createAddHookMessageChannel } from "import-in-the-middle";
3
+ import { register } from "module";
4
+ import { metrics, trace } from "@opentelemetry/api";
5
+ import { registerInstrumentations } from "@opentelemetry/instrumentation";
6
+
7
+ // src/azure/monitor/start-from-env.ts
8
+ import { useAzureMonitor } from "@azure/monitor-opentelemetry";
9
+
10
+ // src/azure/monitor/env.ts
11
+ import { createEnv } from "@t3-oss/env-core";
12
+ import { z } from "zod";
13
+ var loadEnv = () => createEnv({
14
+ emptyStringAsUndefined: true,
15
+ onValidationError: (errors) => {
16
+ throw new Error(
17
+ errors.map(
18
+ (error) => `Environment variable ${error.path} - ${error.message}`
19
+ ).join(", ")
20
+ );
21
+ },
22
+ runtimeEnv: process.env,
23
+ server: {
24
+ APPINSIGHTS_CONNECTION_STRING: z.string().describe("The connection string for Application Insights."),
25
+ APPINSIGHTS_SAMPLING_PERCENTAGE: z.optional(
26
+ z.coerce.number().min(0).max(100).default(5).describe(
27
+ "Application Insights sampling percentage between 0 and 100. If not set, defaults to 5."
28
+ )
29
+ ).transform((value) => {
30
+ const percentage = Number(value);
31
+ return isNaN(percentage) ? 5 : percentage;
32
+ }).transform((value) => value / 100)
33
+ }
34
+ });
35
+
36
+ // src/azure/monitor/start-from-env.ts
37
+ var initFromEnv = () => {
38
+ const env = loadEnv();
39
+ return useAzureMonitor({
40
+ azureMonitorExporterOptions: {
41
+ connectionString: env.APPINSIGHTS_CONNECTION_STRING
42
+ },
43
+ enableLiveMetrics: true,
44
+ samplingRatio: env.APPINSIGHTS_SAMPLING_PERCENTAGE
45
+ });
46
+ };
47
+
48
+ // src/azure/opentelemetry/azure-undici-instrumentation.ts
49
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
50
+ var registerUndiciInstrumentation = () => new UndiciInstrumentation({
51
+ requestHook: (span, requestInfo) => {
52
+ const { method, origin, path } = requestInfo;
53
+ span.setAttributes({
54
+ "http.host": origin,
55
+ "http.method": method,
56
+ "http.target": path,
57
+ "http.url": `${origin}${path}`
58
+ });
59
+ },
60
+ responseHook: (span, { response }) => {
61
+ span.setAttribute("http.status_code", response.statusCode);
62
+ }
63
+ });
64
+
65
+ // src/azure/functions/index.mts
66
+ var { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel();
67
+ register("import-in-the-middle/hook.mjs", import.meta.url, registerOptions);
68
+ initFromEnv();
69
+ registerInstrumentations({
70
+ instrumentations: [registerUndiciInstrumentation()],
71
+ meterProvider: metrics.getMeterProvider(),
72
+ tracerProvider: trace.getTracerProvider()
73
+ });
74
+ await waitForAllMessagesAcknowledged();
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/azure/functions/index.mts","../../src/azure/monitor/start-from-env.ts","../../src/azure/monitor/env.ts","../../src/azure/opentelemetry/azure-undici-instrumentation.ts"],"sourcesContent":["import { createAddHookMessageChannel } from \"import-in-the-middle\";\nimport { register } from \"module\";\n\nconst { registerOptions, waitForAllMessagesAcknowledged } =\n createAddHookMessageChannel();\n\nregister(\"import-in-the-middle/hook.mjs\", import.meta.url, registerOptions);\n\nimport { metrics, trace } from \"@opentelemetry/api\";\nimport { registerInstrumentations } from \"@opentelemetry/instrumentation\";\n\nimport { initFromEnv } from \"../monitor/start-from-env\";\nimport { registerUndiciInstrumentation } from \"../opentelemetry/azure-undici-instrumentation\";\n\ninitFromEnv();\n\nregisterInstrumentations({\n instrumentations: [registerUndiciInstrumentation()],\n meterProvider: metrics.getMeterProvider(),\n tracerProvider: trace.getTracerProvider(),\n});\n\nawait waitForAllMessagesAcknowledged();\n","import { useAzureMonitor } from \"@azure/monitor-opentelemetry\";\n\nimport { loadEnv } from \"./env\";\n\nexport const initFromEnv = () => {\n const env = loadEnv();\n\n return useAzureMonitor({\n azureMonitorExporterOptions: {\n connectionString: env.APPINSIGHTS_CONNECTION_STRING,\n },\n enableLiveMetrics: true,\n samplingRatio: env.APPINSIGHTS_SAMPLING_PERCENTAGE,\n });\n};\n","// Load and type check environment variables on runtime\nimport { createEnv } from \"@t3-oss/env-core\";\nimport { z } from \"zod\";\n\nexport const loadEnv = () =>\n createEnv({\n emptyStringAsUndefined: true,\n onValidationError: (errors) => {\n throw new Error(\n errors\n .map(\n (error) => `Environment variable ${error.path} - ${error.message}`,\n )\n .join(\", \"),\n );\n },\n runtimeEnv: process.env,\n server: {\n APPINSIGHTS_CONNECTION_STRING: z\n .string()\n .describe(\"The connection string for Application Insights.\"),\n APPINSIGHTS_SAMPLING_PERCENTAGE: z\n .optional(\n z.coerce\n .number()\n .min(0)\n .max(100)\n .default(5)\n .describe(\n \"Application Insights sampling percentage between 0 and 100. If not set, defaults to 5.\",\n ),\n )\n .transform((value) => {\n const percentage = Number(value);\n return isNaN(percentage) ? 5 : percentage;\n })\n .transform((value) => value / 100),\n },\n });\n","import { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\n\n// instrument native node fetch\nexport const registerUndiciInstrumentation = () =>\n new UndiciInstrumentation({\n requestHook: (span, requestInfo) => {\n const { method, origin, path } = requestInfo;\n // Default instrumented attributes don't feed well into AppInsights,\n // so we set them manually.\n span.setAttributes({\n \"http.host\": origin,\n \"http.method\": method,\n \"http.target\": path,\n \"http.url\": `${origin}${path}`,\n });\n },\n responseHook: (span, { response }) => {\n // Same as above, set the status code manually.\n span.setAttribute(\"http.status_code\", response.statusCode);\n },\n });\n"],"mappings":";AAAA,SAAS,mCAAmC;AAC5C,SAAS,gBAAgB;AAOzB,SAAS,SAAS,aAAa;AAC/B,SAAS,gCAAgC;;;ACTzC,SAAS,uBAAuB;;;ACChC,SAAS,iBAAiB;AAC1B,SAAS,SAAS;AAEX,IAAM,UAAU,MACrB,UAAU;AAAA,EACR,wBAAwB;AAAA,EACxB,mBAAmB,CAAC,WAAW;AAC7B,UAAM,IAAI;AAAA,MACR,OACG;AAAA,QACC,CAAC,UAAU,wBAAwB,MAAM,IAAI,MAAM,MAAM,OAAO;AAAA,MAClE,EACC,KAAK,IAAI;AAAA,IACd;AAAA,EACF;AAAA,EACA,YAAY,QAAQ;AAAA,EACpB,QAAQ;AAAA,IACN,+BAA+B,EAC5B,OAAO,EACP,SAAS,iDAAiD;AAAA,IAC7D,iCAAiC,EAC9B;AAAA,MACC,EAAE,OACC,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP,QAAQ,CAAC,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ,EACC,UAAU,CAAC,UAAU;AACpB,YAAM,aAAa,OAAO,KAAK;AAC/B,aAAO,MAAM,UAAU,IAAI,IAAI;AAAA,IACjC,CAAC,EACA,UAAU,CAAC,UAAU,QAAQ,GAAG;AAAA,EACrC;AACF,CAAC;;;ADlCI,IAAM,cAAc,MAAM;AAC/B,QAAM,MAAM,QAAQ;AAEpB,SAAO,gBAAgB;AAAA,IACrB,6BAA6B;AAAA,MAC3B,kBAAkB,IAAI;AAAA,IACxB;AAAA,IACA,mBAAmB;AAAA,IACnB,eAAe,IAAI;AAAA,EACrB,CAAC;AACH;;;AEdA,SAAS,6BAA6B;AAG/B,IAAM,gCAAgC,MAC3C,IAAI,sBAAsB;AAAA,EACxB,aAAa,CAAC,MAAM,gBAAgB;AAClC,UAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI;AAGjC,SAAK,cAAc;AAAA,MACjB,aAAa;AAAA,MACb,eAAe;AAAA,MACf,eAAe;AAAA,MACf,YAAY,GAAG,MAAM,GAAG,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EACA,cAAc,CAAC,MAAM,EAAE,SAAS,MAAM;AAEpC,SAAK,aAAa,oBAAoB,SAAS,UAAU;AAAA,EAC3D;AACF,CAAC;;;AHjBH,IAAM,EAAE,iBAAiB,+BAA+B,IACtD,4BAA4B;AAE9B,SAAS,iCAAiC,YAAY,KAAK,eAAe;AAQ1E,YAAY;AAEZ,yBAAyB;AAAA,EACvB,kBAAkB,CAAC,8BAA8B,CAAC;AAAA,EAClD,eAAe,QAAQ,iBAAiB;AAAA,EACxC,gBAAgB,MAAM,kBAAkB;AAC1C,CAAC;AAED,MAAM,+BAA+B;","names":[]}
@@ -1,4 +1,6 @@
1
- import { Instrumentation } from "@opentelemetry/instrumentation";
1
+ import { AzureMonitorOpenTelemetryOptions } from '@azure/monitor-opentelemetry';
2
+ import { Instrumentation } from '@opentelemetry/instrumentation';
3
+
2
4
  /**
3
5
  * Initialize the Azure Monitor with the given instrumentations.
4
6
  * Call this function to register the instrumentations with the Azure Monitor.
@@ -14,5 +16,8 @@ import { Instrumentation } from "@opentelemetry/instrumentation";
14
16
  *
15
17
  * initAzureMonitor();
16
18
  * @param instrumentations the list of instrumentations to register with the Azure Monitor.
19
+ * @param azureMonitorOptions custom configuration for Azure Monitor. If not provided, then it will be initialized using the environment variables.
17
20
  */
18
- export declare const initAzureMonitor: (instrumentations?: readonly Instrumentation[]) => void;
21
+ declare const initAzureMonitor: (instrumentations?: readonly Instrumentation[], azureMonitorOptions?: AzureMonitorOpenTelemetryOptions) => void;
22
+
23
+ export { initAzureMonitor };
@@ -0,0 +1,84 @@
1
+ // src/azure/monitor/index.ts
2
+ import {
3
+ useAzureMonitor as useAzureMonitor2
4
+ } from "@azure/monitor-opentelemetry";
5
+ import { metrics, trace } from "@opentelemetry/api";
6
+ import {
7
+ registerInstrumentations
8
+ } from "@opentelemetry/instrumentation";
9
+
10
+ // src/azure/opentelemetry/azure-undici-instrumentation.ts
11
+ import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
12
+ var registerUndiciInstrumentation = () => new UndiciInstrumentation({
13
+ requestHook: (span, requestInfo) => {
14
+ const { method, origin, path } = requestInfo;
15
+ span.setAttributes({
16
+ "http.host": origin,
17
+ "http.method": method,
18
+ "http.target": path,
19
+ "http.url": `${origin}${path}`
20
+ });
21
+ },
22
+ responseHook: (span, { response }) => {
23
+ span.setAttribute("http.status_code", response.statusCode);
24
+ }
25
+ });
26
+
27
+ // src/azure/monitor/start-from-env.ts
28
+ import { useAzureMonitor } from "@azure/monitor-opentelemetry";
29
+
30
+ // src/azure/monitor/env.ts
31
+ import { createEnv } from "@t3-oss/env-core";
32
+ import { z } from "zod";
33
+ var loadEnv = () => createEnv({
34
+ emptyStringAsUndefined: true,
35
+ onValidationError: (errors) => {
36
+ throw new Error(
37
+ errors.map(
38
+ (error) => `Environment variable ${error.path} - ${error.message}`
39
+ ).join(", ")
40
+ );
41
+ },
42
+ runtimeEnv: process.env,
43
+ server: {
44
+ APPINSIGHTS_CONNECTION_STRING: z.string().describe("The connection string for Application Insights."),
45
+ APPINSIGHTS_SAMPLING_PERCENTAGE: z.optional(
46
+ z.coerce.number().min(0).max(100).default(5).describe(
47
+ "Application Insights sampling percentage between 0 and 100. If not set, defaults to 5."
48
+ )
49
+ ).transform((value) => {
50
+ const percentage = Number(value);
51
+ return isNaN(percentage) ? 5 : percentage;
52
+ }).transform((value) => value / 100)
53
+ }
54
+ });
55
+
56
+ // src/azure/monitor/start-from-env.ts
57
+ var initFromEnv = () => {
58
+ const env = loadEnv();
59
+ return useAzureMonitor({
60
+ azureMonitorExporterOptions: {
61
+ connectionString: env.APPINSIGHTS_CONNECTION_STRING
62
+ },
63
+ enableLiveMetrics: true,
64
+ samplingRatio: env.APPINSIGHTS_SAMPLING_PERCENTAGE
65
+ });
66
+ };
67
+
68
+ // src/azure/monitor/index.ts
69
+ var initAzureMonitor = (instrumentations = [], azureMonitorOptions) => {
70
+ if (azureMonitorOptions) {
71
+ useAzureMonitor2(azureMonitorOptions);
72
+ } else {
73
+ initFromEnv();
74
+ }
75
+ registerInstrumentations({
76
+ instrumentations: [registerUndiciInstrumentation(), ...instrumentations],
77
+ meterProvider: metrics.getMeterProvider(),
78
+ tracerProvider: trace.getTracerProvider()
79
+ });
80
+ };
81
+ export {
82
+ initAzureMonitor
83
+ };
84
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/azure/monitor/index.ts","../../src/azure/opentelemetry/azure-undici-instrumentation.ts","../../src/azure/monitor/start-from-env.ts","../../src/azure/monitor/env.ts"],"sourcesContent":["import {\n AzureMonitorOpenTelemetryOptions,\n useAzureMonitor,\n} from \"@azure/monitor-opentelemetry\";\nimport { metrics, trace } from \"@opentelemetry/api\";\nimport {\n Instrumentation,\n registerInstrumentations,\n} from \"@opentelemetry/instrumentation\";\n\nimport { registerUndiciInstrumentation } from \"../opentelemetry/azure-undici-instrumentation\";\nimport { initFromEnv } from \"./start-from-env\";\n\n/**\n * Initialize the Azure Monitor with the given instrumentations.\n * Call this function to register the instrumentations with the Azure Monitor.\n * By default, the Undici instrumentation is included; if you need to add more,\n * you can pass them as an array of instrumentations.\n *\n * @remarks\n * This function will register the given instrumentations with the Azure Monitor.\n * It is recommended to call this function at the beginning of your application.\n *\n * @example\n * import { initAzureMonitor } from \"@pagopa/azure-tracing/azure-monitor\";\n *\n * initAzureMonitor();\n * @param instrumentations the list of instrumentations to register with the Azure Monitor.\n * @param azureMonitorOptions custom configuration for Azure Monitor. If not provided, then it will be initialized using the environment variables.\n */\nexport const initAzureMonitor = (\n instrumentations: readonly Instrumentation[] = [],\n azureMonitorOptions?: AzureMonitorOpenTelemetryOptions,\n) => {\n if (azureMonitorOptions) {\n useAzureMonitor(azureMonitorOptions);\n } else {\n initFromEnv();\n }\n\n registerInstrumentations({\n instrumentations: [registerUndiciInstrumentation(), ...instrumentations],\n meterProvider: metrics.getMeterProvider(),\n tracerProvider: trace.getTracerProvider(),\n });\n};\n","import { UndiciInstrumentation } from \"@opentelemetry/instrumentation-undici\";\n\n// instrument native node fetch\nexport const registerUndiciInstrumentation = () =>\n new UndiciInstrumentation({\n requestHook: (span, requestInfo) => {\n const { method, origin, path } = requestInfo;\n // Default instrumented attributes don't feed well into AppInsights,\n // so we set them manually.\n span.setAttributes({\n \"http.host\": origin,\n \"http.method\": method,\n \"http.target\": path,\n \"http.url\": `${origin}${path}`,\n });\n },\n responseHook: (span, { response }) => {\n // Same as above, set the status code manually.\n span.setAttribute(\"http.status_code\", response.statusCode);\n },\n });\n","import { useAzureMonitor } from \"@azure/monitor-opentelemetry\";\n\nimport { loadEnv } from \"./env\";\n\nexport const initFromEnv = () => {\n const env = loadEnv();\n\n return useAzureMonitor({\n azureMonitorExporterOptions: {\n connectionString: env.APPINSIGHTS_CONNECTION_STRING,\n },\n enableLiveMetrics: true,\n samplingRatio: env.APPINSIGHTS_SAMPLING_PERCENTAGE,\n });\n};\n","// Load and type check environment variables on runtime\nimport { createEnv } from \"@t3-oss/env-core\";\nimport { z } from \"zod\";\n\nexport const loadEnv = () =>\n createEnv({\n emptyStringAsUndefined: true,\n onValidationError: (errors) => {\n throw new Error(\n errors\n .map(\n (error) => `Environment variable ${error.path} - ${error.message}`,\n )\n .join(\", \"),\n );\n },\n runtimeEnv: process.env,\n server: {\n APPINSIGHTS_CONNECTION_STRING: z\n .string()\n .describe(\"The connection string for Application Insights.\"),\n APPINSIGHTS_SAMPLING_PERCENTAGE: z\n .optional(\n z.coerce\n .number()\n .min(0)\n .max(100)\n .default(5)\n .describe(\n \"Application Insights sampling percentage between 0 and 100. If not set, defaults to 5.\",\n ),\n )\n .transform((value) => {\n const percentage = Number(value);\n return isNaN(percentage) ? 5 : percentage;\n })\n .transform((value) => value / 100),\n },\n });\n"],"mappings":";AAAA;AAAA,EAEE,mBAAAA;AAAA,OACK;AACP,SAAS,SAAS,aAAa;AAC/B;AAAA,EAEE;AAAA,OACK;;;ACRP,SAAS,6BAA6B;AAG/B,IAAM,gCAAgC,MAC3C,IAAI,sBAAsB;AAAA,EACxB,aAAa,CAAC,MAAM,gBAAgB;AAClC,UAAM,EAAE,QAAQ,QAAQ,KAAK,IAAI;AAGjC,SAAK,cAAc;AAAA,MACjB,aAAa;AAAA,MACb,eAAe;AAAA,MACf,eAAe;AAAA,MACf,YAAY,GAAG,MAAM,GAAG,IAAI;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA,EACA,cAAc,CAAC,MAAM,EAAE,SAAS,MAAM;AAEpC,SAAK,aAAa,oBAAoB,SAAS,UAAU;AAAA,EAC3D;AACF,CAAC;;;ACpBH,SAAS,uBAAuB;;;ACChC,SAAS,iBAAiB;AAC1B,SAAS,SAAS;AAEX,IAAM,UAAU,MACrB,UAAU;AAAA,EACR,wBAAwB;AAAA,EACxB,mBAAmB,CAAC,WAAW;AAC7B,UAAM,IAAI;AAAA,MACR,OACG;AAAA,QACC,CAAC,UAAU,wBAAwB,MAAM,IAAI,MAAM,MAAM,OAAO;AAAA,MAClE,EACC,KAAK,IAAI;AAAA,IACd;AAAA,EACF;AAAA,EACA,YAAY,QAAQ;AAAA,EACpB,QAAQ;AAAA,IACN,+BAA+B,EAC5B,OAAO,EACP,SAAS,iDAAiD;AAAA,IAC7D,iCAAiC,EAC9B;AAAA,MACC,EAAE,OACC,OAAO,EACP,IAAI,CAAC,EACL,IAAI,GAAG,EACP,QAAQ,CAAC,EACT;AAAA,QACC;AAAA,MACF;AAAA,IACJ,EACC,UAAU,CAAC,UAAU;AACpB,YAAM,aAAa,OAAO,KAAK;AAC/B,aAAO,MAAM,UAAU,IAAI,IAAI;AAAA,IACjC,CAAC,EACA,UAAU,CAAC,UAAU,QAAQ,GAAG;AAAA,EACrC;AACF,CAAC;;;ADlCI,IAAM,cAAc,MAAM;AAC/B,QAAM,MAAM,QAAQ;AAEpB,SAAO,gBAAgB;AAAA,IACrB,6BAA6B;AAAA,MAC3B,kBAAkB,IAAI;AAAA,IACxB;AAAA,IACA,mBAAmB;AAAA,IACnB,eAAe,IAAI;AAAA,EACrB,CAAC;AACH;;;AFgBO,IAAM,mBAAmB,CAC9B,mBAA+C,CAAC,GAChD,wBACG;AACH,MAAI,qBAAqB;AACvB,IAAAC,iBAAgB,mBAAmB;AAAA,EACrC,OAAO;AACL,gBAAY;AAAA,EACd;AAEA,2BAAyB;AAAA,IACvB,kBAAkB,CAAC,8BAA8B,GAAG,GAAG,gBAAgB;AAAA,IACvE,eAAe,QAAQ,iBAAiB;AAAA,IACxC,gBAAgB,MAAM,kBAAkB;AAAA,EAC1C,CAAC;AACH;","names":["useAzureMonitor","useAzureMonitor"]}
@@ -5,4 +5,6 @@
5
5
  * @param eventName the name of the event to emit
6
6
  * @param attributes the attributes to include with the event
7
7
  */
8
- export declare const emitCustomEvent: (eventName: string, attributes: Record<string, string>) => (loggerName?: string) => void;
8
+ declare const emitCustomEvent: (eventName: string, attributes: Record<string, string>) => (loggerName?: string) => void;
9
+
10
+ export { emitCustomEvent };
@@ -0,0 +1,14 @@
1
+ // src/azure/opentelemetry/logger.ts
2
+ import { logs } from "@opentelemetry/api-logs";
3
+ var emitCustomEvent = (eventName, attributes) => (loggerName = "ApplicationInsightsLogger") => {
4
+ logs.getLogger(loggerName).emit({
5
+ attributes: {
6
+ "microsoft.custom_event.name": eventName,
7
+ ...attributes
8
+ }
9
+ });
10
+ };
11
+ export {
12
+ emitCustomEvent
13
+ };
14
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/azure/opentelemetry/logger.ts"],"sourcesContent":["import { logs } from \"@opentelemetry/api-logs\";\n\n/**\n * Emit a custom event to the Azure Monitor.\n * This function is used to emit custom events to the Azure Monitor.\n *\n * @param eventName the name of the event to emit\n * @param attributes the attributes to include with the event\n */\nexport const emitCustomEvent =\n (eventName: string, attributes: Record<string, string>) =>\n (loggerName = \"ApplicationInsightsLogger\") => {\n logs.getLogger(loggerName).emit({\n attributes: {\n \"microsoft.custom_event.name\": eventName,\n ...attributes,\n },\n });\n };\n"],"mappings":";AAAA,SAAS,YAAY;AASd,IAAM,kBACX,CAAC,WAAmB,eACpB,CAAC,aAAa,gCAAgC;AAC5C,OAAK,UAAU,UAAU,EAAE,KAAK;AAAA,IAC9B,YAAY;AAAA,MACV,+BAA+B;AAAA,MAC/B,GAAG;AAAA,IACL;AAAA,EACF,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pagopa/azure-tracing",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "A package that contains some utilities to enable Azure tracing on Node.js applications.",
6
6
  "keywords": [
@@ -11,30 +11,32 @@
11
11
  "files": [
12
12
  "dist"
13
13
  ],
14
- "main": "./dist/azure-functions.mjs",
14
+ "main": "./dist/functions/index.js",
15
15
  "exports": {
16
16
  ".": {
17
- "import": "./dist/azure-functions.mjs"
17
+ "import": "./dist/functions/index.js"
18
18
  },
19
19
  "./logger": {
20
- "import": "./dist/logger.js"
20
+ "import": "./dist/opentelemetry/logger.js"
21
21
  },
22
22
  "./azure-monitor": {
23
- "import": "./dist/azure-monitor.js"
23
+ "import": "./dist/monitor/index.js"
24
24
  },
25
25
  "./azure-functions": {
26
- "import": "./dist/azure-functions-hooks.js"
26
+ "import": "./dist/functions/hooks.js"
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
30
  "@azure/functions": "^4.7.0",
31
- "@azure/monitor-opentelemetry": "^1.9.0",
32
- "@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.29",
31
+ "@azure/monitor-opentelemetry": "^1.10.0",
32
+ "@azure/monitor-opentelemetry-exporter": "^1.0.0-beta.30",
33
33
  "@opentelemetry/api": "^1.9.0",
34
34
  "@opentelemetry/api-logs": "^0.200.0",
35
35
  "@opentelemetry/instrumentation": "^0.200.0",
36
36
  "@opentelemetry/instrumentation-undici": "^0.11.0",
37
- "import-in-the-middle": "^1.13.1"
37
+ "@t3-oss/env-core": "^0.12.0",
38
+ "import-in-the-middle": "^1.13.1",
39
+ "zod": "^3.24.2"
38
40
  },
39
41
  "devDependencies": {
40
42
  "@pagopa/eslint-config": "workspace:^",
@@ -42,11 +44,12 @@
42
44
  "@types/node": "^20.12.2",
43
45
  "eslint": "8.57.0",
44
46
  "shx": "^0.4.0",
47
+ "tsup": "^8.4.0",
45
48
  "typescript": "~5.2.2"
46
49
  },
47
50
  "scripts": {
48
51
  "clean": "shx rm -rf dist",
49
- "build": "tsc",
52
+ "build": "tsup",
50
53
  "lint": "eslint --fix src",
51
54
  "lint:check": "eslint src",
52
55
  "typecheck": "tsc --noEmit"
@@ -1,26 +0,0 @@
1
- import { context as otelContext, propagation } from "@opentelemetry/api";
2
- /**
3
- * Registers Azure Function hooks to enable OpenTelemetry tracing.
4
- * These hooks extract trace context from the Azure Function context
5
- * and bind it to the function handler, ensuring that traces are
6
- * properly propagated across function invocations.
7
- *
8
- * Once the issues that causes this workaround are resolved, it will be possible
9
- * to add the [azure-functions-nodejs-opentelemetry](https://github.com/Azure/azure-functions-nodejs-opentelemetry/tree/main)
10
- * to the instrumentation package and remove this workaround.
11
- *
12
- * @param {Object} options - An object containing the Azure Functions `hook` object.
13
- * @param {Function} options.hook - The Azure Functions `hook` object from the `app` module.
14
- */
15
- export const registerAzureFunctionHooks = ({ hook }) => {
16
- hook.preInvocation((context) => {
17
- const traceContext = context.invocationContext.traceContext;
18
- if (traceContext) {
19
- context.functionHandler = otelContext.bind(propagation.extract(otelContext.active(), {
20
- traceparent: traceContext.traceParent,
21
- tracestate: traceContext.traceState,
22
- }), context.functionHandler);
23
- }
24
- });
25
- };
26
- //# sourceMappingURL=azure-functions-hooks.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"azure-functions-hooks.js","sourceRoot":"","sources":["../src/azure-functions-hooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,EAAE,IAAI,EAAc,EAAE,EAAE;IACjE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,YAAY,CAAC;QAC5D,IAAI,YAAY,EAAE;YAChB,OAAO,CAAC,eAAe,GAAG,WAAW,CAAC,IAAI,CACxC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE;gBACxC,WAAW,EAAE,YAAY,CAAC,WAAW;gBACrC,UAAU,EAAE,YAAY,CAAC,UAAU;aACpC,CAAC,EACF,OAAO,CAAC,eAAe,CACxB,CAAC;SACH;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC"}
@@ -1 +0,0 @@
1
- export {};
@@ -1,16 +0,0 @@
1
- import { createAddHookMessageChannel } from "import-in-the-middle";
2
- import { register } from "module";
3
- const { registerOptions, waitForAllMessagesAcknowledged } = createAddHookMessageChannel();
4
- register("import-in-the-middle/hook.mjs", import.meta.url, registerOptions);
5
- import { useAzureMonitor } from "@azure/monitor-opentelemetry";
6
- import { metrics, trace } from "@opentelemetry/api";
7
- import { registerInstrumentations } from "@opentelemetry/instrumentation";
8
- import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
9
- useAzureMonitor();
10
- registerInstrumentations({
11
- instrumentations: [new UndiciInstrumentation()],
12
- meterProvider: metrics.getMeterProvider(),
13
- tracerProvider: trace.getTracerProvider(),
14
- });
15
- await waitForAllMessagesAcknowledged();
16
- //# sourceMappingURL=azure-functions.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"azure-functions.mjs","sourceRoot":"","sources":["../src/azure-functions.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,EAAE,eAAe,EAAE,8BAA8B,EAAE,GACvD,2BAA2B,EAAE,CAAC;AAEhC,QAAQ,CAAC,+BAA+B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,eAAe,EAAE,CAAC;AAElB,wBAAwB,CAAC;IACvB,gBAAgB,EAAE,CAAC,IAAI,qBAAqB,EAAE,CAAC;IAC/C,aAAa,EAAE,OAAO,CAAC,gBAAgB,EAAE;IACzC,cAAc,EAAE,KAAK,CAAC,iBAAiB,EAAE;CAC1C,CAAC,CAAC;AAEH,MAAM,8BAA8B,EAAE,CAAC"}
@@ -1,49 +0,0 @@
1
- import { useAzureMonitor } from "@azure/monitor-opentelemetry";
2
- import { metrics, trace } from "@opentelemetry/api";
3
- import { registerInstrumentations, } from "@opentelemetry/instrumentation";
4
- import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
5
- /**
6
- * Initialize the Azure Monitor with the given instrumentations.
7
- * Call this function to register the instrumentations with the Azure Monitor.
8
- * By default, the Undici instrumentation is included; if you need to add more,
9
- * you can pass them as an array of instrumentations.
10
- *
11
- * @remarks
12
- * This function will register the given instrumentations with the Azure Monitor.
13
- * It is recommended to call this function at the beginning of your application.
14
- *
15
- * @example
16
- * import { initAzureMonitor } from "@pagopa/azure-tracing/azure-monitor";
17
- *
18
- * initAzureMonitor();
19
- * @param instrumentations the list of instrumentations to register with the Azure Monitor.
20
- */
21
- export const initAzureMonitor = (instrumentations = []) => {
22
- useAzureMonitor();
23
- registerInstrumentations({
24
- instrumentations: [
25
- // instrument native node fetch
26
- new UndiciInstrumentation({
27
- requestHook: (span, requestInfo) => {
28
- const { method, origin, path } = requestInfo;
29
- // Default instrumented attributes don't feed well into AppInsights,
30
- // so we set them manually.
31
- span.setAttributes({
32
- "http.host": origin,
33
- "http.method": method,
34
- "http.target": path,
35
- "http.url": `${origin}${path}`,
36
- });
37
- },
38
- responseHook: (span, { response }) => {
39
- // Same as above, set the status code manually.
40
- span.setAttribute("http.status_code", response.statusCode);
41
- },
42
- }),
43
- ...instrumentations,
44
- ],
45
- meterProvider: metrics.getMeterProvider(),
46
- tracerProvider: trace.getTracerProvider(),
47
- });
48
- };
49
- //# sourceMappingURL=azure-monitor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"azure-monitor.js","sourceRoot":"","sources":["../src/azure-monitor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAEL,wBAAwB,GACzB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,mBAA+C,EAAE,EACjD,EAAE;IACF,eAAe,EAAE,CAAC;IAElB,wBAAwB,CAAC;QACvB,gBAAgB,EAAE;YAChB,+BAA+B;YAC/B,IAAI,qBAAqB,CAAC;gBACxB,WAAW,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE;oBACjC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,WAAW,CAAC;oBAC7C,oEAAoE;oBACpE,2BAA2B;oBAC3B,IAAI,CAAC,aAAa,CAAC;wBACjB,WAAW,EAAE,MAAM;wBACnB,aAAa,EAAE,MAAM;wBACrB,aAAa,EAAE,IAAI;wBACnB,UAAU,EAAE,GAAG,MAAM,GAAG,IAAI,EAAE;qBAC/B,CAAC,CAAC;gBACL,CAAC;gBACD,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;oBACnC,+CAA+C;oBAC/C,IAAI,CAAC,YAAY,CAAC,kBAAkB,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;gBAC7D,CAAC;aACF,CAAC;YACF,GAAG,gBAAgB;SACpB;QACD,aAAa,EAAE,OAAO,CAAC,gBAAgB,EAAE;QACzC,cAAc,EAAE,KAAK,CAAC,iBAAiB,EAAE;KAC1C,CAAC,CAAC;AACL,CAAC,CAAC"}
package/dist/logger.js DELETED
@@ -1,17 +0,0 @@
1
- import { logs } from "@opentelemetry/api-logs";
2
- /**
3
- * Emit a custom event to the Azure Monitor.
4
- * This function is used to emit custom events to the Azure Monitor.
5
- *
6
- * @param eventName the name of the event to emit
7
- * @param attributes the attributes to include with the event
8
- */
9
- export const emitCustomEvent = (eventName, attributes) => (loggerName = "ApplicationInsightsLogger") => {
10
- logs.getLogger(loggerName).emit({
11
- attributes: {
12
- "microsoft.custom_event.name": eventName,
13
- ...attributes,
14
- },
15
- });
16
- };
17
- //# sourceMappingURL=logger.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,CAAC,SAAiB,EAAE,UAAkC,EAAE,EAAE,CAC1D,CAAC,UAAU,GAAG,2BAA2B,EAAE,EAAE;IAC3C,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;QAC9B,UAAU,EAAE;YACV,6BAA6B,EAAE,SAAS;YACxC,GAAG,UAAU;SACd;KACF,CAAC,CAAC;AACL,CAAC,CAAC"}