@ryanzeng/nest-observe 0.1.0 → 0.1.2
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 +31 -1
- package/dist/index.d.mts +42 -15
- package/dist/index.d.ts +42 -15
- package/dist/index.js +379 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +388 -60
- package/dist/index.mjs.map +1 -1
- package/dist/register.js +327 -52
- package/dist/register.js.map +1 -1
- package/dist/register.mjs +330 -52
- package/dist/register.mjs.map +1 -1
- package/package.json +19 -9
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __export(index_exports, {
|
|
|
32
32
|
CompatibleNestInstrumentation: () => CompatibleNestInstrumentation,
|
|
33
33
|
HttpRequestMetrics: () => HttpRequestMetrics,
|
|
34
34
|
IgnoreTrace: () => IgnoreTrace,
|
|
35
|
+
NestHttpMetricsInterceptor: () => NestHttpMetricsInterceptor,
|
|
35
36
|
NestLoggerInstrumentation: () => NestLoggerInstrumentation,
|
|
36
37
|
NestMethodInstrumenter: () => NestMethodInstrumenter,
|
|
37
38
|
OBSERVE_HANDLE: () => OBSERVE_HANDLE,
|
|
@@ -139,7 +140,9 @@ function resolveObserveConfig(options = {}, env = process.env) {
|
|
|
139
140
|
allowedHeaders,
|
|
140
141
|
exportTimeoutMillis: positiveInteger(options.exportTimeoutMillis ?? env.OTEL_EXPORTER_OTLP_TIMEOUT, 1e4),
|
|
141
142
|
metricExportIntervalMillis: positiveInteger(options.metricExportIntervalMillis ?? env.OTEL_METRIC_EXPORT_INTERVAL, 6e4),
|
|
142
|
-
resourceAttributes
|
|
143
|
+
resourceAttributes,
|
|
144
|
+
diagnosticLogging: booleanValue(options.diagnosticLogging, true),
|
|
145
|
+
failFast: booleanValue(options.failFast, false)
|
|
143
146
|
};
|
|
144
147
|
if (options.exporters) result.exporters = options.exporters;
|
|
145
148
|
return result;
|
|
@@ -232,19 +235,28 @@ function invokeWithSpan(tracer, spanName, attributes, invoke, onFinish) {
|
|
|
232
235
|
// src/decorators/trace.decorator.ts
|
|
233
236
|
function decorateMethod(target, propertyKey, descriptor, options) {
|
|
234
237
|
const original = descriptor.value;
|
|
235
|
-
if (typeof original !== "function" || isTraceIgnored(original, target.constructor))
|
|
238
|
+
if (typeof original !== "function" || isTraceIgnored(original, target.constructor))
|
|
239
|
+
return;
|
|
236
240
|
const className = target.constructor?.name || "Anonymous";
|
|
237
241
|
const methodName = String(propertyKey);
|
|
238
242
|
const wrapped = function(...args) {
|
|
239
|
-
const tracer = import_api2.trace.getTracer("@
|
|
240
|
-
return invokeWithSpan(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
const tracer = import_api2.trace.getTracer("@ryanzeng/nest-observe");
|
|
244
|
+
return invokeWithSpan(
|
|
245
|
+
tracer,
|
|
246
|
+
options.name ?? `${className}.${methodName}`,
|
|
247
|
+
{
|
|
248
|
+
"code.function.name": methodName,
|
|
249
|
+
"nestjs.class": className,
|
|
250
|
+
"nestjs.method": methodName,
|
|
251
|
+
...options.attributes
|
|
252
|
+
},
|
|
253
|
+
() => original.apply(this, args)
|
|
254
|
+
);
|
|
246
255
|
};
|
|
247
|
-
Object.defineProperty(wrapped, "name", {
|
|
256
|
+
Object.defineProperty(wrapped, "name", {
|
|
257
|
+
value: original.name,
|
|
258
|
+
configurable: true
|
|
259
|
+
});
|
|
248
260
|
markTraceDecorated(wrapped);
|
|
249
261
|
descriptor.value = wrapped;
|
|
250
262
|
}
|
|
@@ -326,7 +338,13 @@ var SEVERITY = {
|
|
|
326
338
|
};
|
|
327
339
|
function bodyValue(message) {
|
|
328
340
|
if (message instanceof Error) return redactText(message.stack ?? `${message.name}: ${message.message}`);
|
|
329
|
-
|
|
341
|
+
const sanitized = redact(message);
|
|
342
|
+
if (sanitized === null || typeof sanitized !== "object") return sanitized;
|
|
343
|
+
try {
|
|
344
|
+
return JSON.stringify(sanitized, (_key, value) => typeof value === "bigint" ? value.toString() : value);
|
|
345
|
+
} catch {
|
|
346
|
+
return redactText(String(sanitized));
|
|
347
|
+
}
|
|
330
348
|
}
|
|
331
349
|
var NestLoggerInstrumentation = class {
|
|
332
350
|
constructor(emitter, resourceAttributes = {}) {
|
|
@@ -389,7 +407,7 @@ var SEVERITY_NUMBER = {
|
|
|
389
407
|
};
|
|
390
408
|
var OpenTelemetryLogEmitter = class {
|
|
391
409
|
logger;
|
|
392
|
-
constructor(name = "@
|
|
410
|
+
constructor(name = "@ryanzeng/nest-observe", version, logger) {
|
|
393
411
|
this.logger = logger ?? import_api_logs.logs.getLogger(name, version);
|
|
394
412
|
}
|
|
395
413
|
emit(record) {
|
|
@@ -560,9 +578,14 @@ var HttpRequestMetrics = class {
|
|
|
560
578
|
errorCount;
|
|
561
579
|
startedAt = /* @__PURE__ */ new WeakMap();
|
|
562
580
|
start(request) {
|
|
581
|
+
if (this.startedAt.has(request)) return false;
|
|
563
582
|
this.startedAt.set(request, process.hrtime.bigint());
|
|
583
|
+
return true;
|
|
564
584
|
}
|
|
565
585
|
record(request, response) {
|
|
586
|
+
const started = this.startedAt.get(request);
|
|
587
|
+
if (started === void 0) return false;
|
|
588
|
+
this.startedAt.delete(request);
|
|
566
589
|
const statusCode = Number.isFinite(response.statusCode) ? response.statusCode : 0;
|
|
567
590
|
const attributes = {
|
|
568
591
|
"service.name": this.serviceName,
|
|
@@ -571,12 +594,9 @@ var HttpRequestMetrics = class {
|
|
|
571
594
|
"http.response.status_code": statusCode
|
|
572
595
|
};
|
|
573
596
|
this.requestCount.add(1, attributes);
|
|
574
|
-
|
|
575
|
-
if (started !== void 0) {
|
|
576
|
-
this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
|
|
577
|
-
this.startedAt.delete(request);
|
|
578
|
-
}
|
|
597
|
+
this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
|
|
579
598
|
if (statusCode >= 500) this.errorCount.add(1, attributes);
|
|
599
|
+
return true;
|
|
580
600
|
}
|
|
581
601
|
};
|
|
582
602
|
|
|
@@ -665,6 +685,7 @@ var RuntimeMetrics = class {
|
|
|
665
685
|
var import_common2 = require("@nestjs/common");
|
|
666
686
|
var import_core = require("@nestjs/core");
|
|
667
687
|
var import_api7 = require("@opentelemetry/api");
|
|
688
|
+
var import_rxjs2 = require("rxjs");
|
|
668
689
|
|
|
669
690
|
// src/sdk.ts
|
|
670
691
|
var import_api_logs2 = require("@opentelemetry/api-logs");
|
|
@@ -680,6 +701,113 @@ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
|
|
|
680
701
|
var import_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
|
|
681
702
|
var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
|
|
682
703
|
|
|
704
|
+
// src/diagnostics.ts
|
|
705
|
+
function toError(value) {
|
|
706
|
+
if (value instanceof Error) return value;
|
|
707
|
+
if (typeof value === "string") return new Error(value);
|
|
708
|
+
try {
|
|
709
|
+
return new Error(JSON.stringify(value));
|
|
710
|
+
} catch {
|
|
711
|
+
return new Error(String(value));
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
function sanitizeError(error) {
|
|
715
|
+
const sanitized = new Error(redactText(error.message));
|
|
716
|
+
sanitized.name = error.name;
|
|
717
|
+
if (error.stack) sanitized.stack = redactText(error.stack);
|
|
718
|
+
return sanitized;
|
|
719
|
+
}
|
|
720
|
+
var ObserveDiagnostics = class {
|
|
721
|
+
constructor(logging, onError) {
|
|
722
|
+
this.logging = logging;
|
|
723
|
+
this.onError = onError;
|
|
724
|
+
}
|
|
725
|
+
logging;
|
|
726
|
+
onError;
|
|
727
|
+
currentStatus = "starting";
|
|
728
|
+
currentError;
|
|
729
|
+
failedSignals = /* @__PURE__ */ new Set();
|
|
730
|
+
reported = /* @__PURE__ */ new Set();
|
|
731
|
+
get status() {
|
|
732
|
+
return this.currentStatus;
|
|
733
|
+
}
|
|
734
|
+
get lastError() {
|
|
735
|
+
return this.currentError;
|
|
736
|
+
}
|
|
737
|
+
activate() {
|
|
738
|
+
if (this.currentStatus === "starting") this.currentStatus = "active";
|
|
739
|
+
}
|
|
740
|
+
inactive() {
|
|
741
|
+
this.currentStatus = "inactive";
|
|
742
|
+
}
|
|
743
|
+
stop() {
|
|
744
|
+
this.currentStatus = "stopped";
|
|
745
|
+
}
|
|
746
|
+
success(signal) {
|
|
747
|
+
this.failedSignals.delete(signal);
|
|
748
|
+
for (const fingerprint of this.reported) {
|
|
749
|
+
if (fingerprint.startsWith(`${signal}\0`)) this.reported.delete(fingerprint);
|
|
750
|
+
}
|
|
751
|
+
if (this.currentStatus === "degraded" && this.failedSignals.size === 0) {
|
|
752
|
+
this.currentStatus = "active";
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
failure(signal, stage, value) {
|
|
756
|
+
const error = sanitizeError(toError(value));
|
|
757
|
+
const event = { signal, stage, error, timestamp: Date.now() };
|
|
758
|
+
this.currentError = event;
|
|
759
|
+
this.failedSignals.add(signal);
|
|
760
|
+
if (this.currentStatus !== "inactive" && this.currentStatus !== "stopped") {
|
|
761
|
+
this.currentStatus = "degraded";
|
|
762
|
+
}
|
|
763
|
+
const fingerprint = `${signal}\0${stage}\0${error.name}\0${error.message}`;
|
|
764
|
+
if (this.reported.has(fingerprint)) return;
|
|
765
|
+
this.reported.add(fingerprint);
|
|
766
|
+
try {
|
|
767
|
+
this.onError?.(event);
|
|
768
|
+
} catch {
|
|
769
|
+
}
|
|
770
|
+
if (!this.logging) return;
|
|
771
|
+
try {
|
|
772
|
+
process.stderr.write(
|
|
773
|
+
`[nest-observe] ${signal} ${stage} failed: ${redactText(error.message)}
|
|
774
|
+
`
|
|
775
|
+
);
|
|
776
|
+
} catch {
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
function withExporterDiagnostics(exporter, signal, diagnostics) {
|
|
781
|
+
const delegate = exporter;
|
|
782
|
+
return new Proxy(exporter, {
|
|
783
|
+
get(target, property) {
|
|
784
|
+
if (property === "export") {
|
|
785
|
+
return (items, callback) => {
|
|
786
|
+
try {
|
|
787
|
+
delegate.export.call(target, items, (result) => {
|
|
788
|
+
if (result.code === 0) {
|
|
789
|
+
diagnostics.success(signal);
|
|
790
|
+
} else {
|
|
791
|
+
diagnostics.failure(
|
|
792
|
+
signal,
|
|
793
|
+
"export",
|
|
794
|
+
result.error ?? new Error(`${signal} export failed`)
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
callback(result);
|
|
798
|
+
});
|
|
799
|
+
} catch (error) {
|
|
800
|
+
diagnostics.failure(signal, "export", error);
|
|
801
|
+
throw error;
|
|
802
|
+
}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const value = Reflect.get(target, property, target);
|
|
806
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
|
|
683
811
|
// src/exceptions/process-exception-capture.ts
|
|
684
812
|
var import_api5 = require("@opentelemetry/api");
|
|
685
813
|
function asError(value) {
|
|
@@ -727,21 +855,133 @@ var ProcessExceptionCapture = class {
|
|
|
727
855
|
};
|
|
728
856
|
|
|
729
857
|
// src/resource.ts
|
|
730
|
-
var import_node_os2 = require("os");
|
|
731
858
|
var import_resources = require("@opentelemetry/resources");
|
|
732
|
-
var
|
|
733
|
-
|
|
859
|
+
var import_node_os2 = require("os");
|
|
860
|
+
|
|
861
|
+
// package.json
|
|
862
|
+
var package_default = {
|
|
863
|
+
name: "@ryanzeng/nest-observe",
|
|
864
|
+
version: "0.1.2",
|
|
865
|
+
description: "Zero-config, vendor-neutral OpenTelemetry observability for NestJS",
|
|
866
|
+
keywords: [
|
|
867
|
+
"nestjs",
|
|
868
|
+
"opentelemetry",
|
|
869
|
+
"observability",
|
|
870
|
+
"otlp",
|
|
871
|
+
"tracing",
|
|
872
|
+
"metrics",
|
|
873
|
+
"logging"
|
|
874
|
+
],
|
|
875
|
+
license: "MIT",
|
|
876
|
+
homepage: "https://github.com/ryanzen9/nest-observe#readme",
|
|
877
|
+
bugs: {
|
|
878
|
+
url: "https://github.com/ryanzen9/nest-observe/issues"
|
|
879
|
+
},
|
|
880
|
+
repository: {
|
|
881
|
+
type: "git",
|
|
882
|
+
url: "git+https://github.com/ryanzen9/nest-observe.git"
|
|
883
|
+
},
|
|
884
|
+
packageManager: "pnpm@10.13.1",
|
|
885
|
+
sideEffects: [
|
|
886
|
+
"./dist/register.js",
|
|
887
|
+
"./dist/register.mjs"
|
|
888
|
+
],
|
|
889
|
+
main: "./dist/index.js",
|
|
890
|
+
module: "./dist/index.mjs",
|
|
891
|
+
types: "./dist/index.d.ts",
|
|
892
|
+
exports: {
|
|
893
|
+
".": {
|
|
894
|
+
import: {
|
|
895
|
+
types: "./dist/index.d.mts",
|
|
896
|
+
default: "./dist/index.mjs"
|
|
897
|
+
},
|
|
898
|
+
require: {
|
|
899
|
+
types: "./dist/index.d.ts",
|
|
900
|
+
default: "./dist/index.js"
|
|
901
|
+
}
|
|
902
|
+
},
|
|
903
|
+
"./register": {
|
|
904
|
+
import: {
|
|
905
|
+
types: "./dist/register.d.mts",
|
|
906
|
+
default: "./dist/register.mjs"
|
|
907
|
+
},
|
|
908
|
+
require: {
|
|
909
|
+
types: "./dist/register.d.ts",
|
|
910
|
+
default: "./dist/register.js"
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
},
|
|
914
|
+
files: [
|
|
915
|
+
"dist",
|
|
916
|
+
"README.md",
|
|
917
|
+
"CHANGELOG.md",
|
|
918
|
+
"LICENSE"
|
|
919
|
+
],
|
|
920
|
+
scripts: {
|
|
921
|
+
build: "tsup",
|
|
922
|
+
test: "vitest run",
|
|
923
|
+
"test:watch": "vitest",
|
|
924
|
+
typecheck: "tsc --noEmit",
|
|
925
|
+
check: "pnpm typecheck && pnpm test && pnpm build"
|
|
926
|
+
},
|
|
927
|
+
engines: {
|
|
928
|
+
node: ">=20"
|
|
929
|
+
},
|
|
930
|
+
peerDependencies: {
|
|
931
|
+
"@nestjs/common": ">=10 <13",
|
|
932
|
+
"@nestjs/core": ">=10 <13",
|
|
933
|
+
"reflect-metadata": ">=0.1.12 <1",
|
|
934
|
+
rxjs: "^7.1.0"
|
|
935
|
+
},
|
|
936
|
+
dependencies: {
|
|
937
|
+
"@opentelemetry/api": "^1.9.0",
|
|
938
|
+
"@opentelemetry/api-logs": "^0.221.0",
|
|
939
|
+
"@opentelemetry/exporter-logs-otlp-proto": "^0.221.0",
|
|
940
|
+
"@opentelemetry/exporter-metrics-otlp-proto": "^0.221.0",
|
|
941
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
|
|
942
|
+
"@opentelemetry/instrumentation": "^0.221.0",
|
|
943
|
+
"@opentelemetry/instrumentation-http": "^0.221.0",
|
|
944
|
+
"@opentelemetry/instrumentation-nestjs-core": "^0.67.0",
|
|
945
|
+
"@opentelemetry/resources": "^2.10.0",
|
|
946
|
+
"@opentelemetry/sdk-logs": "^0.221.0",
|
|
947
|
+
"@opentelemetry/sdk-metrics": "^2.10.0",
|
|
948
|
+
"@opentelemetry/sdk-trace-base": "^2.10.0",
|
|
949
|
+
"@opentelemetry/sdk-trace-node": "^2.10.0",
|
|
950
|
+
"@prisma/instrumentation": "^7.10.0"
|
|
951
|
+
},
|
|
952
|
+
devDependencies: {
|
|
953
|
+
"@nestjs/common": "^12.0.1",
|
|
954
|
+
"@nestjs/core": "^12.0.1",
|
|
955
|
+
"@nestjs/testing": "^12.0.1",
|
|
956
|
+
"@opentelemetry/sdk-trace-base": "^2.10.0",
|
|
957
|
+
"@types/node": "^24.0.0",
|
|
958
|
+
"reflect-metadata": "^0.2.2",
|
|
959
|
+
rxjs: "^7.8.2",
|
|
960
|
+
tsup: "^8.5.1",
|
|
961
|
+
typescript: "^5.9.3",
|
|
962
|
+
vitest: "^4.1.11"
|
|
963
|
+
},
|
|
964
|
+
publishConfig: {
|
|
965
|
+
access: "public"
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
// src/resource.ts
|
|
970
|
+
var SDK_NAME = package_default.name;
|
|
971
|
+
var SDK_VERSION = package_default.version;
|
|
734
972
|
function createObserveResource(config) {
|
|
735
|
-
return (0, import_resources.defaultResource)().merge(
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
973
|
+
return (0, import_resources.defaultResource)().merge(
|
|
974
|
+
(0, import_resources.resourceFromAttributes)({
|
|
975
|
+
...config.resourceAttributes,
|
|
976
|
+
"service.name": config.serviceName,
|
|
977
|
+
"service.version": config.serviceVersion,
|
|
978
|
+
"deployment.environment.name": config.environment,
|
|
979
|
+
"service.instance.id": config.instanceId,
|
|
980
|
+
"telemetry.sdk.name": SDK_NAME,
|
|
981
|
+
"telemetry.sdk.version": SDK_VERSION,
|
|
982
|
+
"host.name": config.resourceAttributes["host.name"] ?? (0, import_node_os2.hostname)()
|
|
983
|
+
})
|
|
984
|
+
);
|
|
745
985
|
}
|
|
746
986
|
|
|
747
987
|
// src/security/span-redaction-processor.ts
|
|
@@ -785,14 +1025,23 @@ var SpanRedactionProcessor = class {
|
|
|
785
1025
|
|
|
786
1026
|
// src/sdk.ts
|
|
787
1027
|
var InactiveObserveHandle = class {
|
|
788
|
-
constructor(config) {
|
|
1028
|
+
constructor(config, diagnostics) {
|
|
789
1029
|
this.config = config;
|
|
1030
|
+
this.diagnostics = diagnostics;
|
|
790
1031
|
}
|
|
791
1032
|
config;
|
|
1033
|
+
diagnostics;
|
|
792
1034
|
started = false;
|
|
793
1035
|
tracerProvider = void 0;
|
|
794
1036
|
meterProvider = void 0;
|
|
795
1037
|
loggerProvider = void 0;
|
|
1038
|
+
httpRequestMetrics = void 0;
|
|
1039
|
+
get status() {
|
|
1040
|
+
return this.diagnostics.status;
|
|
1041
|
+
}
|
|
1042
|
+
get lastError() {
|
|
1043
|
+
return this.diagnostics.lastError;
|
|
1044
|
+
}
|
|
796
1045
|
forceFlush() {
|
|
797
1046
|
return Promise.resolve();
|
|
798
1047
|
}
|
|
@@ -801,52 +1050,73 @@ var InactiveObserveHandle = class {
|
|
|
801
1050
|
}
|
|
802
1051
|
};
|
|
803
1052
|
var ActiveObserveHandle = class {
|
|
804
|
-
constructor(config, tracerProvider, meterProvider, loggerProvider, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations) {
|
|
1053
|
+
constructor(config, tracerProvider, meterProvider, loggerProvider, httpRequestMetrics, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations, diagnostics) {
|
|
805
1054
|
this.config = config;
|
|
806
1055
|
this.tracerProvider = tracerProvider;
|
|
807
1056
|
this.meterProvider = meterProvider;
|
|
808
1057
|
this.loggerProvider = loggerProvider;
|
|
1058
|
+
this.httpRequestMetrics = httpRequestMetrics;
|
|
809
1059
|
this.runtimeMetrics = runtimeMetrics;
|
|
810
1060
|
this.loggerInstrumentation = loggerInstrumentation;
|
|
811
1061
|
this.exceptionCapture = exceptionCapture;
|
|
812
1062
|
this.instrumentations = instrumentations;
|
|
1063
|
+
this.diagnostics = diagnostics;
|
|
813
1064
|
}
|
|
814
1065
|
config;
|
|
815
1066
|
tracerProvider;
|
|
816
1067
|
meterProvider;
|
|
817
1068
|
loggerProvider;
|
|
1069
|
+
httpRequestMetrics;
|
|
818
1070
|
runtimeMetrics;
|
|
819
1071
|
loggerInstrumentation;
|
|
820
1072
|
exceptionCapture;
|
|
821
1073
|
instrumentations;
|
|
1074
|
+
diagnostics;
|
|
822
1075
|
started = true;
|
|
823
1076
|
stopped = false;
|
|
1077
|
+
get status() {
|
|
1078
|
+
return this.diagnostics.status;
|
|
1079
|
+
}
|
|
1080
|
+
get lastError() {
|
|
1081
|
+
return this.diagnostics.lastError;
|
|
1082
|
+
}
|
|
824
1083
|
async forceFlush() {
|
|
825
|
-
await
|
|
826
|
-
this.tracerProvider
|
|
827
|
-
this.meterProvider
|
|
828
|
-
this.loggerProvider
|
|
829
|
-
]
|
|
1084
|
+
await this.runSafely("forceFlush", [
|
|
1085
|
+
...this.config.traces && this.tracerProvider ? [["traces", () => this.tracerProvider.forceFlush()]] : [],
|
|
1086
|
+
...this.config.metrics && this.meterProvider ? [["metrics", () => this.meterProvider.forceFlush()]] : [],
|
|
1087
|
+
...this.config.logs && this.loggerProvider ? [["logs", () => this.loggerProvider.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })]] : []
|
|
1088
|
+
]);
|
|
830
1089
|
}
|
|
831
1090
|
async shutdown() {
|
|
832
1091
|
if (this.stopped) return;
|
|
833
1092
|
this.stopped = true;
|
|
834
1093
|
this.loggerInstrumentation?.disable();
|
|
835
|
-
this.runtimeMetrics?.stop();
|
|
836
1094
|
this.exceptionCapture.stop();
|
|
837
1095
|
for (const instrumentation of this.instrumentations) {
|
|
838
1096
|
try {
|
|
839
1097
|
instrumentation.disable();
|
|
840
|
-
} catch {
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
this.diagnostics.failure("sdk", "shutdown", error);
|
|
841
1100
|
}
|
|
842
1101
|
}
|
|
843
1102
|
await this.forceFlush();
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
this.
|
|
847
|
-
this.
|
|
848
|
-
|
|
1103
|
+
this.runtimeMetrics?.stop();
|
|
1104
|
+
await this.runSafely("shutdown", [
|
|
1105
|
+
...this.tracerProvider ? [["traces", () => this.tracerProvider.shutdown()]] : [],
|
|
1106
|
+
...this.meterProvider ? [["metrics", () => this.meterProvider.shutdown()]] : [],
|
|
1107
|
+
...this.loggerProvider ? [["logs", () => this.loggerProvider.shutdown()]] : []
|
|
1108
|
+
]);
|
|
849
1109
|
if (activeRuntime === this) activeRuntime = void 0;
|
|
1110
|
+
this.diagnostics.stop();
|
|
1111
|
+
}
|
|
1112
|
+
async runSafely(stage, operations) {
|
|
1113
|
+
await Promise.all(operations.map(async ([signal, operation]) => {
|
|
1114
|
+
try {
|
|
1115
|
+
await operation();
|
|
1116
|
+
} catch (error) {
|
|
1117
|
+
this.diagnostics.failure(signal, stage, error);
|
|
1118
|
+
}
|
|
1119
|
+
}));
|
|
850
1120
|
}
|
|
851
1121
|
};
|
|
852
1122
|
var activeRuntime;
|
|
@@ -857,12 +1127,16 @@ function exporterConfig(url, config) {
|
|
|
857
1127
|
timeoutMillis: config.exportTimeoutMillis
|
|
858
1128
|
};
|
|
859
1129
|
}
|
|
860
|
-
function createTraceProvider(config, resource) {
|
|
1130
|
+
function createTraceProvider(config, resource, diagnostics) {
|
|
861
1131
|
if (!config.traces && !config.metrics) return void 0;
|
|
862
1132
|
const spanProcessors = [new SpanRedactionProcessor()];
|
|
863
1133
|
if (config.traces) {
|
|
864
1134
|
const exporter = config.exporters?.span ?? new import_exporter_trace_otlp_proto.OTLPTraceExporter(exporterConfig(config.endpoints.traces, config));
|
|
865
|
-
spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(
|
|
1135
|
+
spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(withExporterDiagnostics(
|
|
1136
|
+
exporter,
|
|
1137
|
+
"traces",
|
|
1138
|
+
diagnostics
|
|
1139
|
+
), {
|
|
866
1140
|
exportTimeoutMillis: config.exportTimeoutMillis,
|
|
867
1141
|
maxQueueSize: 2048,
|
|
868
1142
|
maxExportBatchSize: 512
|
|
@@ -877,14 +1151,18 @@ function createTraceProvider(config, resource) {
|
|
|
877
1151
|
provider.register();
|
|
878
1152
|
return provider;
|
|
879
1153
|
}
|
|
880
|
-
function createMeterProvider(config, resource) {
|
|
1154
|
+
function createMeterProvider(config, resource, diagnostics) {
|
|
881
1155
|
if (!config.metrics) return void 0;
|
|
882
1156
|
let reader;
|
|
883
1157
|
if (config.exporters?.metricReader) {
|
|
884
1158
|
reader = config.exporters.metricReader;
|
|
885
1159
|
} else {
|
|
886
1160
|
reader = new import_sdk_metrics.PeriodicExportingMetricReader({
|
|
887
|
-
exporter:
|
|
1161
|
+
exporter: withExporterDiagnostics(
|
|
1162
|
+
new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
|
|
1163
|
+
"metrics",
|
|
1164
|
+
diagnostics
|
|
1165
|
+
),
|
|
888
1166
|
exportIntervalMillis: config.metricExportIntervalMillis,
|
|
889
1167
|
exportTimeoutMillis: Math.min(config.exportTimeoutMillis, config.metricExportIntervalMillis),
|
|
890
1168
|
cardinalityLimits: { default: 2e3, histogram: 2e3 }
|
|
@@ -902,13 +1180,13 @@ function createMeterProvider(config, resource) {
|
|
|
902
1180
|
import_api6.metrics.setGlobalMeterProvider(provider);
|
|
903
1181
|
return provider;
|
|
904
1182
|
}
|
|
905
|
-
function createLoggerProvider(config, resource) {
|
|
1183
|
+
function createLoggerProvider(config, resource, diagnostics) {
|
|
906
1184
|
if (!config.logs) return void 0;
|
|
907
1185
|
const exporter = config.exporters?.log ?? new import_exporter_logs_otlp_proto.OTLPLogExporter(exporterConfig(config.endpoints.logs, config));
|
|
908
1186
|
const provider = new import_sdk_logs.LoggerProvider({
|
|
909
1187
|
resource,
|
|
910
1188
|
processors: [new import_sdk_logs.BatchLogRecordProcessor({
|
|
911
|
-
exporter,
|
|
1189
|
+
exporter: withExporterDiagnostics(exporter, "logs", diagnostics),
|
|
912
1190
|
exportTimeoutMillis: config.exportTimeoutMillis,
|
|
913
1191
|
maxQueueSize: 2048,
|
|
914
1192
|
maxExportBatchSize: 512
|
|
@@ -920,7 +1198,11 @@ function createLoggerProvider(config, resource) {
|
|
|
920
1198
|
function observe(options = {}) {
|
|
921
1199
|
if (activeRuntime?.started) return activeRuntime;
|
|
922
1200
|
const config = resolveObserveConfig(options);
|
|
923
|
-
|
|
1201
|
+
const diagnostics = new ObserveDiagnostics(config.diagnosticLogging, options.onError);
|
|
1202
|
+
if (!config.enabled) {
|
|
1203
|
+
diagnostics.inactive();
|
|
1204
|
+
return new InactiveObserveHandle(config, diagnostics);
|
|
1205
|
+
}
|
|
924
1206
|
let tracerProvider;
|
|
925
1207
|
let meterProvider;
|
|
926
1208
|
let loggerProvider;
|
|
@@ -930,10 +1212,11 @@ function observe(options = {}) {
|
|
|
930
1212
|
const instrumentations = [];
|
|
931
1213
|
try {
|
|
932
1214
|
const resource = createObserveResource(config);
|
|
933
|
-
meterProvider = createMeterProvider(config, resource);
|
|
1215
|
+
meterProvider = createMeterProvider(config, resource, diagnostics);
|
|
934
1216
|
const meter = meterProvider?.getMeter(SDK_NAME, SDK_VERSION);
|
|
935
|
-
|
|
936
|
-
|
|
1217
|
+
const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
|
|
1218
|
+
tracerProvider = createTraceProvider(config, resource, diagnostics);
|
|
1219
|
+
loggerProvider = createLoggerProvider(config, resource, diagnostics);
|
|
937
1220
|
runtimeMetrics = meter ? new RuntimeMetrics(meter) : void 0;
|
|
938
1221
|
runtimeMetrics?.start();
|
|
939
1222
|
const otelLogger = loggerProvider?.getLogger(SDK_NAME, SDK_VERSION);
|
|
@@ -948,7 +1231,6 @@ function observe(options = {}) {
|
|
|
948
1231
|
exceptionCapture.start();
|
|
949
1232
|
if (config.traces || config.metrics) {
|
|
950
1233
|
const safeHeaders = config.allowedHeaders.filter((header) => !isSensitiveKey(header));
|
|
951
|
-
const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
|
|
952
1234
|
instrumentations.push(new import_instrumentation_http.HttpInstrumentation({
|
|
953
1235
|
requireParentforOutgoingSpans: false,
|
|
954
1236
|
headersToSpanAttributes: {
|
|
@@ -992,14 +1274,18 @@ function observe(options = {}) {
|
|
|
992
1274
|
tracerProvider,
|
|
993
1275
|
meterProvider,
|
|
994
1276
|
loggerProvider,
|
|
1277
|
+
httpRequestMetrics,
|
|
995
1278
|
runtimeMetrics,
|
|
996
1279
|
loggerInstrumentation,
|
|
997
1280
|
exceptionCapture,
|
|
998
|
-
instrumentations
|
|
1281
|
+
instrumentations,
|
|
1282
|
+
diagnostics
|
|
999
1283
|
);
|
|
1284
|
+
diagnostics.activate();
|
|
1000
1285
|
activeRuntime = runtime;
|
|
1001
1286
|
return runtime;
|
|
1002
|
-
} catch {
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
diagnostics.failure("sdk", "initialization", error);
|
|
1003
1289
|
loggerInstrumentation?.disable();
|
|
1004
1290
|
runtimeMetrics?.stop();
|
|
1005
1291
|
exceptionCapture?.stop();
|
|
@@ -1014,7 +1300,9 @@ function observe(options = {}) {
|
|
|
1014
1300
|
meterProvider?.shutdown(),
|
|
1015
1301
|
loggerProvider?.shutdown()
|
|
1016
1302
|
].filter((item) => Boolean(item)));
|
|
1017
|
-
|
|
1303
|
+
diagnostics.inactive();
|
|
1304
|
+
if (config.failFast) throw toError(error);
|
|
1305
|
+
return new InactiveObserveHandle(config, diagnostics);
|
|
1018
1306
|
}
|
|
1019
1307
|
}
|
|
1020
1308
|
function getObserveRuntime() {
|
|
@@ -1024,6 +1312,37 @@ function getObserveRuntime() {
|
|
|
1024
1312
|
// src/nest/observe.module.ts
|
|
1025
1313
|
var OBSERVE_OPTIONS = /* @__PURE__ */ Symbol("OBSERVE_OPTIONS");
|
|
1026
1314
|
var OBSERVE_HANDLE = /* @__PURE__ */ Symbol("OBSERVE_HANDLE");
|
|
1315
|
+
var NestHttpMetricsInterceptor = class {
|
|
1316
|
+
constructor(handle) {
|
|
1317
|
+
this.handle = handle;
|
|
1318
|
+
}
|
|
1319
|
+
handle;
|
|
1320
|
+
intercept(context3, next) {
|
|
1321
|
+
if (context3.getType() !== "http" || !this.handle.started || !this.handle.httpRequestMetrics) {
|
|
1322
|
+
return next.handle();
|
|
1323
|
+
}
|
|
1324
|
+
const http = context3.switchToHttp();
|
|
1325
|
+
const request = http.getRequest();
|
|
1326
|
+
const response = http.getResponse();
|
|
1327
|
+
const started = this.handle.httpRequestMetrics.start(request);
|
|
1328
|
+
const result = next.handle();
|
|
1329
|
+
if (!started) return result;
|
|
1330
|
+
const responseEventSource = response.raw ?? response;
|
|
1331
|
+
if (typeof responseEventSource.once === "function") {
|
|
1332
|
+
responseEventSource.once("finish", () => {
|
|
1333
|
+
this.handle.httpRequestMetrics?.record(request, response);
|
|
1334
|
+
});
|
|
1335
|
+
return result;
|
|
1336
|
+
}
|
|
1337
|
+
return result.pipe((0, import_rxjs2.finalize)(() => {
|
|
1338
|
+
this.handle.httpRequestMetrics?.record(request, response);
|
|
1339
|
+
}));
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
NestHttpMetricsInterceptor = __decorateClass([
|
|
1343
|
+
(0, import_common2.Injectable)(),
|
|
1344
|
+
__decorateParam(0, (0, import_common2.Inject)(OBSERVE_HANDLE))
|
|
1345
|
+
], NestHttpMetricsInterceptor);
|
|
1027
1346
|
var NestObserveExplorer = class {
|
|
1028
1347
|
constructor(discovery, options, handle) {
|
|
1029
1348
|
this.discovery = discovery;
|
|
@@ -1077,6 +1396,7 @@ var ObserveModule = class {
|
|
|
1077
1396
|
providers: [
|
|
1078
1397
|
{ provide: OBSERVE_OPTIONS, useValue: options },
|
|
1079
1398
|
{ provide: OBSERVE_HANDLE, useFactory: () => observe(options) },
|
|
1399
|
+
{ provide: import_core.APP_INTERCEPTOR, useClass: NestHttpMetricsInterceptor },
|
|
1080
1400
|
NestObserveExplorer
|
|
1081
1401
|
],
|
|
1082
1402
|
exports: [OBSERVE_HANDLE]
|
|
@@ -1092,6 +1412,7 @@ ObserveModule = __decorateClass([
|
|
|
1092
1412
|
CompatibleNestInstrumentation,
|
|
1093
1413
|
HttpRequestMetrics,
|
|
1094
1414
|
IgnoreTrace,
|
|
1415
|
+
NestHttpMetricsInterceptor,
|
|
1095
1416
|
NestLoggerInstrumentation,
|
|
1096
1417
|
NestMethodInstrumenter,
|
|
1097
1418
|
OBSERVE_HANDLE,
|