@ryanzeng/nest-observe 0.1.1 → 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/dist/index.mjs CHANGED
@@ -89,7 +89,9 @@ function resolveObserveConfig(options = {}, env = process.env) {
89
89
  allowedHeaders,
90
90
  exportTimeoutMillis: positiveInteger(options.exportTimeoutMillis ?? env.OTEL_EXPORTER_OTLP_TIMEOUT, 1e4),
91
91
  metricExportIntervalMillis: positiveInteger(options.metricExportIntervalMillis ?? env.OTEL_METRIC_EXPORT_INTERVAL, 6e4),
92
- resourceAttributes
92
+ resourceAttributes,
93
+ diagnosticLogging: booleanValue(options.diagnosticLogging, true),
94
+ failFast: booleanValue(options.failFast, false)
93
95
  };
94
96
  if (options.exporters) result.exporters = options.exporters;
95
97
  return result;
@@ -285,7 +287,13 @@ var SEVERITY = {
285
287
  };
286
288
  function bodyValue(message) {
287
289
  if (message instanceof Error) return redactText(message.stack ?? `${message.name}: ${message.message}`);
288
- return redact(message);
290
+ const sanitized = redact(message);
291
+ if (sanitized === null || typeof sanitized !== "object") return sanitized;
292
+ try {
293
+ return JSON.stringify(sanitized, (_key, value) => typeof value === "bigint" ? value.toString() : value);
294
+ } catch {
295
+ return redactText(String(sanitized));
296
+ }
289
297
  }
