@noony-serverless/core 0.3.4 → 0.4.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/build/core/containerPool.d.ts +129 -26
- package/build/core/containerPool.js +213 -68
- package/build/core/handler.d.ts +2 -2
- package/build/core/handler.js +6 -12
- package/build/core/index.d.ts +1 -0
- package/build/core/index.js +1 -0
- package/build/core/logger.d.ts +89 -1
- package/build/core/logger.js +136 -5
- package/build/core/telemetry/config.d.ts +331 -0
- package/build/core/telemetry/config.js +153 -0
- package/build/core/telemetry/index.d.ts +22 -0
- package/build/core/telemetry/index.js +45 -0
- package/build/core/telemetry/provider.d.ts +203 -0
- package/build/core/telemetry/provider.js +3 -0
- package/build/core/telemetry/providers/console-provider.d.ts +54 -0
- package/build/core/telemetry/providers/console-provider.js +124 -0
- package/build/core/telemetry/providers/index.d.ts +10 -0
- package/build/core/telemetry/providers/index.js +19 -0
- package/build/core/telemetry/providers/noop-provider.d.ts +51 -0
- package/build/core/telemetry/providers/noop-provider.js +67 -0
- package/build/core/telemetry/providers/opentelemetry-provider.d.ts +102 -0
- package/build/core/telemetry/providers/opentelemetry-provider.js +342 -0
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +16 -8
- package/build/middlewares/dependencyInjectionMiddleware.js +31 -11
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +1 -1
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/FastAuthGuard.js +3 -2
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +7 -9
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/services/FastUserContextService.d.ts +11 -32
- package/build/middlewares/index.d.ts +1 -0
- package/build/middlewares/index.js +1 -0
- package/build/middlewares/openTelemetryMiddleware.d.ts +162 -0
- package/build/middlewares/openTelemetryMiddleware.js +359 -0
- package/build/middlewares/rateLimitingMiddleware.js +16 -5
- package/build/utils/container.utils.js +4 -1
- package/build/utils/fastify-wrapper.d.ts +74 -0
- package/build/utils/fastify-wrapper.js +175 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/index.js +23 -1
- package/build/utils/otel.helper.d.ts +122 -0
- package/build/utils/otel.helper.js +258 -0
- package/build/utils/pubsub-trace.utils.d.ts +102 -0
- package/build/utils/pubsub-trace.utils.js +155 -0
- package/build/utils/wrapper-utils.d.ts +177 -0
- package/build/utils/wrapper-utils.js +236 -0
- package/package.json +61 -2
package/build/utils/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Utility functions for Noony Core
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.asBoolean = exports.asNumber = exports.asStringArray = exports.asString = exports.getService = void 0;
|
|
6
|
+
exports.createFastifyHandler = exports.wrapNoonyHandler = exports.createHttpFunction = exports.isOTELInstalled = exports.isOTELActive = exports.createCloudLoggingEntry = exports.formatTraceIdForCloudLogging = exports.getOTELContextFromContext = exports.getOTELContextFromSpan = exports.getOTELContext = exports.createOTELMixin = exports.createParentContext = exports.injectTraceContext = exports.extractTraceContext = exports.isPubSubMessage = exports.asBoolean = exports.asNumber = exports.asStringArray = exports.asString = exports.getService = void 0;
|
|
7
7
|
// Container utilities
|
|
8
8
|
var container_utils_1 = require("./container.utils");
|
|
9
9
|
Object.defineProperty(exports, "getService", { enumerable: true, get: function () { return container_utils_1.getService; } });
|
|
@@ -13,4 +13,26 @@ Object.defineProperty(exports, "asString", { enumerable: true, get: function ()
|
|
|
13
13
|
Object.defineProperty(exports, "asStringArray", { enumerable: true, get: function () { return query_param_utils_1.asStringArray; } });
|
|
14
14
|
Object.defineProperty(exports, "asNumber", { enumerable: true, get: function () { return query_param_utils_1.asNumber; } });
|
|
15
15
|
Object.defineProperty(exports, "asBoolean", { enumerable: true, get: function () { return query_param_utils_1.asBoolean; } });
|
|
16
|
+
// Pub/Sub trace propagation utilities
|
|
17
|
+
var pubsub_trace_utils_1 = require("./pubsub-trace.utils");
|
|
18
|
+
Object.defineProperty(exports, "isPubSubMessage", { enumerable: true, get: function () { return pubsub_trace_utils_1.isPubSubMessage; } });
|
|
19
|
+
Object.defineProperty(exports, "extractTraceContext", { enumerable: true, get: function () { return pubsub_trace_utils_1.extractTraceContext; } });
|
|
20
|
+
Object.defineProperty(exports, "injectTraceContext", { enumerable: true, get: function () { return pubsub_trace_utils_1.injectTraceContext; } });
|
|
21
|
+
Object.defineProperty(exports, "createParentContext", { enumerable: true, get: function () { return pubsub_trace_utils_1.createParentContext; } });
|
|
22
|
+
// OpenTelemetry logger integration utilities
|
|
23
|
+
var otel_helper_1 = require("./otel.helper");
|
|
24
|
+
Object.defineProperty(exports, "createOTELMixin", { enumerable: true, get: function () { return otel_helper_1.createOTELMixin; } });
|
|
25
|
+
Object.defineProperty(exports, "getOTELContext", { enumerable: true, get: function () { return otel_helper_1.getOTELContext; } });
|
|
26
|
+
Object.defineProperty(exports, "getOTELContextFromSpan", { enumerable: true, get: function () { return otel_helper_1.getOTELContextFromSpan; } });
|
|
27
|
+
Object.defineProperty(exports, "getOTELContextFromContext", { enumerable: true, get: function () { return otel_helper_1.getOTELContextFromContext; } });
|
|
28
|
+
Object.defineProperty(exports, "formatTraceIdForCloudLogging", { enumerable: true, get: function () { return otel_helper_1.formatTraceIdForCloudLogging; } });
|
|
29
|
+
Object.defineProperty(exports, "createCloudLoggingEntry", { enumerable: true, get: function () { return otel_helper_1.createCloudLoggingEntry; } });
|
|
30
|
+
Object.defineProperty(exports, "isOTELActive", { enumerable: true, get: function () { return otel_helper_1.isOTELActive; } });
|
|
31
|
+
Object.defineProperty(exports, "isOTELInstalled", { enumerable: true, get: function () { return otel_helper_1.isOTELInstalled; } });
|
|
32
|
+
// Wrapper utilities for GCP Functions, Express, and Fastify
|
|
33
|
+
var wrapper_utils_1 = require("./wrapper-utils");
|
|
34
|
+
Object.defineProperty(exports, "createHttpFunction", { enumerable: true, get: function () { return wrapper_utils_1.createHttpFunction; } });
|
|
35
|
+
Object.defineProperty(exports, "wrapNoonyHandler", { enumerable: true, get: function () { return wrapper_utils_1.wrapNoonyHandler; } });
|
|
36
|
+
var fastify_wrapper_1 = require("./fastify-wrapper");
|
|
37
|
+
Object.defineProperty(exports, "createFastifyHandler", { enumerable: true, get: function () { return fastify_wrapper_1.createFastifyHandler; } });
|
|
16
38
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Helper Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides helper functions for integrating OpenTelemetry with logging systems.
|
|
5
|
+
* These utilities enable automatic trace/span ID injection into log entries for
|
|
6
|
+
* correlation with distributed traces in Cloud Logging and other observability platforms.
|
|
7
|
+
*
|
|
8
|
+
* @module utils/otel.helper
|
|
9
|
+
*/
|
|
10
|
+
import type { Context as OtelContext, Span } from '@opentelemetry/api';
|
|
11
|
+
/**
|
|
12
|
+
* OTEL context object for logger integration
|
|
13
|
+
*/
|
|
14
|
+
export interface OTELLogContext {
|
|
15
|
+
traceId?: string;
|
|
16
|
+
spanId?: string;
|
|
17
|
+
traceFlags?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create Pino mixin for automatic trace/span ID injection
|
|
21
|
+
*
|
|
22
|
+
* This function creates a Pino mixin that automatically adds OpenTelemetry
|
|
23
|
+
* trace and span IDs to every log entry. This enables log-trace correlation
|
|
24
|
+
* in Cloud Logging and other observability platforms.
|
|
25
|
+
*
|
|
26
|
+
* Usage with Pino:
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import pino from 'pino';
|
|
29
|
+
* import { createOTELMixin } from '@noony-serverless/core';
|
|
30
|
+
*
|
|
31
|
+
* const logger = pino({
|
|
32
|
+
* mixin: createOTELMixin,
|
|
33
|
+
* // ... other config
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* logger.info('User created'); // Automatically includes traceId, spanId, traceFlags
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* Log output example:
|
|
40
|
+
* ```json
|
|
41
|
+
* {
|
|
42
|
+
* "level": 30,
|
|
43
|
+
* "time": 1640000000000,
|
|
44
|
+
* "msg": "User created",
|
|
45
|
+
* "traceId": "13ea7e3c2d3b4547baaa399062df1f2d",
|
|
46
|
+
* "spanId": "1234567890123456",
|
|
47
|
+
* "traceFlags": 1
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @returns Mixin object with trace context or empty object if no active span
|
|
52
|
+
*/
|
|
53
|
+
export declare const createOTELMixin: () => OTELLogContext;
|
|
54
|
+
/**
|
|
55
|
+
* Extract OTEL context from active span
|
|
56
|
+
*
|
|
57
|
+
* Similar to createOTELMixin but returns undefined if no span is active,
|
|
58
|
+
* making it easier to conditionally add trace context.
|
|
59
|
+
*
|
|
60
|
+
* @returns OTEL context or undefined if no active span
|
|
61
|
+
*/
|
|
62
|
+
export declare const getOTELContext: () => OTELLogContext | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Extract OTEL context from a specific span
|
|
65
|
+
*
|
|
66
|
+
* Useful when you have a reference to a span and want to extract its context
|
|
67
|
+
* for logging or propagation purposes.
|
|
68
|
+
*
|
|
69
|
+
* @param span - The OpenTelemetry span to extract context from
|
|
70
|
+
* @returns OTEL context from the span
|
|
71
|
+
*/
|
|
72
|
+
export declare const getOTELContextFromSpan: (span: Span) => OTELLogContext;
|
|
73
|
+
/**
|
|
74
|
+
* Extract OTEL context from an OTEL Context object
|
|
75
|
+
*
|
|
76
|
+
* Useful when working with OTEL Context propagation (e.g., in Pub/Sub messages)
|
|
77
|
+
* and you need to extract the span context for logging.
|
|
78
|
+
*
|
|
79
|
+
* @param context - The OpenTelemetry context to extract from
|
|
80
|
+
* @returns OTEL context from the context or undefined if no span
|
|
81
|
+
*/
|
|
82
|
+
export declare const getOTELContextFromContext: (context: OtelContext) => OTELLogContext | undefined;
|
|
83
|
+
/**
|
|
84
|
+
* Format trace ID for Cloud Logging
|
|
85
|
+
*
|
|
86
|
+
* Cloud Logging expects trace IDs in a specific format:
|
|
87
|
+
* projects/[PROJECT_ID]/traces/[TRACE_ID]
|
|
88
|
+
*
|
|
89
|
+
* This function formats a raw trace ID into the Cloud Logging format.
|
|
90
|
+
*
|
|
91
|
+
* @param traceId - Raw trace ID (32-character hex string)
|
|
92
|
+
* @param projectId - GCP project ID (optional, defaults to GOOGLE_CLOUD_PROJECT env var)
|
|
93
|
+
* @returns Formatted trace ID for Cloud Logging or undefined if inputs invalid
|
|
94
|
+
*/
|
|
95
|
+
export declare const formatTraceIdForCloudLogging: (traceId?: string, projectId?: string) => string | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Create Cloud Logging compatible log entry
|
|
98
|
+
*
|
|
99
|
+
* Combines OTEL context with log metadata to create a Cloud Logging compatible
|
|
100
|
+
* log entry with trace correlation.
|
|
101
|
+
*
|
|
102
|
+
* @param message - Log message
|
|
103
|
+
* @param metadata - Additional log metadata
|
|
104
|
+
* @param projectId - GCP project ID (optional)
|
|
105
|
+
* @returns Cloud Logging compatible log entry
|
|
106
|
+
*/
|
|
107
|
+
export declare const createCloudLoggingEntry: (message: string, metadata?: Record<string, any>, projectId?: string) => Record<string, any>;
|
|
108
|
+
/**
|
|
109
|
+
* Check if OpenTelemetry is available and active
|
|
110
|
+
*
|
|
111
|
+
* Useful for conditional OTEL feature usage in libraries and applications.
|
|
112
|
+
*
|
|
113
|
+
* @returns true if OTEL is available and there's an active span
|
|
114
|
+
*/
|
|
115
|
+
export declare const isOTELActive: () => boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Check if OpenTelemetry SDK is installed
|
|
118
|
+
*
|
|
119
|
+
* @returns true if @opentelemetry/api is installed
|
|
120
|
+
*/
|
|
121
|
+
export declare const isOTELInstalled: () => boolean;
|
|
122
|
+
//# sourceMappingURL=otel.helper.d.ts.map
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenTelemetry Helper Utilities
|
|
4
|
+
*
|
|
5
|
+
* Provides helper functions for integrating OpenTelemetry with logging systems.
|
|
6
|
+
* These utilities enable automatic trace/span ID injection into log entries for
|
|
7
|
+
* correlation with distributed traces in Cloud Logging and other observability platforms.
|
|
8
|
+
*
|
|
9
|
+
* @module utils/otel.helper
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.isOTELInstalled = exports.isOTELActive = exports.createCloudLoggingEntry = exports.formatTraceIdForCloudLogging = exports.getOTELContextFromContext = exports.getOTELContextFromSpan = exports.getOTELContext = exports.createOTELMixin = void 0;
|
|
13
|
+
// Conditional OpenTelemetry import (optional dependency)
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
let trace;
|
|
16
|
+
try {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
+
const otelApi = require('@opentelemetry/api');
|
|
19
|
+
trace = otelApi.trace;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// OpenTelemetry not installed - helpers will return empty objects
|
|
23
|
+
trace = null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create Pino mixin for automatic trace/span ID injection
|
|
27
|
+
*
|
|
28
|
+
* This function creates a Pino mixin that automatically adds OpenTelemetry
|
|
29
|
+
* trace and span IDs to every log entry. This enables log-trace correlation
|
|
30
|
+
* in Cloud Logging and other observability platforms.
|
|
31
|
+
*
|
|
32
|
+
* Usage with Pino:
|
|
33
|
+
* ```typescript
|
|
34
|
+
* import pino from 'pino';
|
|
35
|
+
* import { createOTELMixin } from '@noony-serverless/core';
|
|
36
|
+
*
|
|
37
|
+
* const logger = pino({
|
|
38
|
+
* mixin: createOTELMixin,
|
|
39
|
+
* // ... other config
|
|
40
|
+
* });
|
|
41
|
+
*
|
|
42
|
+
* logger.info('User created'); // Automatically includes traceId, spanId, traceFlags
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Log output example:
|
|
46
|
+
* ```json
|
|
47
|
+
* {
|
|
48
|
+
* "level": 30,
|
|
49
|
+
* "time": 1640000000000,
|
|
50
|
+
* "msg": "User created",
|
|
51
|
+
* "traceId": "13ea7e3c2d3b4547baaa399062df1f2d",
|
|
52
|
+
* "spanId": "1234567890123456",
|
|
53
|
+
* "traceFlags": 1
|
|
54
|
+
* }
|
|
55
|
+
* ```
|
|
56
|
+
*
|
|
57
|
+
* @returns Mixin object with trace context or empty object if no active span
|
|
58
|
+
*/
|
|
59
|
+
const createOTELMixin = () => {
|
|
60
|
+
if (!trace) {
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const span = trace.getActiveSpan();
|
|
65
|
+
if (!span) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
const spanContext = span.spanContext();
|
|
69
|
+
return {
|
|
70
|
+
traceId: spanContext.traceId,
|
|
71
|
+
spanId: spanContext.spanId,
|
|
72
|
+
traceFlags: spanContext.traceFlags,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
// Gracefully handle any errors in trace extraction
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
exports.createOTELMixin = createOTELMixin;
|
|
81
|
+
/**
|
|
82
|
+
* Extract OTEL context from active span
|
|
83
|
+
*
|
|
84
|
+
* Similar to createOTELMixin but returns undefined if no span is active,
|
|
85
|
+
* making it easier to conditionally add trace context.
|
|
86
|
+
*
|
|
87
|
+
* @returns OTEL context or undefined if no active span
|
|
88
|
+
*/
|
|
89
|
+
const getOTELContext = () => {
|
|
90
|
+
if (!trace) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const span = trace.getActiveSpan();
|
|
95
|
+
if (!span) {
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const spanContext = span.spanContext();
|
|
99
|
+
return {
|
|
100
|
+
traceId: spanContext.traceId,
|
|
101
|
+
spanId: spanContext.spanId,
|
|
102
|
+
traceFlags: spanContext.traceFlags,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
exports.getOTELContext = getOTELContext;
|
|
110
|
+
/**
|
|
111
|
+
* Extract OTEL context from a specific span
|
|
112
|
+
*
|
|
113
|
+
* Useful when you have a reference to a span and want to extract its context
|
|
114
|
+
* for logging or propagation purposes.
|
|
115
|
+
*
|
|
116
|
+
* @param span - The OpenTelemetry span to extract context from
|
|
117
|
+
* @returns OTEL context from the span
|
|
118
|
+
*/
|
|
119
|
+
const getOTELContextFromSpan = (span) => {
|
|
120
|
+
try {
|
|
121
|
+
const spanContext = span.spanContext();
|
|
122
|
+
return {
|
|
123
|
+
traceId: spanContext.traceId,
|
|
124
|
+
spanId: spanContext.spanId,
|
|
125
|
+
traceFlags: spanContext.traceFlags,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
exports.getOTELContextFromSpan = getOTELContextFromSpan;
|
|
133
|
+
/**
|
|
134
|
+
* Extract OTEL context from an OTEL Context object
|
|
135
|
+
*
|
|
136
|
+
* Useful when working with OTEL Context propagation (e.g., in Pub/Sub messages)
|
|
137
|
+
* and you need to extract the span context for logging.
|
|
138
|
+
*
|
|
139
|
+
* @param context - The OpenTelemetry context to extract from
|
|
140
|
+
* @returns OTEL context from the context or undefined if no span
|
|
141
|
+
*/
|
|
142
|
+
const getOTELContextFromContext = (context) => {
|
|
143
|
+
if (!trace) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const span = trace.getSpan(context);
|
|
148
|
+
if (!span) {
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
const spanContext = span.spanContext();
|
|
152
|
+
return {
|
|
153
|
+
traceId: spanContext.traceId,
|
|
154
|
+
spanId: spanContext.spanId,
|
|
155
|
+
traceFlags: spanContext.traceFlags,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
exports.getOTELContextFromContext = getOTELContextFromContext;
|
|
163
|
+
/**
|
|
164
|
+
* Format trace ID for Cloud Logging
|
|
165
|
+
*
|
|
166
|
+
* Cloud Logging expects trace IDs in a specific format:
|
|
167
|
+
* projects/[PROJECT_ID]/traces/[TRACE_ID]
|
|
168
|
+
*
|
|
169
|
+
* This function formats a raw trace ID into the Cloud Logging format.
|
|
170
|
+
*
|
|
171
|
+
* @param traceId - Raw trace ID (32-character hex string)
|
|
172
|
+
* @param projectId - GCP project ID (optional, defaults to GOOGLE_CLOUD_PROJECT env var)
|
|
173
|
+
* @returns Formatted trace ID for Cloud Logging or undefined if inputs invalid
|
|
174
|
+
*/
|
|
175
|
+
const formatTraceIdForCloudLogging = (traceId, projectId) => {
|
|
176
|
+
if (!traceId) {
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
const project = projectId || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCP_PROJECT;
|
|
180
|
+
if (!project) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
return `projects/${project}/traces/${traceId}`;
|
|
184
|
+
};
|
|
185
|
+
exports.formatTraceIdForCloudLogging = formatTraceIdForCloudLogging;
|
|
186
|
+
/**
|
|
187
|
+
* Create Cloud Logging compatible log entry
|
|
188
|
+
*
|
|
189
|
+
* Combines OTEL context with log metadata to create a Cloud Logging compatible
|
|
190
|
+
* log entry with trace correlation.
|
|
191
|
+
*
|
|
192
|
+
* @param message - Log message
|
|
193
|
+
* @param metadata - Additional log metadata
|
|
194
|
+
* @param projectId - GCP project ID (optional)
|
|
195
|
+
* @returns Cloud Logging compatible log entry
|
|
196
|
+
*/
|
|
197
|
+
const createCloudLoggingEntry = (message,
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
199
|
+
metadata = {}, projectId
|
|
200
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
201
|
+
) => {
|
|
202
|
+
const otelContext = (0, exports.getOTELContext)();
|
|
203
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
204
|
+
const entry = {
|
|
205
|
+
message,
|
|
206
|
+
...metadata,
|
|
207
|
+
};
|
|
208
|
+
if (otelContext?.traceId) {
|
|
209
|
+
entry.traceId = otelContext.traceId;
|
|
210
|
+
entry.spanId = otelContext.spanId;
|
|
211
|
+
// Add Cloud Logging trace reference
|
|
212
|
+
const formattedTrace = (0, exports.formatTraceIdForCloudLogging)(otelContext.traceId, projectId);
|
|
213
|
+
if (formattedTrace) {
|
|
214
|
+
entry['logging.googleapis.com/trace'] = formattedTrace;
|
|
215
|
+
}
|
|
216
|
+
// Add span ID for Cloud Logging correlation
|
|
217
|
+
if (otelContext.spanId) {
|
|
218
|
+
entry['logging.googleapis.com/spanId'] = otelContext.spanId;
|
|
219
|
+
}
|
|
220
|
+
// Add trace sampled flag
|
|
221
|
+
if (otelContext.traceFlags !== undefined) {
|
|
222
|
+
entry['logging.googleapis.com/trace_sampled'] =
|
|
223
|
+
(otelContext.traceFlags & 1) === 1;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return entry;
|
|
227
|
+
};
|
|
228
|
+
exports.createCloudLoggingEntry = createCloudLoggingEntry;
|
|
229
|
+
/**
|
|
230
|
+
* Check if OpenTelemetry is available and active
|
|
231
|
+
*
|
|
232
|
+
* Useful for conditional OTEL feature usage in libraries and applications.
|
|
233
|
+
*
|
|
234
|
+
* @returns true if OTEL is available and there's an active span
|
|
235
|
+
*/
|
|
236
|
+
const isOTELActive = () => {
|
|
237
|
+
if (!trace) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const span = trace.getActiveSpan();
|
|
242
|
+
return !!span;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
exports.isOTELActive = isOTELActive;
|
|
249
|
+
/**
|
|
250
|
+
* Check if OpenTelemetry SDK is installed
|
|
251
|
+
*
|
|
252
|
+
* @returns true if @opentelemetry/api is installed
|
|
253
|
+
*/
|
|
254
|
+
const isOTELInstalled = () => {
|
|
255
|
+
return !!trace;
|
|
256
|
+
};
|
|
257
|
+
exports.isOTELInstalled = isOTELInstalled;
|
|
258
|
+
//# sourceMappingURL=otel.helper.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Context } from '../core';
|
|
2
|
+
/**
|
|
3
|
+
* Google Cloud Pub/Sub message structure
|
|
4
|
+
*/
|
|
5
|
+
export interface PubSubMessage {
|
|
6
|
+
message: {
|
|
7
|
+
data: string;
|
|
8
|
+
publishTime?: string;
|
|
9
|
+
messageId?: string;
|
|
10
|
+
attributes?: Record<string, string>;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* W3C Trace Context extracted from Pub/Sub message attributes
|
|
15
|
+
*/
|
|
16
|
+
export interface TraceContext {
|
|
17
|
+
traceparent?: string;
|
|
18
|
+
tracestate?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Type guard to check if request body is a Pub/Sub message
|
|
22
|
+
*
|
|
23
|
+
* @param body - Request body to check
|
|
24
|
+
* @returns True if body is a Pub/Sub message
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* if (isPubSubMessage(context.req.body)) {
|
|
28
|
+
* const traceContext = extractTraceContext(context.req.body);
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
export declare function isPubSubMessage(body: unknown): body is PubSubMessage;
|
|
32
|
+
/**
|
|
33
|
+
* Extract W3C Trace Context from Pub/Sub message attributes
|
|
34
|
+
*
|
|
35
|
+
* Extracts the following headers from message.attributes:
|
|
36
|
+
* - `traceparent`: W3C Trace Context propagation header (required)
|
|
37
|
+
* - `tracestate`: Vendor-specific trace state (optional)
|
|
38
|
+
*
|
|
39
|
+
* @param message - Pub/Sub message
|
|
40
|
+
* @returns Trace context object with traceparent and tracestate
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const traceContext = extractTraceContext(pubsubMessage);
|
|
44
|
+
* if (traceContext.traceparent) {
|
|
45
|
+
* console.log('Parent trace ID:', traceContext.traceparent);
|
|
46
|
+
* }
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractTraceContext(message: PubSubMessage): TraceContext;
|
|
49
|
+
/**
|
|
50
|
+
* Inject W3C Trace Context into Pub/Sub message attributes
|
|
51
|
+
*
|
|
52
|
+
* Adds trace context from the current OpenTelemetry span to message attributes.
|
|
53
|
+
* This enables distributed tracing across Pub/Sub publishers and subscribers.
|
|
54
|
+
*
|
|
55
|
+
* The trace context is extracted using OpenTelemetry's propagation API and
|
|
56
|
+
* injected into the message attributes as:
|
|
57
|
+
* - `traceparent`: W3C Trace Context version-traceid-spanid-flags
|
|
58
|
+
* - `tracestate`: Vendor-specific trace state (if present)
|
|
59
|
+
*
|
|
60
|
+
* @param message - Pub/Sub message to inject trace context into
|
|
61
|
+
* @param context - Noony request context (optional, used to get active span)
|
|
62
|
+
* @returns Message with trace context injected into attributes
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* // Publishing a message with trace context
|
|
66
|
+
* import { injectTraceContext } from '@noony-serverless/core';
|
|
67
|
+
*
|
|
68
|
+
* const message = {
|
|
69
|
+
* data: Buffer.from(JSON.stringify({ userId: '123' })).toString('base64'),
|
|
70
|
+
* attributes: {
|
|
71
|
+
* type: 'user.created'
|
|
72
|
+
* }
|
|
73
|
+
* };
|
|
74
|
+
*
|
|
75
|
+
* const tracedMessage = injectTraceContext(message, context);
|
|
76
|
+
* await pubsub.topic('users').publish(tracedMessage);
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* // Publishing without context (uses active span from context)
|
|
80
|
+
* const tracedMessage = injectTraceContext(message);
|
|
81
|
+
* await pubsub.topic('users').publish(tracedMessage);
|
|
82
|
+
*/
|
|
83
|
+
export declare function injectTraceContext(message: {
|
|
84
|
+
data: string;
|
|
85
|
+
attributes?: Record<string, string>;
|
|
86
|
+
}, context?: Context<unknown, unknown>): {
|
|
87
|
+
data: string;
|
|
88
|
+
attributes: Record<string, string>;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Extract trace context and create parent context for OpenTelemetry
|
|
92
|
+
*
|
|
93
|
+
* This is a lower-level utility used internally by OpenTelemetryMiddleware.
|
|
94
|
+
* Most users should use the middleware's automatic trace propagation instead.
|
|
95
|
+
*
|
|
96
|
+
* @param traceContext - Extracted W3C trace context
|
|
97
|
+
* @returns OpenTelemetry context with extracted trace context
|
|
98
|
+
*
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export declare function createParentContext(traceContext: TraceContext): Record<string, string>;
|
|
102
|
+
//# sourceMappingURL=pubsub-trace.utils.d.ts.map
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPubSubMessage = isPubSubMessage;
|
|
4
|
+
exports.extractTraceContext = extractTraceContext;
|
|
5
|
+
exports.injectTraceContext = injectTraceContext;
|
|
6
|
+
exports.createParentContext = createParentContext;
|
|
7
|
+
/**
|
|
8
|
+
* Type guard to check if request body is a Pub/Sub message
|
|
9
|
+
*
|
|
10
|
+
* @param body - Request body to check
|
|
11
|
+
* @returns True if body is a Pub/Sub message
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* if (isPubSubMessage(context.req.body)) {
|
|
15
|
+
* const traceContext = extractTraceContext(context.req.body);
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
function isPubSubMessage(body) {
|
|
19
|
+
return (!!body &&
|
|
20
|
+
typeof body === 'object' &&
|
|
21
|
+
'message' in body &&
|
|
22
|
+
typeof body.message === 'object' &&
|
|
23
|
+
'data' in body.message);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Extract W3C Trace Context from Pub/Sub message attributes
|
|
27
|
+
*
|
|
28
|
+
* Extracts the following headers from message.attributes:
|
|
29
|
+
* - `traceparent`: W3C Trace Context propagation header (required)
|
|
30
|
+
* - `tracestate`: Vendor-specific trace state (optional)
|
|
31
|
+
*
|
|
32
|
+
* @param message - Pub/Sub message
|
|
33
|
+
* @returns Trace context object with traceparent and tracestate
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* const traceContext = extractTraceContext(pubsubMessage);
|
|
37
|
+
* if (traceContext.traceparent) {
|
|
38
|
+
* console.log('Parent trace ID:', traceContext.traceparent);
|
|
39
|
+
* }
|
|
40
|
+
*/
|
|
41
|
+
function extractTraceContext(message) {
|
|
42
|
+
const attributes = message.message.attributes || {};
|
|
43
|
+
return {
|
|
44
|
+
traceparent: attributes.traceparent,
|
|
45
|
+
tracestate: attributes.tracestate,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Inject W3C Trace Context into Pub/Sub message attributes
|
|
50
|
+
*
|
|
51
|
+
* Adds trace context from the current OpenTelemetry span to message attributes.
|
|
52
|
+
* This enables distributed tracing across Pub/Sub publishers and subscribers.
|
|
53
|
+
*
|
|
54
|
+
* The trace context is extracted using OpenTelemetry's propagation API and
|
|
55
|
+
* injected into the message attributes as:
|
|
56
|
+
* - `traceparent`: W3C Trace Context version-traceid-spanid-flags
|
|
57
|
+
* - `tracestate`: Vendor-specific trace state (if present)
|
|
58
|
+
*
|
|
59
|
+
* @param message - Pub/Sub message to inject trace context into
|
|
60
|
+
* @param context - Noony request context (optional, used to get active span)
|
|
61
|
+
* @returns Message with trace context injected into attributes
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // Publishing a message with trace context
|
|
65
|
+
* import { injectTraceContext } from '@noony-serverless/core';
|
|
66
|
+
*
|
|
67
|
+
* const message = {
|
|
68
|
+
* data: Buffer.from(JSON.stringify({ userId: '123' })).toString('base64'),
|
|
69
|
+
* attributes: {
|
|
70
|
+
* type: 'user.created'
|
|
71
|
+
* }
|
|
72
|
+
* };
|
|
73
|
+
*
|
|
74
|
+
* const tracedMessage = injectTraceContext(message, context);
|
|
75
|
+
* await pubsub.topic('users').publish(tracedMessage);
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* // Publishing without context (uses active span from context)
|
|
79
|
+
* const tracedMessage = injectTraceContext(message);
|
|
80
|
+
* await pubsub.topic('users').publish(tracedMessage);
|
|
81
|
+
*/
|
|
82
|
+
function injectTraceContext(message, context) {
|
|
83
|
+
// Initialize attributes if not present
|
|
84
|
+
const attributes = message.attributes || {};
|
|
85
|
+
try {
|
|
86
|
+
// Try to use OpenTelemetry API if available
|
|
87
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
88
|
+
const otelApi = require('@opentelemetry/api');
|
|
89
|
+
const { trace, propagation, context: otelContext } = otelApi;
|
|
90
|
+
// Get current context (either from provided context or active context)
|
|
91
|
+
let activeContext = otelContext.active();
|
|
92
|
+
// If Noony context provided, try to get span from businessData
|
|
93
|
+
if (context) {
|
|
94
|
+
const span = context.businessData.get('otel_span');
|
|
95
|
+
if (span && typeof span === 'object' && 'spanContext' in span) {
|
|
96
|
+
// Create context with span
|
|
97
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
98
|
+
activeContext = trace.setSpan(activeContext, span);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Get active span if no span found in businessData
|
|
102
|
+
const activeSpan = trace.getSpan(activeContext);
|
|
103
|
+
if (!activeSpan) {
|
|
104
|
+
// No active span, return message without trace context
|
|
105
|
+
return {
|
|
106
|
+
data: message.data,
|
|
107
|
+
attributes,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Inject trace context into a carrier object
|
|
111
|
+
const carrier = {};
|
|
112
|
+
propagation.inject(activeContext, carrier);
|
|
113
|
+
// Merge trace context into message attributes
|
|
114
|
+
return {
|
|
115
|
+
data: message.data,
|
|
116
|
+
attributes: {
|
|
117
|
+
...attributes,
|
|
118
|
+
...carrier, // This adds traceparent and tracestate
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// OpenTelemetry not available or error during injection
|
|
124
|
+
// Return message without trace context (fail gracefully)
|
|
125
|
+
console.warn('[Telemetry] Failed to inject trace context:', error);
|
|
126
|
+
return {
|
|
127
|
+
data: message.data,
|
|
128
|
+
attributes,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Extract trace context and create parent context for OpenTelemetry
|
|
134
|
+
*
|
|
135
|
+
* This is a lower-level utility used internally by OpenTelemetryMiddleware.
|
|
136
|
+
* Most users should use the middleware's automatic trace propagation instead.
|
|
137
|
+
*
|
|
138
|
+
* @param traceContext - Extracted W3C trace context
|
|
139
|
+
* @returns OpenTelemetry context with extracted trace context
|
|
140
|
+
*
|
|
141
|
+
* @internal
|
|
142
|
+
*/
|
|
143
|
+
function createParentContext(traceContext) {
|
|
144
|
+
if (!traceContext.traceparent) {
|
|
145
|
+
return {};
|
|
146
|
+
}
|
|
147
|
+
const carrier = {
|
|
148
|
+
traceparent: traceContext.traceparent,
|
|
149
|
+
};
|
|
150
|
+
if (traceContext.tracestate) {
|
|
151
|
+
carrier.tracestate = traceContext.tracestate;
|
|
152
|
+
}
|
|
153
|
+
return carrier;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=pubsub-trace.utils.js.map
|