@atrim/instrument-node 0.5.0-c05e3a1-20251119131235 → 0.5.1
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 +66 -0
- package/package.json +6 -6
- package/target/dist/index.cjs +74 -58
- package/target/dist/index.cjs.map +1 -1
- package/target/dist/index.d.cts +140 -107
- package/target/dist/index.d.ts +140 -107
- package/target/dist/index.js +68 -57
- package/target/dist/index.js.map +1 -1
- package/target/dist/integrations/effect/index.cjs +145 -13
- package/target/dist/integrations/effect/index.cjs.map +1 -1
- package/target/dist/integrations/effect/index.d.cts +187 -12
- package/target/dist/integrations/effect/index.d.ts +187 -12
- package/target/dist/integrations/effect/index.js +144 -15
- package/target/dist/integrations/effect/index.js.map +1 -1
package/README.md
CHANGED
|
@@ -161,6 +161,72 @@ const program = Effect.gen(function* () {
|
|
|
161
161
|
await Effect.runPromise(program)
|
|
162
162
|
```
|
|
163
163
|
|
|
164
|
+
### Effect-TS Span Annotation Helpers
|
|
165
|
+
|
|
166
|
+
The library provides 9 production-tested annotation helpers for enriching spans with semantic attributes:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
import { Effect } from 'effect'
|
|
170
|
+
import {
|
|
171
|
+
annotateUser,
|
|
172
|
+
annotateBatch,
|
|
173
|
+
annotateDataSize,
|
|
174
|
+
annotateLLM,
|
|
175
|
+
annotateQuery,
|
|
176
|
+
annotateHttpRequest,
|
|
177
|
+
annotateError,
|
|
178
|
+
annotatePriority,
|
|
179
|
+
annotateCache,
|
|
180
|
+
autoEnrichSpan,
|
|
181
|
+
withAutoEnrichedSpan
|
|
182
|
+
} from '@atrim/instrument-node/effect'
|
|
183
|
+
|
|
184
|
+
// Example: Batch processing with automatic enrichment
|
|
185
|
+
const processBatch = Effect.gen(function* () {
|
|
186
|
+
// Auto-enrich with Effect metadata (fiber ID, status, parent span info)
|
|
187
|
+
yield* autoEnrichSpan()
|
|
188
|
+
|
|
189
|
+
// Add user context
|
|
190
|
+
yield* annotateUser('user-123', 'user@example.com')
|
|
191
|
+
|
|
192
|
+
// Add batch metadata
|
|
193
|
+
yield* annotateBatch(100, 10) // 100 items in batches of 10
|
|
194
|
+
|
|
195
|
+
// Process items
|
|
196
|
+
const results = yield* processItems(items)
|
|
197
|
+
|
|
198
|
+
// Update with results
|
|
199
|
+
yield* annotateBatch(100, 10, results.success, results.failures)
|
|
200
|
+
|
|
201
|
+
return results
|
|
202
|
+
}).pipe(Effect.withSpan('batch.process'))
|
|
203
|
+
|
|
204
|
+
// Or use the convenience wrapper
|
|
205
|
+
const processWithAutoEnrich = withAutoEnrichedSpan('batch.process')(
|
|
206
|
+
Effect.gen(function* () {
|
|
207
|
+
yield* annotateBatch(100, 10)
|
|
208
|
+
return yield* processItems(items)
|
|
209
|
+
})
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Available annotation helpers:**
|
|
214
|
+
- `annotateUser(userId, email?, username?)` - User context
|
|
215
|
+
- `annotateDataSize(bytes, items, compressionRatio?)` - Data size metrics
|
|
216
|
+
- `annotateBatch(totalItems, batchSize, successCount?, failureCount?)` - Batch operations
|
|
217
|
+
- `annotateLLM(model, provider, tokens?)` - LLM operations (GPT, Claude, etc.)
|
|
218
|
+
- `annotateQuery(query, duration?, rowCount?, database?)` - Database queries
|
|
219
|
+
- `annotateHttpRequest(method, url, statusCode?, contentLength?)` - HTTP requests
|
|
220
|
+
- `annotateError(error, recoverable, errorType?)` - Error context
|
|
221
|
+
- `annotatePriority(priority, reason?)` - Operation priority
|
|
222
|
+
- `annotateCache(hit, key, ttl?)` - Cache operations
|
|
223
|
+
|
|
224
|
+
**Auto-enrichment utilities:**
|
|
225
|
+
- `autoEnrichSpan()` - Automatically add Effect metadata (fiber ID, status, parent span info)
|
|
226
|
+
- `withAutoEnrichedSpan(name, options?)` - Wrapper combining `Effect.withSpan()` + auto-enrichment
|
|
227
|
+
|
|
228
|
+
All helpers return `Effect.Effect<void>` and use `Effect.annotateCurrentSpan()` under the hood.
|
|
229
|
+
|
|
164
230
|
### Bun Runtime
|
|
165
231
|
|
|
166
232
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atrim/instrument-node",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "OpenTelemetry instrumentation for Node.js with centralized YAML configuration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"LICENSE"
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@effect/opentelemetry": "^0.59.
|
|
58
|
-
"@effect/platform": "^0.93.
|
|
59
|
-
"@effect/platform-node": "
|
|
57
|
+
"@effect/opentelemetry": "^0.59.1",
|
|
58
|
+
"@effect/platform": "^0.93.6",
|
|
59
|
+
"@effect/platform-node": "^0.103.0",
|
|
60
60
|
"@opentelemetry/auto-instrumentations-node": "^0.67.0",
|
|
61
61
|
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
|
62
62
|
"@opentelemetry/instrumentation": "^0.208.0",
|
|
63
63
|
"@opentelemetry/sdk-node": "^0.208.0",
|
|
64
64
|
"@opentelemetry/sdk-trace-base": "^2.2.0",
|
|
65
|
-
"effect": "^3.19.
|
|
65
|
+
"effect": "^3.19.8",
|
|
66
66
|
"yaml": "^2.3.0",
|
|
67
67
|
"zod": "^3.22.0"
|
|
68
68
|
},
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@opentelemetry/semantic-conventions": "^1.38.0",
|
|
77
77
|
"@types/node": "^20.10.0",
|
|
78
78
|
"@vitest/coverage-v8": "^4.0.8",
|
|
79
|
-
"effect": "^3.19.
|
|
79
|
+
"effect": "^3.19.8",
|
|
80
80
|
"testcontainers": "^11.8.1",
|
|
81
81
|
"tsup": "^8.0.1",
|
|
82
82
|
"tsx": "^4.7.0",
|
package/target/dist/index.cjs
CHANGED
|
@@ -706,6 +706,15 @@ var getServiceInfoWithFallback = detectServiceInfo.pipe(
|
|
|
706
706
|
})
|
|
707
707
|
)
|
|
708
708
|
);
|
|
709
|
+
async function detectServiceInfoAsync() {
|
|
710
|
+
return effect.Effect.runPromise(getServiceInfoWithFallback);
|
|
711
|
+
}
|
|
712
|
+
async function getServiceNameAsync() {
|
|
713
|
+
return effect.Effect.runPromise(getServiceName);
|
|
714
|
+
}
|
|
715
|
+
async function getServiceVersionAsync() {
|
|
716
|
+
return effect.Effect.runPromise(getServiceVersion);
|
|
717
|
+
}
|
|
709
718
|
var NodeConfigLoaderLive = ConfigLoaderLive.pipe(
|
|
710
719
|
effect.Layer.provide(effect.Layer.mergeAll(platformNode.NodeContext.layer, platform.FetchHttpClient.layer))
|
|
711
720
|
);
|
|
@@ -787,7 +796,7 @@ async function loadConfigWithOptions(options = {}) {
|
|
|
787
796
|
|
|
788
797
|
// src/core/sdk-initializer.ts
|
|
789
798
|
var sdkInstance = null;
|
|
790
|
-
var
|
|
799
|
+
var initializationPromise = null;
|
|
791
800
|
function buildHttpInstrumentationConfig(options, config, _otlpEndpoint) {
|
|
792
801
|
const httpConfig = { enabled: true };
|
|
793
802
|
const programmaticPatterns = options.http?.ignoreOutgoingUrls || [];
|
|
@@ -903,38 +912,27 @@ function isTracingAlreadyInitialized() {
|
|
|
903
912
|
return false;
|
|
904
913
|
}
|
|
905
914
|
}
|
|
906
|
-
|
|
915
|
+
async function initializeSdk(options = {}) {
|
|
907
916
|
if (sdkInstance) {
|
|
908
917
|
logger.warn("@atrim/instrumentation: SDK already initialized. Returning existing instance.");
|
|
909
918
|
return sdkInstance;
|
|
910
919
|
}
|
|
911
|
-
if (
|
|
920
|
+
if (initializationPromise) {
|
|
912
921
|
logger.log(
|
|
913
|
-
"@atrim/instrumentation: SDK
|
|
922
|
+
"@atrim/instrumentation: SDK already initialized, waiting for initialization to complete..."
|
|
914
923
|
);
|
|
915
|
-
return
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
);
|
|
928
|
-
return result;
|
|
929
|
-
});
|
|
930
|
-
var performInitializationEffect = (options) => effect.Effect.gen(function* () {
|
|
931
|
-
const config = yield* effect.Effect.tryPromise({
|
|
932
|
-
try: () => loadConfigWithOptions(options),
|
|
933
|
-
catch: (error) => new InitializationError2({
|
|
934
|
-
reason: "Failed to load configuration",
|
|
935
|
-
cause: error
|
|
936
|
-
})
|
|
937
|
-
});
|
|
924
|
+
return initializationPromise;
|
|
925
|
+
}
|
|
926
|
+
initializationPromise = performInitialization(options);
|
|
927
|
+
try {
|
|
928
|
+
const result = await initializationPromise;
|
|
929
|
+
return result;
|
|
930
|
+
} finally {
|
|
931
|
+
initializationPromise = null;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
async function performInitialization(options) {
|
|
935
|
+
const config = await loadConfigWithOptions(options);
|
|
938
936
|
const loggingLevel = config.instrumentation.logging || "on";
|
|
939
937
|
logger.setLevel(loggingLevel);
|
|
940
938
|
const alreadyInitialized = isTracingAlreadyInitialized();
|
|
@@ -950,25 +948,14 @@ var performInitializationEffect = (options) => effect.Effect.gen(function* () {
|
|
|
950
948
|
logger.log("");
|
|
951
949
|
return null;
|
|
952
950
|
}
|
|
953
|
-
const serviceInfo =
|
|
954
|
-
effect.Effect.catchAll(
|
|
955
|
-
() => effect.Effect.succeed({
|
|
956
|
-
name: "unknown-service",
|
|
957
|
-
version: void 0
|
|
958
|
-
})
|
|
959
|
-
)
|
|
960
|
-
);
|
|
951
|
+
const serviceInfo = await detectServiceInfoAsync();
|
|
961
952
|
const serviceName = options.serviceName || serviceInfo.name;
|
|
962
953
|
const serviceVersion = options.serviceVersion || serviceInfo.version;
|
|
963
|
-
const rawExporter =
|
|
964
|
-
const exporter =
|
|
954
|
+
const rawExporter = createOtlpExporter(options.otlp);
|
|
955
|
+
const exporter = new SafeSpanExporter(rawExporter);
|
|
965
956
|
const useSimpleProcessor = process.env.NODE_ENV === "test" || process.env.OTEL_USE_SIMPLE_PROCESSOR === "true";
|
|
966
|
-
const baseProcessor =
|
|
967
|
-
|
|
968
|
-
);
|
|
969
|
-
const patternProcessor = yield* effect.Effect.sync(
|
|
970
|
-
() => new PatternSpanProcessor(config, baseProcessor)
|
|
971
|
-
);
|
|
957
|
+
const baseProcessor = useSimpleProcessor ? new sdkTraceBase.SimpleSpanProcessor(exporter) : new sdkTraceBase.BatchSpanProcessor(exporter);
|
|
958
|
+
const patternProcessor = new PatternSpanProcessor(config, baseProcessor);
|
|
972
959
|
const instrumentations = [];
|
|
973
960
|
const hasWebFramework = hasWebFrameworkInstalled();
|
|
974
961
|
const enableAutoInstrumentation = shouldEnableAutoInstrumentation(
|
|
@@ -981,11 +968,15 @@ var performInitializationEffect = (options) => effect.Effect.gen(function* () {
|
|
|
981
968
|
const undiciConfig = buildUndiciInstrumentationConfig(options, config);
|
|
982
969
|
instrumentations.push(
|
|
983
970
|
...autoInstrumentationsNode.getNodeAutoInstrumentations({
|
|
971
|
+
// Enable HTTP instrumentation with filtering (for http/https modules)
|
|
984
972
|
"@opentelemetry/instrumentation-http": httpConfig,
|
|
973
|
+
// Enable undici instrumentation with filtering (for fetch API)
|
|
985
974
|
"@opentelemetry/instrumentation-undici": undiciConfig,
|
|
975
|
+
// Enable web framework instrumentations
|
|
986
976
|
"@opentelemetry/instrumentation-express": { enabled: true },
|
|
987
977
|
"@opentelemetry/instrumentation-fastify": { enabled: true },
|
|
988
978
|
"@opentelemetry/instrumentation-koa": { enabled: true },
|
|
979
|
+
// Disable noisy instrumentations by default
|
|
989
980
|
"@opentelemetry/instrumentation-fs": { enabled: false },
|
|
990
981
|
"@opentelemetry/instrumentation-dns": { enabled: false }
|
|
991
982
|
})
|
|
@@ -1013,20 +1004,18 @@ var performInitializationEffect = (options) => effect.Effect.gen(function* () {
|
|
|
1013
1004
|
serviceName,
|
|
1014
1005
|
...serviceVersion && { serviceVersion },
|
|
1015
1006
|
instrumentations,
|
|
1007
|
+
// Allow advanced overrides
|
|
1016
1008
|
...options.sdk
|
|
1017
1009
|
};
|
|
1018
|
-
const sdk =
|
|
1019
|
-
|
|
1020
|
-
s.start();
|
|
1021
|
-
return s;
|
|
1022
|
-
});
|
|
1010
|
+
const sdk = new sdkNode.NodeSDK(sdkConfig);
|
|
1011
|
+
sdk.start();
|
|
1023
1012
|
sdkInstance = sdk;
|
|
1024
1013
|
if (!options.disableAutoShutdown) {
|
|
1025
|
-
|
|
1014
|
+
registerShutdownHandlers(sdk);
|
|
1026
1015
|
}
|
|
1027
1016
|
logInitialization(config, serviceName, serviceVersion, options, enableAutoInstrumentation);
|
|
1028
1017
|
return sdk;
|
|
1029
|
-
}
|
|
1018
|
+
}
|
|
1030
1019
|
function getSdkInstance() {
|
|
1031
1020
|
return sdkInstance;
|
|
1032
1021
|
}
|
|
@@ -1039,7 +1028,7 @@ async function shutdownSdk() {
|
|
|
1039
1028
|
}
|
|
1040
1029
|
function resetSdk() {
|
|
1041
1030
|
sdkInstance = null;
|
|
1042
|
-
|
|
1031
|
+
initializationPromise = null;
|
|
1043
1032
|
}
|
|
1044
1033
|
function registerShutdownHandlers(sdk) {
|
|
1045
1034
|
const shutdown = async (signal) => {
|
|
@@ -1096,8 +1085,30 @@ function logInitialization(config, serviceName, serviceVersion, options, autoIns
|
|
|
1096
1085
|
}
|
|
1097
1086
|
|
|
1098
1087
|
// src/api.ts
|
|
1099
|
-
|
|
1100
|
-
const sdk =
|
|
1088
|
+
async function initializeInstrumentation(options = {}) {
|
|
1089
|
+
const sdk = await initializeSdk(options);
|
|
1090
|
+
if (sdk) {
|
|
1091
|
+
const config = await loadConfigWithOptions(options);
|
|
1092
|
+
initializePatternMatcher(config);
|
|
1093
|
+
}
|
|
1094
|
+
return sdk;
|
|
1095
|
+
}
|
|
1096
|
+
async function initializePatternMatchingOnly(options = {}) {
|
|
1097
|
+
const config = await loadConfigWithOptions(options);
|
|
1098
|
+
initializePatternMatcher(config);
|
|
1099
|
+
logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
|
|
1100
|
+
logger.log(
|
|
1101
|
+
" Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
var initializeInstrumentationEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
1105
|
+
const sdk = yield* effect.Effect.tryPromise({
|
|
1106
|
+
try: () => initializeSdk(options),
|
|
1107
|
+
catch: (error) => new InitializationError2({
|
|
1108
|
+
reason: "SDK initialization failed",
|
|
1109
|
+
cause: error
|
|
1110
|
+
})
|
|
1111
|
+
});
|
|
1101
1112
|
if (sdk) {
|
|
1102
1113
|
yield* effect.Effect.tryPromise({
|
|
1103
1114
|
try: () => loadConfigWithOptions(options),
|
|
@@ -1115,7 +1126,7 @@ var initializeInstrumentation = (options = {}) => effect.Effect.gen(function* ()
|
|
|
1115
1126
|
}
|
|
1116
1127
|
return sdk;
|
|
1117
1128
|
});
|
|
1118
|
-
var
|
|
1129
|
+
var initializePatternMatchingOnlyEffect = (options = {}) => effect.Effect.gen(function* () {
|
|
1119
1130
|
const config = yield* effect.Effect.tryPromise({
|
|
1120
1131
|
try: () => loadConfigWithOptions(options),
|
|
1121
1132
|
catch: (error) => new ConfigError2({
|
|
@@ -1125,7 +1136,7 @@ var initializePatternMatchingOnly = (options = {}) => effect.Effect.gen(function
|
|
|
1125
1136
|
});
|
|
1126
1137
|
yield* effect.Effect.sync(() => {
|
|
1127
1138
|
initializePatternMatcher(config);
|
|
1128
|
-
logger.log("@atrim/instrumentation: Pattern matching initialized (
|
|
1139
|
+
logger.log("@atrim/instrumentation: Pattern matching initialized (legacy mode)");
|
|
1129
1140
|
logger.log(
|
|
1130
1141
|
" Note: NodeSDK is not initialized. Use initializeInstrumentation() for complete setup."
|
|
1131
1142
|
);
|
|
@@ -1241,15 +1252,20 @@ exports.annotateDbQuery = annotateDbQuery;
|
|
|
1241
1252
|
exports.annotateHttpRequest = annotateHttpRequest;
|
|
1242
1253
|
exports.clearConfigCache = _resetConfigLoaderCache;
|
|
1243
1254
|
exports.createOtlpExporter = createOtlpExporter;
|
|
1244
|
-
exports.detectServiceInfo =
|
|
1255
|
+
exports.detectServiceInfo = detectServiceInfoAsync;
|
|
1256
|
+
exports.detectServiceInfoEffect = detectServiceInfo;
|
|
1245
1257
|
exports.getOtlpEndpoint = getOtlpEndpoint;
|
|
1246
1258
|
exports.getPatternMatcher = getPatternMatcher;
|
|
1247
1259
|
exports.getSdkInstance = getSdkInstance;
|
|
1248
1260
|
exports.getServiceInfoWithFallback = getServiceInfoWithFallback;
|
|
1249
|
-
exports.getServiceName =
|
|
1250
|
-
exports.
|
|
1261
|
+
exports.getServiceName = getServiceNameAsync;
|
|
1262
|
+
exports.getServiceNameEffect = getServiceName;
|
|
1263
|
+
exports.getServiceVersion = getServiceVersionAsync;
|
|
1264
|
+
exports.getServiceVersionEffect = getServiceVersion;
|
|
1251
1265
|
exports.initializeInstrumentation = initializeInstrumentation;
|
|
1266
|
+
exports.initializeInstrumentationEffect = initializeInstrumentationEffect;
|
|
1252
1267
|
exports.initializePatternMatchingOnly = initializePatternMatchingOnly;
|
|
1268
|
+
exports.initializePatternMatchingOnlyEffect = initializePatternMatchingOnlyEffect;
|
|
1253
1269
|
exports.loadConfig = loadConfig;
|
|
1254
1270
|
exports.loadConfigFromInline = loadConfigFromInline;
|
|
1255
1271
|
exports.loadConfigWithOptions = loadConfigWithOptions;
|