290
298
  var NestLoggerInstrumentation = class {
291
299
  constructor(emitter, resourceAttributes = {}) {
@@ -522,9 +530,14 @@ var HttpRequestMetrics = class {
522
530
  errorCount;
523
531
  startedAt = /* @__PURE__ */ new WeakMap();
524
532
  start(request) {
533
+ if (this.startedAt.has(request)) return false;
525
534
  this.startedAt.set(request, process.hrtime.bigint());
535
+ return true;
526
536
  }
527
537
  record(request, response) {
538
+ const started = this.startedAt.get(request);
539
+ if (started === void 0) return false;
540
+ this.startedAt.delete(request);
528
541
  const statusCode = Number.isFinite(response.statusCode) ? response.statusCode : 0;
529
542
  const attributes = {
530
543
  "service.name": this.serviceName,
@@ -533,12 +546,9 @@ var HttpRequestMetrics = class {
533
546
  "http.response.status_code": statusCode
534
547
  };
535
548
  this.requestCount.add(1, attributes);
536
- const started = this.startedAt.get(request);
537
- if (started !== void 0) {
538
- this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
539
- this.startedAt.delete(request);
540
- }
549
+ this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
541
550
  if (statusCode >= 500) this.errorCount.add(1, attributes);
551
+ return true;
542
552
  }
543
553
  };
544
554
 
@@ -624,9 +634,15 @@ var RuntimeMetrics = class {
624
634
  };
625
635
 
626
636
  // src/nest/observe.module.ts
627
- import { Global, Inject, Injectable, Module } from "@nestjs/common";
628
- import { DiscoveryModule, DiscoveryService } from "@nestjs/core";
637
+ import {
638
+ Global,
639
+ Inject,
640
+ Injectable,
641
+ Module
642
+ } from "@nestjs/common";
643
+ import { APP_INTERCEPTOR, DiscoveryModule, DiscoveryService } from "@nestjs/core";
629
644
  import { metrics as metrics2, trace as trace5 } from "@opentelemetry/api";
645
+ import { finalize as finalize2 } from "rxjs";
630
646
 
631
647
  // src/sdk.ts
632
648
  import { logs as logs2 } from "@opentelemetry/api-logs";
@@ -642,6 +658,113 @@ import { AggregationType, MeterProvider, PeriodicExportingMetricReader } from "@
642
658
  import { AlwaysOffSampler, BatchSpanProcessor, ParentBasedSampler, TraceIdRatioBasedSampler } from "@opentelemetry/sdk-trace-base";
643
659
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
644
660
 
661
+ // src/diagnostics.ts
662
+ function toError(value) {
663
+ if (value instanceof Error) return value;
664
+ if (typeof value === "string") return new Error(value);
665
+ try {
666
+ return new Error(JSON.stringify(value));
667
+ } catch {
668
+ return new Error(String(value));
669
+ }
670
+ }
671
+ function sanitizeError(error) {
672
+ const sanitized = new Error(redactText(error.message));
673
+ sanitized.name = error.name;
674
+ if (error.stack) sanitized.stack = redactText(error.stack);
675
+ return sanitized;
676
+ }
677
+ var ObserveDiagnostics = class {
678
+ constructor(logging, onError) {
679
+ this.logging = logging;
680
+ this.onError = onError;
681
+ }
682
+ logging;
683
+ onError;
684
+ currentStatus = "starting";
685
+ currentError;
686
+ failedSignals = /* @__PURE__ */ new Set();
687
+ reported = /* @__PURE__ */ new Set();
688
+ get status() {
689
+ return this.currentStatus;
690
+ }
691
+ get lastError() {
692
+ return this.currentError;
693
+ }
694
+ activate() {
695
+ if (this.currentStatus === "starting") this.currentStatus = "active";
696
+ }
697
+ inactive() {
698
+ this.currentStatus = "inactive";
699
+ }
700
+ stop() {
701
+ this.currentStatus = "stopped";
702
+ }
703
+ success(signal) {
704
+ this.failedSignals.delete(signal);
705
+ for (const fingerprint of this.reported) {
706
+ if (fingerprint.startsWith(`${signal}\0`)) this.reported.delete(fingerprint);
707
+ }
708
+ if (this.currentStatus === "degraded" && this.failedSignals.size === 0) {
709
+ this.currentStatus = "active";
710
+ }
711
+ }
712
+ failure(signal, stage, value) {
713
+ const error = sanitizeError(toError(value));
714
+ const event = { signal, stage, error, timestamp: Date.now() };
715
+ this.currentError = event;
716
+ this.failedSignals.add(signal);
717
+ if (this.currentStatus !== "inactive" && this.currentStatus !== "stopped") {
718
+ this.currentStatus = "degraded";
719
+ }
720
+ const fingerprint = `${signal}\0${stage}\0${error.name}\0${error.message}`;
721
+ if (this.reported.has(fingerprint)) return;
722
+ this.reported.add(fingerprint);
723
+ try {
724
+ this.onError?.(event);
725
+ } catch {
726
+ }
727
+ if (!this.logging) return;
728
+ try {
729
+ process.stderr.write(
730
+ `[nest-observe] ${signal} ${stage} failed: ${redactText(error.message)}
731
+ `
732
+ );
733
+ } catch {
734
+ }
735
+ }
736
+ };
737
+ function withExporterDiagnostics(exporter, signal, diagnostics) {
738
+ const delegate = exporter;
739
+ return new Proxy(exporter, {
740
+ get(target, property) {
741
+ if (property === "export") {
742
+ return (items, callback) => {
743
+ try {
744
+ delegate.export.call(target, items, (result) => {
745
+ if (result.code === 0) {
746
+ diagnostics.success(signal);
747
+ } else {
748
+ diagnostics.failure(
749
+ signal,
750
+ "export",
751
+ result.error ?? new Error(`${signal} export failed`)
752
+ );
753
+ }
754
+ callback(result);
755
+ });
756
+ } catch (error) {
757
+ diagnostics.failure(signal, "export", error);
758
+ throw error;
759
+ }
760
+ };
761
+ }
762
+ const value = Reflect.get(target, property, target);
763
+ return typeof value === "function" ? value.bind(target) : value;
764
+ }
765
+ });
766
+ }
767
+
645
768
  // src/exceptions/process-exception-capture.ts
646
769
  import { trace as trace3, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
647
770
  function asError(value) {
@@ -694,8 +817,118 @@ import {
694
817
  resourceFromAttributes
695
818
  } from "@opentelemetry/resources";
696
819
  import { hostname } from "os";
697
- var SDK_NAME = "@ryanzeng/nest-observe";
698
- var SDK_VERSION = "0.1.0";
820
+
821
+ // package.json
822
+ var package_default = {
823
+ name: "@ryanzeng/nest-observe",
824
+ version: "0.1.2",
825
+ description: "Zero-config, vendor-neutral OpenTelemetry observability for NestJS",
826
+ keywords: [
827
+ "nestjs",
828
+ "opentelemetry",
829
+ "observability",
830
+ "otlp",
831
+ "tracing",
832
+ "metrics",
833
+ "logging"
834
+ ],
835
+ license: "MIT",
836
+ homepage: "https://github.com/ryanzen9/nest-observe#readme",
837
+ bugs: {
838
+ url: "https://github.com/ryanzen9/nest-observe/issues"
839
+ },
840
+ repository: {
841
+ type: "git",
842
+ url: "git+https://github.com/ryanzen9/nest-observe.git"
843
+ },
844
+ packageManager: "pnpm@10.13.1",
845
+ sideEffects: [
846
+ "./dist/register.js",
847
+ "./dist/register.mjs"
848
+ ],
849
+ main: "./dist/index.js",
850
+ module: "./dist/index.mjs",
851
+ types: "./dist/index.d.ts",
852
+ exports: {
853
+ ".": {
854
+ import: {
855
+ types: "./dist/index.d.mts",
856
+ default: "./dist/index.mjs"
857
+ },
858
+ require: {
859
+ types: "./dist/index.d.ts",
860
+ default: "./dist/index.js"
861
+ }
862
+ },
863
+ "./register": {
864
+ import: {
865
+ types: "./dist/register.d.mts",
866
+ default: "./dist/register.mjs"
867
+ },
868
+ require: {
869
+ types: "./dist/register.d.ts",
870
+ default: "./dist/register.js"
871
+ }
872
+ }
873
+ },
874
+ files: [
875
+ "dist",
876
+ "README.md",
877
+ "CHANGELOG.md",
878
+ "LICENSE"
879
+ ],
880
+ scripts: {
881
+ build: "tsup",
882
+ test: "vitest run",
883
+ "test:watch": "vitest",
884
+ typecheck: "tsc --noEmit",
885
+ check: "pnpm typecheck && pnpm test && pnpm build"
886
+ },
887
+ engines: {
888
+ node: ">=20"
889
+ },
890
+ peerDependencies: {
891
+ "@nestjs/common": ">=10 <13",
892
+ "@nestjs/core": ">=10 <13",
893
+ "reflect-metadata": ">=0.1.12 <1",
894
+ rxjs: "^7.1.0"
895
+ },
896
+ dependencies: {
897
+ "@opentelemetry/api": "^1.9.0",
898
+ "@opentelemetry/api-logs": "^0.221.0",
899
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.221.0",
900
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.221.0",
901
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
902
+ "@opentelemetry/instrumentation": "^0.221.0",
903
+ "@opentelemetry/instrumentation-http": "^0.221.0",
904
+ "@opentelemetry/instrumentation-nestjs-core": "^0.67.0",
905
+ "@opentelemetry/resources": "^2.10.0",
906
+ "@opentelemetry/sdk-logs": "^0.221.0",
907
+ "@opentelemetry/sdk-metrics": "^2.10.0",
908
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
909
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
910
+ "@prisma/instrumentation": "^7.10.0"
911
+ },
912
+ devDependencies: {
913
+ "@nestjs/common": "^12.0.1",
914
+ "@nestjs/core": "^12.0.1",
915
+ "@nestjs/testing": "^12.0.1",
916
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
917
+ "@types/node": "^24.0.0",
918
+ "reflect-metadata": "^0.2.2",
919
+ rxjs: "^7.8.2",
920
+ tsup: "^8.5.1",
921
+ typescript: "^5.9.3",
922
+ vitest: "^4.1.11"
923
+ },
924
+ publishConfig: {
925
+ access: "public"
926
+ }
927
+ };
928
+
929
+ // src/resource.ts
930
+ var SDK_NAME = package_default.name;
931
+ var SDK_VERSION = package_default.version;
699
932
  function createObserveResource(config) {
700
933
  return defaultResource().merge(
701
934
  resourceFromAttributes({
@@ -752,14 +985,23 @@ var SpanRedactionProcessor = class {
752
985
 
753
986
  // src/sdk.ts
754
987
  var InactiveObserveHandle = class {
755
- constructor(config) {
988
+ constructor(config, diagnostics) {
756
989
  this.config = config;
990
+ this.diagnostics = diagnostics;
757
991
  }
758
992
  config;
993
+ diagnostics;
759
994
  started = false;
760
995
  tracerProvider = void 0;
761
996
  meterProvider = void 0;
762
997
  loggerProvider = void 0;
998
+ httpRequestMetrics = void 0;
999
+ get status() {
1000
+ return this.diagnostics.status;
1001
+ }
1002
+ get lastError() {
1003
+ return this.diagnostics.lastError;
1004
+ }
763
1005
  forceFlush() {
764
1006
  return Promise.resolve();
765
1007
  }
@@ -768,52 +1010,73 @@ var InactiveObserveHandle = class {
768
1010
  }
769
1011
  };
770
1012
  var ActiveObserveHandle = class {
771
- constructor(config, tracerProvider, meterProvider, loggerProvider, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations) {
1013
+ constructor(config, tracerProvider, meterProvider, loggerProvider, httpRequestMetrics, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations, diagnostics) {
772
1014
  this.config = config;
773
1015
  this.tracerProvider = tracerProvider;
774
1016
  this.meterProvider = meterProvider;
775
1017
  this.loggerProvider = loggerProvider;
1018
+ this.httpRequestMetrics = httpRequestMetrics;
776
1019
  this.runtimeMetrics = runtimeMetrics;
777
1020
  this.loggerInstrumentation = loggerInstrumentation;
778
1021
  this.exceptionCapture = exceptionCapture;
779
1022
  this.instrumentations = instrumentations;
1023
+ this.diagnostics = diagnostics;
780
1024
  }
781
1025
  config;
782
1026
  tracerProvider;
783
1027
  meterProvider;
784
1028
  loggerProvider;
1029
+ httpRequestMetrics;
785
1030
  runtimeMetrics;
786
1031
  loggerInstrumentation;
787
1032
  exceptionCapture;
788
1033
  instrumentations;
1034
+ diagnostics;
789
1035
  started = true;
790
1036
  stopped = false;
1037
+ get status() {
1038
+ return this.diagnostics.status;
1039
+ }
1040
+ get lastError() {
1041
+ return this.diagnostics.lastError;
1042
+ }
791
1043
  async forceFlush() {
792
- await Promise.allSettled([
793
- this.tracerProvider?.forceFlush(),
794
- this.meterProvider?.forceFlush(),
795
- this.loggerProvider?.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })
796
- ].filter((item) => Boolean(item)));
1044
+ await this.runSafely("forceFlush", [
1045
+ ...this.config.traces && this.tracerProvider ? [["traces", () => this.tracerProvider.forceFlush()]] : [],
1046
+ ...this.config.metrics && this.meterProvider ? [["metrics", () => this.meterProvider.forceFlush()]] : [],
1047
+ ...this.config.logs && this.loggerProvider ? [["logs", () => this.loggerProvider.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })]] : []
1048
+ ]);
797
1049
  }
798
1050
  async shutdown() {
799
1051
  if (this.stopped) return;
800
1052
  this.stopped = true;
801
1053
  this.loggerInstrumentation?.disable();
802
- this.runtimeMetrics?.stop();
803
1054
  this.exceptionCapture.stop();
804
1055
  for (const instrumentation of this.instrumentations) {
805
1056
  try {
806
1057
  instrumentation.disable();
807
- } catch {
1058
+ } catch (error) {
1059
+ this.diagnostics.failure("sdk", "shutdown", error);
808
1060
  }
809
1061
  }
810
1062
  await this.forceFlush();
811
- await Promise.allSettled([
812
- this.tracerProvider?.shutdown(),
813
- this.meterProvider?.shutdown(),
814
- this.loggerProvider?.shutdown()
815
- ].filter((item) => Boolean(item)));
1063
+ this.runtimeMetrics?.stop();
1064
+ await this.runSafely("shutdown", [
1065
+ ...this.tracerProvider ? [["traces", () => this.tracerProvider.shutdown()]] : [],
1066
+ ...this.meterProvider ? [["metrics", () => this.meterProvider.shutdown()]] : [],
1067
+ ...this.loggerProvider ? [["logs", () => this.loggerProvider.shutdown()]] : []
1068
+ ]);
816
1069
  if (activeRuntime === this) activeRuntime = void 0;
1070
+ this.diagnostics.stop();
1071
+ }
1072
+ async runSafely(stage, operations) {
1073
+ await Promise.all(operations.map(async ([signal, operation]) => {
1074
+ try {
1075
+ await operation();
1076
+ } catch (error) {
1077
+ this.diagnostics.failure(signal, stage, error);
1078
+ }
1079
+ }));
817
1080
  }
818
1081
  };
819
1082
  var activeRuntime;
@@ -824,12 +1087,16 @@ function exporterConfig(url, config) {
824
1087
  timeoutMillis: config.exportTimeoutMillis
825
1088
  };
826
1089
  }
827
- function createTraceProvider(config, resource) {
1090
+ function createTraceProvider(config, resource, diagnostics) {
828
1091
  if (!config.traces && !config.metrics) return void 0;
829
1092
  const spanProcessors = [new SpanRedactionProcessor()];
830
1093
  if (config.traces) {
831
1094
  const exporter = config.exporters?.span ?? new OTLPTraceExporter(exporterConfig(config.endpoints.traces, config));
832
- spanProcessors.push(new BatchSpanProcessor(exporter, {
1095
+ spanProcessors.push(new BatchSpanProcessor(withExporterDiagnostics(
1096
+ exporter,
1097
+ "traces",
1098
+ diagnostics
1099
+ ), {
833
1100
  exportTimeoutMillis: config.exportTimeoutMillis,
834
1101
  maxQueueSize: 2048,
835
1102
  maxExportBatchSize: 512
@@ -844,14 +1111,18 @@ function createTraceProvider(config, resource) {
844
1111
  provider.register();
845
1112
  return provider;
846
1113
  }
847
- function createMeterProvider(config, resource) {
1114
+ function createMeterProvider(config, resource, diagnostics) {
848
1115
  if (!config.metrics) return void 0;
849
1116
  let reader;
850
1117
  if (config.exporters?.metricReader) {
851
1118
  reader = config.exporters.metricReader;
852
1119
  } else {
853
1120
  reader = new PeriodicExportingMetricReader({
854
- exporter: new OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1121
+ exporter: withExporterDiagnostics(
1122
+ new OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1123
+ "metrics",
1124
+ diagnostics
1125
+ ),
855
1126
  exportIntervalMillis: config.metricExportIntervalMillis,
856
1127
  exportTimeoutMillis: Math.min(config.exportTimeoutMillis, config.metricExportIntervalMillis),
857
1128
  cardinalityLimits: { default: 2e3, histogram: 2e3 }
@@ -869,13 +1140,13 @@ function createMeterProvider(config, resource) {
869
1140
  metrics.setGlobalMeterProvider(provider);
870
1141
  return provider;
871
1142
  }
872
- function createLoggerProvider(config, resource) {
1143
+ function createLoggerProvider(config, resource, diagnostics) {
873
1144
  if (!config.logs) return void 0;
874
1145
  const exporter = config.exporters?.log ?? new OTLPLogExporter(exporterConfig(config.endpoints.logs, config));
875
1146
  const provider = new LoggerProvider({
876
1147
  resource,
877
1148
  processors: [new BatchLogRecordProcessor({
878
- exporter,
1149
+ exporter: withExporterDiagnostics(exporter, "logs", diagnostics),
879
1150
  exportTimeoutMillis: config.exportTimeoutMillis,
880
1151
  maxQueueSize: 2048,
881
1152
  maxExportBatchSize: 512
@@ -887,7 +1158,11 @@ function createLoggerProvider(config, resource) {
887
1158
  function observe(options = {}) {
888
1159
  if (activeRuntime?.started) return activeRuntime;
889
1160
  const config = resolveObserveConfig(options);
890
- if (!config.enabled) return new InactiveObserveHandle(config);
1161
+ const diagnostics = new ObserveDiagnostics(config.diagnosticLogging, options.onError);
1162
+ if (!config.enabled) {
1163
+ diagnostics.inactive();
1164
+ return new InactiveObserveHandle(config, diagnostics);
1165
+ }
891
1166
  let tracerProvider;
892
1167
  let meterProvider;
893
1168
  let loggerProvider;
@@ -897,10 +1172,11 @@ function observe(options = {}) {
897
1172
  const instrumentations = [];
898
1173
  try {
899
1174
  const resource = createObserveResource(config);
900
- meterProvider = createMeterProvider(config, resource);
1175
+ meterProvider = createMeterProvider(config, resource, diagnostics);
901
1176
  const meter = meterProvider?.getMeter(SDK_NAME, SDK_VERSION);
902
- tracerProvider = createTraceProvider(config, resource);
903
- loggerProvider = createLoggerProvider(config, resource);
1177
+ const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
1178
+ tracerProvider = createTraceProvider(config, resource, diagnostics);
1179
+ loggerProvider = createLoggerProvider(config, resource, diagnostics);
904
1180
  runtimeMetrics = meter ? new RuntimeMetrics(meter) : void 0;
905
1181
  runtimeMetrics?.start();
906
1182
  const otelLogger = loggerProvider?.getLogger(SDK_NAME, SDK_VERSION);
@@ -915,7 +1191,6 @@ function observe(options = {}) {
915
1191
  exceptionCapture.start();
916
1192
  if (config.traces || config.metrics) {
917
1193
  const safeHeaders = config.allowedHeaders.filter((header) => !isSensitiveKey(header));
918
- const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
919
1194
  instrumentations.push(new HttpInstrumentation({
920
1195
  requireParentforOutgoingSpans: false,
921
1196
  headersToSpanAttributes: {
@@ -959,14 +1234,18 @@ function observe(options = {}) {
959
1234
  tracerProvider,
960
1235
  meterProvider,
961
1236
  loggerProvider,
1237
+ httpRequestMetrics,
962
1238
  runtimeMetrics,
963
1239
  loggerInstrumentation,
964
1240
  exceptionCapture,
965
- instrumentations
1241
+ instrumentations,
1242
+ diagnostics
966
1243
  );
1244
+ diagnostics.activate();
967
1245
  activeRuntime = runtime;
968
1246
  return runtime;
969
- } catch {
1247
+ } catch (error) {
1248
+ diagnostics.failure("sdk", "initialization", error);
970
1249
  loggerInstrumentation?.disable();
971
1250
  runtimeMetrics?.stop();
972
1251
  exceptionCapture?.stop();
@@ -981,7 +1260,9 @@ function observe(options = {}) {
981
1260
  meterProvider?.shutdown(),
982
1261
  loggerProvider?.shutdown()
983
1262
  ].filter((item) => Boolean(item)));
984
- return new InactiveObserveHandle(config);
1263
+ diagnostics.inactive();
1264
+ if (config.failFast) throw toError(error);
1265
+ return new InactiveObserveHandle(config, diagnostics);
985
1266
  }
986
1267
  }
987
1268
  function getObserveRuntime() {
@@ -991,6 +1272,37 @@ function getObserveRuntime() {
991
1272
  // src/nest/observe.module.ts
992
1273
  var OBSERVE_OPTIONS = /* @__PURE__ */ Symbol("OBSERVE_OPTIONS");
993
1274
  var OBSERVE_HANDLE = /* @__PURE__ */ Symbol("OBSERVE_HANDLE");
1275
+ var NestHttpMetricsInterceptor = class {
1276
+ constructor(handle) {
1277
+ this.handle = handle;
1278
+ }
1279
+ handle;
1280
+ intercept(context3, next) {
1281
+ if (context3.getType() !== "http" || !this.handle.started || !this.handle.httpRequestMetrics) {
1282
+ return next.handle();
1283
+ }
1284
+ const http = context3.switchToHttp();
1285
+ const request = http.getRequest();
1286
+ const response = http.getResponse();
1287
+ const started = this.handle.httpRequestMetrics.start(request);
1288
+ const result = next.handle();
1289
+ if (!started) return result;
1290
+ const responseEventSource = response.raw ?? response;
1291
+ if (typeof responseEventSource.once === "function") {
1292
+ responseEventSource.once("finish", () => {
1293
+ this.handle.httpRequestMetrics?.record(request, response);
1294
+ });
1295
+ return result;
1296
+ }
1297
+ return result.pipe(finalize2(() => {
1298
+ this.handle.httpRequestMetrics?.record(request, response);
1299
+ }));
1300
+ }
1301
+ };
1302
+ NestHttpMetricsInterceptor = __decorateClass([
1303
+ Injectable(),
1304
+ __decorateParam(0, Inject(OBSERVE_HANDLE))
1305
+ ], NestHttpMetricsInterceptor);
994
1306
  var NestObserveExplorer = class {
995
1307
  constructor(discovery, options, handle) {
996
1308
  this.discovery = discovery;
@@ -1044,6 +1356,7 @@ var ObserveModule = class {
1044
1356
  providers: [
1045
1357
  { provide: OBSERVE_OPTIONS, useValue: options },
1046
1358
  { provide: OBSERVE_HANDLE, useFactory: () => observe(options) },
1359
+ { provide: APP_INTERCEPTOR, useClass: NestHttpMetricsInterceptor },
1047
1360
  NestObserveExplorer
1048
1361
  ],
1049
1362
  exports: [OBSERVE_HANDLE]
@@ -1058,6 +1371,7 @@ export {
1058
1371
  CompatibleNestInstrumentation,
1059
1372
  HttpRequestMetrics,
1060
1373
  IgnoreTrace,
1374
+ NestHttpMetricsInterceptor,
1061
1375
  NestLoggerInstrumentation,
1062
1376
  NestMethodInstrumenter,
1063
1377
  OBSERVE_HANDLE,