@ryanzeng/nest-observe 0.1.1 → 0.1.3

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/register.js CHANGED
@@ -93,15 +93,14 @@ function resolveObserveConfig(options = {}, env = process.env) {
93
93
  allowedHeaders,
94
94
  exportTimeoutMillis: positiveInteger(options.exportTimeoutMillis ?? env.OTEL_EXPORTER_OTLP_TIMEOUT, 1e4),
95
95
  metricExportIntervalMillis: positiveInteger(options.metricExportIntervalMillis ?? env.OTEL_METRIC_EXPORT_INTERVAL, 6e4),
96
- resourceAttributes
96
+ resourceAttributes,
97
+ diagnosticLogging: booleanValue(options.diagnosticLogging, true),
98
+ failFast: booleanValue(options.failFast, false)
97
99
  };
98
100
  if (options.exporters) result.exporters = options.exporters;
99
101
  return result;
100
102
  }
101
103
 
102
- // src/exceptions/process-exception-capture.ts
103
- var import_api = require("@opentelemetry/api");
104
-
105
104
  // src/security/redaction.ts
106
105
  var REDACTED = "[REDACTED]";
107
106
  var SENSITIVE_KEY = /(?:authorization|proxy-authorization|cookie|set-cookie|pass(?:word|wd)?|secret|token|api[-_]?key|phone|mobile)/i;
@@ -135,7 +134,115 @@ function redact(value) {
135
134
  return visit(value, /* @__PURE__ */ new WeakSet());
136
135
  }
137
136
 
137
+ // src/diagnostics.ts
138
+ function toError(value) {
139
+ if (value instanceof Error) return value;
140
+ if (typeof value === "string") return new Error(value);
141
+ try {
142
+ return new Error(JSON.stringify(value));
143
+ } catch {
144
+ return new Error(String(value));
145
+ }
146
+ }
147
+ function sanitizeError(error) {
148
+ const sanitized = new Error(redactText(error.message));
149
+ sanitized.name = error.name;
150
+ if (error.stack) sanitized.stack = redactText(error.stack);
151
+ return sanitized;
152
+ }
153
+ var ObserveDiagnostics = class {
154
+ constructor(logging, onError) {
155
+ this.logging = logging;
156
+ this.onError = onError;
157
+ }
158
+ logging;
159
+ onError;
160
+ currentStatus = "starting";
161
+ currentError;
162
+ failedSignals = /* @__PURE__ */ new Set();
163
+ reported = /* @__PURE__ */ new Set();
164
+ get status() {
165
+ return this.currentStatus;
166
+ }
167
+ get lastError() {
168
+ return this.currentError;
169
+ }
170
+ activate() {
171
+ if (this.currentStatus === "starting") this.currentStatus = "active";
172
+ }
173
+ inactive() {
174
+ this.currentStatus = "inactive";
175
+ }
176
+ stop() {
177
+ this.currentStatus = "stopped";
178
+ }
179
+ success(signal) {
180
+ this.failedSignals.delete(signal);
181
+ for (const fingerprint of this.reported) {
182
+ if (fingerprint.startsWith(`${signal}\0`)) this.reported.delete(fingerprint);
183
+ }
184
+ if (this.currentStatus === "degraded" && this.failedSignals.size === 0) {
185
+ this.currentStatus = "active";
186
+ }
187
+ }
188
+ failure(signal, stage, value) {
189
+ const error = sanitizeError(toError(value));
190
+ const event = { signal, stage, error, timestamp: Date.now() };
191
+ this.currentError = event;
192
+ this.failedSignals.add(signal);
193
+ if (this.currentStatus !== "inactive" && this.currentStatus !== "stopped") {
194
+ this.currentStatus = "degraded";
195
+ }
196
+ const fingerprint = `${signal}\0${stage}\0${error.name}\0${error.message}`;
197
+ if (this.reported.has(fingerprint)) return;
198
+ this.reported.add(fingerprint);
199
+ try {
200
+ this.onError?.(event);
201
+ } catch {
202
+ }
203
+ if (!this.logging) return;
204
+ try {
205
+ process.stderr.write(
206
+ `[nest-observe] ${signal} ${stage} failed: ${redactText(error.message)}
207
+ `
208
+ );
209
+ } catch {
210
+ }
211
+ }
212
+ };
213
+ function withExporterDiagnostics(exporter, signal, diagnostics) {
214
+ const delegate = exporter;
215
+ return new Proxy(exporter, {
216
+ get(target, property) {
217
+ if (property === "export") {
218
+ return (items, callback) => {
219
+ try {
220
+ delegate.export.call(target, items, (result) => {
221
+ if (result.code === 0) {
222
+ diagnostics.success(signal);
223
+ } else {
224
+ diagnostics.failure(
225
+ signal,
226
+ "export",
227
+ result.error ?? new Error(`${signal} export failed`)
228
+ );
229
+ }
230
+ callback(result);
231
+ });
232
+ } catch (error) {
233
+ diagnostics.failure(signal, "export", error);
234
+ throw error;
235
+ }
236
+ };
237
+ }
238
+ const value = Reflect.get(target, property, target);
239
+ return typeof value === "function" ? value.bind(target) : value;
240
+ }
241
+ });
242
+ }
243
+
138
244
  // src/exceptions/process-exception-capture.ts
245
+ var import_api = require("@opentelemetry/api");
139
246
  function asError(value) {
140
247
  return value instanceof Error ? value : new Error(String(value));
141
248
  }
@@ -288,7 +395,7 @@ var NestMethodInstrumenter = class {
288
395
  if (!descriptor || typeof original !== "function" || isTraceIgnored(original, type) || isTraceDecorated(original)) continue;
289
396
  const existing = Reflect.get(target, methodName);
290
397
  if (typeof existing === "function" && existing !== original && isTraceDecorated(existing)) continue;
291
- const name = componentName || type.name || "Anonymous";
398
+ const name = typeof componentName === "symbol" ? String(componentName) : typeof componentName === "string" && componentName ? componentName : type.name || "Anonymous";
292
399
  const attributes = {
293
400
  [`nestjs.${kind}`]: name,
294
401
  "nestjs.method": methodName
@@ -341,7 +448,7 @@ var CompatibleNestInstrumentation = class extends import_instrumentation_nestjs_
341
448
  const instance = await original.apply(this, args);
342
449
  try {
343
450
  const wrapper = args[1];
344
- if (!instance || typeof instance !== "object" || !wrapper?.metatype) return instance;
451
+ if (!instance || typeof instance !== "object" || !wrapper?.metatype || wrapper.inject !== void 0) return instance;
345
452
  if (["InternalCoreModule", "ObserveModule", "DiscoveryModule"].includes(wrapper.host?.name ?? "")) {
346
453
  return instance;
347
454
  }
@@ -385,7 +492,8 @@ var SEVERITY = {
385
492
  fatal: "FATAL"
386
493
  };
387
494
  function bodyValue(message) {
388
- if (message instanceof Error) return redactText(message.stack ?? `${message.name}: ${message.message}`);
495
+ if (message instanceof Error)
496
+ return redactText(message.stack ?? `${message.name}: ${message.message}`);
389
497
  return redact(message);
390
498
  }
391
499
  var NestLoggerInstrumentation = class {
@@ -430,7 +538,8 @@ var NestLoggerInstrumentation = class {
430
538
  disable() {
431
539
  if (!this.enabled) return;
432
540
  const prototype = import_common.ConsoleLogger.prototype;
433
- for (const [method, original] of this.originals) prototype[method] = original;
541
+ for (const [method, original] of this.originals)
542
+ prototype[method] = original;
434
543
  this.originals.clear();
435
544
  this.enabled = false;
436
545
  }
@@ -493,9 +602,14 @@ var HttpRequestMetrics = class {
493
602
  errorCount;
494
603
  startedAt = /* @__PURE__ */ new WeakMap();
495
604
  start(request) {
605
+ if (this.startedAt.has(request)) return false;
496
606
  this.startedAt.set(request, process.hrtime.bigint());
607
+ return true;
497
608
  }
498
609
  record(request, response) {
610
+ const started = this.startedAt.get(request);
611
+ if (started === void 0) return false;
612
+ this.startedAt.delete(request);
499
613
  const statusCode = Number.isFinite(response.statusCode) ? response.statusCode : 0;
500
614
  const attributes = {
501
615
  "service.name": this.serviceName,
@@ -504,12 +618,9 @@ var HttpRequestMetrics = class {
504
618
  "http.response.status_code": statusCode
505
619
  };
506
620
  this.requestCount.add(1, attributes);
507
- const started = this.startedAt.get(request);
508
- if (started !== void 0) {
509
- this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
510
- this.startedAt.delete(request);
511
- }
621
+ this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
512
622
  if (statusCode >= 500) this.errorCount.add(1, attributes);
623
+ return true;
513
624
  }
514
625
  };
515
626
 
@@ -597,8 +708,118 @@ var RuntimeMetrics = class {
597
708
  // src/resource.ts
598
709
  var import_resources = require("@opentelemetry/resources");
599
710
  var import_node_os2 = require("os");
600
- var SDK_NAME = "@ryanzeng/nest-observe";
601
- var SDK_VERSION = "0.1.0";
711
+
712
+ // package.json
713
+ var package_default = {
714
+ name: "@ryanzeng/nest-observe",
715
+ version: "0.1.3",
716
+ description: "Zero-config, vendor-neutral OpenTelemetry observability for NestJS",
717
+ keywords: [
718
+ "nestjs",
719
+ "opentelemetry",
720
+ "observability",
721
+ "otlp",
722
+ "tracing",
723
+ "metrics",
724
+ "logging"
725
+ ],
726
+ license: "MIT",
727
+ homepage: "https://github.com/ryanzen9/nest-observe#readme",
728
+ bugs: {
729
+ url: "https://github.com/ryanzen9/nest-observe/issues"
730
+ },
731
+ repository: {
732
+ type: "git",
733
+ url: "git+https://github.com/ryanzen9/nest-observe.git"
734
+ },
735
+ packageManager: "pnpm@10.13.1",
736
+ sideEffects: [
737
+ "./dist/register.js",
738
+ "./dist/register.mjs"
739
+ ],
740
+ main: "./dist/index.js",
741
+ module: "./dist/index.mjs",
742
+ types: "./dist/index.d.ts",
743
+ exports: {
744
+ ".": {
745
+ import: {
746
+ types: "./dist/index.d.mts",
747
+ default: "./dist/index.mjs"
748
+ },
749
+ require: {
750
+ types: "./dist/index.d.ts",
751
+ default: "./dist/index.js"
752
+ }
753
+ },
754
+ "./register": {
755
+ import: {
756
+ types: "./dist/register.d.mts",
757
+ default: "./dist/register.mjs"
758
+ },
759
+ require: {
760
+ types: "./dist/register.d.ts",
761
+ default: "./dist/register.js"
762
+ }
763
+ }
764
+ },
765
+ files: [
766
+ "dist",
767
+ "README.md",
768
+ "CHANGELOG.md",
769
+ "LICENSE"
770
+ ],
771
+ scripts: {
772
+ build: "tsup",
773
+ test: "vitest run",
774
+ "test:watch": "vitest",
775
+ typecheck: "tsc --noEmit",
776
+ check: "pnpm typecheck && pnpm test && pnpm build"
777
+ },
778
+ engines: {
779
+ node: ">=20"
780
+ },
781
+ peerDependencies: {
782
+ "@nestjs/common": ">=10 <13",
783
+ "@nestjs/core": ">=10 <13",
784
+ "reflect-metadata": ">=0.1.12 <1",
785
+ rxjs: "^7.1.0"
786
+ },
787
+ dependencies: {
788
+ "@opentelemetry/api": "^1.9.0",
789
+ "@opentelemetry/api-logs": "^0.221.0",
790
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.221.0",
791
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.221.0",
792
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
793
+ "@opentelemetry/instrumentation": "^0.221.0",
794
+ "@opentelemetry/instrumentation-http": "^0.221.0",
795
+ "@opentelemetry/instrumentation-nestjs-core": "^0.67.0",
796
+ "@opentelemetry/resources": "^2.10.0",
797
+ "@opentelemetry/sdk-logs": "^0.221.0",
798
+ "@opentelemetry/sdk-metrics": "^2.10.0",
799
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
800
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
801
+ "@prisma/instrumentation": "^7.10.0"
802
+ },
803
+ devDependencies: {
804
+ "@nestjs/common": "^12.0.1",
805
+ "@nestjs/core": "^12.0.1",
806
+ "@nestjs/testing": "^12.0.1",
807
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
808
+ "@types/node": "^24.0.0",
809
+ "reflect-metadata": "^0.2.2",
810
+ rxjs: "^7.8.2",
811
+ tsup: "^8.5.1",
812
+ typescript: "^5.9.3",
813
+ vitest: "^4.1.11"
814
+ },
815
+ publishConfig: {
816
+ access: "public"
817
+ }
818
+ };
819
+
820
+ // src/resource.ts
821
+ var SDK_NAME = package_default.name;
822
+ var SDK_VERSION = package_default.version;
602
823
  function createObserveResource(config) {
603
824
  return (0, import_resources.defaultResource)().merge(
604
825
  (0, import_resources.resourceFromAttributes)({
@@ -655,14 +876,23 @@ var SpanRedactionProcessor = class {
655
876
 
656
877
  // src/sdk.ts
657
878
  var InactiveObserveHandle = class {
658
- constructor(config) {
879
+ constructor(config, diagnostics) {
659
880
  this.config = config;
881
+ this.diagnostics = diagnostics;
660
882
  }
661
883
  config;
884
+ diagnostics;
662
885
  started = false;
663
886
  tracerProvider = void 0;
664
887
  meterProvider = void 0;
665
888
  loggerProvider = void 0;
889
+ httpRequestMetrics = void 0;
890
+ get status() {
891
+ return this.diagnostics.status;
892
+ }
893
+ get lastError() {
894
+ return this.diagnostics.lastError;
895
+ }
666
896
  forceFlush() {
667
897
  return Promise.resolve();
668
898
  }
@@ -671,52 +901,73 @@ var InactiveObserveHandle = class {
671
901
  }
672
902
  };
673
903
  var ActiveObserveHandle = class {
674
- constructor(config, tracerProvider, meterProvider, loggerProvider, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations) {
904
+ constructor(config, tracerProvider, meterProvider, loggerProvider, httpRequestMetrics, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations, diagnostics) {
675
905
  this.config = config;
676
906
  this.tracerProvider = tracerProvider;
677
907
  this.meterProvider = meterProvider;
678
908
  this.loggerProvider = loggerProvider;
909
+ this.httpRequestMetrics = httpRequestMetrics;
679
910
  this.runtimeMetrics = runtimeMetrics;
680
911
  this.loggerInstrumentation = loggerInstrumentation;
681
912
  this.exceptionCapture = exceptionCapture;
682
913
  this.instrumentations = instrumentations;
914
+ this.diagnostics = diagnostics;
683
915
  }
684
916
  config;
685
917
  tracerProvider;
686
918
  meterProvider;
687
919
  loggerProvider;
920
+ httpRequestMetrics;
688
921
  runtimeMetrics;
689
922
  loggerInstrumentation;
690
923
  exceptionCapture;
691
924
  instrumentations;
925
+ diagnostics;
692
926
  started = true;
693
927
  stopped = false;
928
+ get status() {
929
+ return this.diagnostics.status;
930
+ }
931
+ get lastError() {
932
+ return this.diagnostics.lastError;
933
+ }
694
934
  async forceFlush() {
695
- await Promise.allSettled([
696
- this.tracerProvider?.forceFlush(),
697
- this.meterProvider?.forceFlush(),
698
- this.loggerProvider?.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })
699
- ].filter((item) => Boolean(item)));
935
+ await this.runSafely("forceFlush", [
936
+ ...this.config.traces && this.tracerProvider ? [["traces", () => this.tracerProvider.forceFlush()]] : [],
937
+ ...this.config.metrics && this.meterProvider ? [["metrics", () => this.meterProvider.forceFlush()]] : [],
938
+ ...this.config.logs && this.loggerProvider ? [["logs", () => this.loggerProvider.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })]] : []
939
+ ]);
700
940
  }
701
941
  async shutdown() {
702
942
  if (this.stopped) return;
703
943
  this.stopped = true;
704
944
  this.loggerInstrumentation?.disable();
705
- this.runtimeMetrics?.stop();
706
945
  this.exceptionCapture.stop();
707
946
  for (const instrumentation of this.instrumentations) {
708
947
  try {
709
948
  instrumentation.disable();
710
- } catch {
949
+ } catch (error) {
950
+ this.diagnostics.failure("sdk", "shutdown", error);
711
951
  }
712
952
  }
713
953
  await this.forceFlush();
714
- await Promise.allSettled([
715
- this.tracerProvider?.shutdown(),
716
- this.meterProvider?.shutdown(),
717
- this.loggerProvider?.shutdown()
718
- ].filter((item) => Boolean(item)));
954
+ this.runtimeMetrics?.stop();
955
+ await this.runSafely("shutdown", [
956
+ ...this.tracerProvider ? [["traces", () => this.tracerProvider.shutdown()]] : [],
957
+ ...this.meterProvider ? [["metrics", () => this.meterProvider.shutdown()]] : [],
958
+ ...this.loggerProvider ? [["logs", () => this.loggerProvider.shutdown()]] : []
959
+ ]);
719
960
  if (activeRuntime === this) activeRuntime = void 0;
961
+ this.diagnostics.stop();
962
+ }
963
+ async runSafely(stage, operations) {
964
+ await Promise.all(operations.map(async ([signal, operation]) => {
965
+ try {
966
+ await operation();
967
+ } catch (error) {
968
+ this.diagnostics.failure(signal, stage, error);
969
+ }
970
+ }));
720
971
  }
721
972
  };
722
973
  var activeRuntime;
@@ -727,12 +978,16 @@ function exporterConfig(url, config) {
727
978
  timeoutMillis: config.exportTimeoutMillis
728
979
  };
729
980
  }
730
- function createTraceProvider(config, resource) {
981
+ function createTraceProvider(config, resource, diagnostics) {
731
982
  if (!config.traces && !config.metrics) return void 0;
732
983
  const spanProcessors = [new SpanRedactionProcessor()];
733
984
  if (config.traces) {
734
985
  const exporter = config.exporters?.span ?? new import_exporter_trace_otlp_proto.OTLPTraceExporter(exporterConfig(config.endpoints.traces, config));
735
- spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(exporter, {
986
+ spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(withExporterDiagnostics(
987
+ exporter,
988
+ "traces",
989
+ diagnostics
990
+ ), {
736
991
  exportTimeoutMillis: config.exportTimeoutMillis,
737
992
  maxQueueSize: 2048,
738
993
  maxExportBatchSize: 512
@@ -747,14 +1002,18 @@ function createTraceProvider(config, resource) {
747
1002
  provider.register();
748
1003
  return provider;
749
1004
  }
750
- function createMeterProvider(config, resource) {
1005
+ function createMeterProvider(config, resource, diagnostics) {
751
1006
  if (!config.metrics) return void 0;
752
1007
  let reader;
753
1008
  if (config.exporters?.metricReader) {
754
1009
  reader = config.exporters.metricReader;
755
1010
  } else {
756
1011
  reader = new import_sdk_metrics.PeriodicExportingMetricReader({
757
- exporter: new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1012
+ exporter: withExporterDiagnostics(
1013
+ new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1014
+ "metrics",
1015
+ diagnostics
1016
+ ),
758
1017
  exportIntervalMillis: config.metricExportIntervalMillis,
759
1018
  exportTimeoutMillis: Math.min(config.exportTimeoutMillis, config.metricExportIntervalMillis),
760
1019
  cardinalityLimits: { default: 2e3, histogram: 2e3 }
@@ -772,13 +1031,13 @@ function createMeterProvider(config, resource) {
772
1031
  import_api5.metrics.setGlobalMeterProvider(provider);
773
1032
  return provider;
774
1033
  }
775
- function createLoggerProvider(config, resource) {
1034
+ function createLoggerProvider(config, resource, diagnostics) {
776
1035
  if (!config.logs) return void 0;
777
1036
  const exporter = config.exporters?.log ?? new import_exporter_logs_otlp_proto.OTLPLogExporter(exporterConfig(config.endpoints.logs, config));
778
1037
  const provider = new import_sdk_logs.LoggerProvider({
779
1038
  resource,
780
1039
  processors: [new import_sdk_logs.BatchLogRecordProcessor({
781
- exporter,
1040
+ exporter: withExporterDiagnostics(exporter, "logs", diagnostics),
782
1041
  exportTimeoutMillis: config.exportTimeoutMillis,
783
1042
  maxQueueSize: 2048,
784
1043
  maxExportBatchSize: 512
@@ -790,7 +1049,11 @@ function createLoggerProvider(config, resource) {
790
1049
  function observe(options = {}) {
791
1050
  if (activeRuntime?.started) return activeRuntime;
792
1051
  const config = resolveObserveConfig(options);
793
- if (!config.enabled) return new InactiveObserveHandle(config);
1052
+ const diagnostics = new ObserveDiagnostics(config.diagnosticLogging, options.onError);
1053
+ if (!config.enabled) {
1054
+ diagnostics.inactive();
1055
+ return new InactiveObserveHandle(config, diagnostics);
1056
+ }
794
1057
  let tracerProvider;
795
1058
  let meterProvider;
796
1059
  let loggerProvider;
@@ -800,10 +1063,11 @@ function observe(options = {}) {
800
1063
  const instrumentations = [];
801
1064
  try {
802
1065
  const resource = createObserveResource(config);
803
- meterProvider = createMeterProvider(config, resource);
1066
+ meterProvider = createMeterProvider(config, resource, diagnostics);
804
1067
  const meter = meterProvider?.getMeter(SDK_NAME, SDK_VERSION);
805
- tracerProvider = createTraceProvider(config, resource);
806
- loggerProvider = createLoggerProvider(config, resource);
1068
+ const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
1069
+ tracerProvider = createTraceProvider(config, resource, diagnostics);
1070
+ loggerProvider = createLoggerProvider(config, resource, diagnostics);
807
1071
  runtimeMetrics = meter ? new RuntimeMetrics(meter) : void 0;
808
1072
  runtimeMetrics?.start();
809
1073
  const otelLogger = loggerProvider?.getLogger(SDK_NAME, SDK_VERSION);
@@ -818,7 +1082,6 @@ function observe(options = {}) {
818
1082
  exceptionCapture.start();
819
1083
  if (config.traces || config.metrics) {
820
1084
  const safeHeaders = config.allowedHeaders.filter((header) => !isSensitiveKey(header));
821
- const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
822
1085
  instrumentations.push(new import_instrumentation_http.HttpInstrumentation({
823
1086
  requireParentforOutgoingSpans: false,
824
1087
  headersToSpanAttributes: {
@@ -862,14 +1125,18 @@ function observe(options = {}) {
862
1125
  tracerProvider,
863
1126
  meterProvider,
864
1127
  loggerProvider,
1128
+ httpRequestMetrics,
865
1129
  runtimeMetrics,
866
1130
  loggerInstrumentation,
867
1131
  exceptionCapture,
868
- instrumentations
1132
+ instrumentations,
1133
+ diagnostics
869
1134
  );
1135
+ diagnostics.activate();
870
1136
  activeRuntime = runtime;
871
1137
  return runtime;
872
- } catch {
1138
+ } catch (error) {
1139
+ diagnostics.failure("sdk", "initialization", error);
873
1140
  loggerInstrumentation?.disable();
874
1141
  runtimeMetrics?.stop();
875
1142
  exceptionCapture?.stop();
@@ -884,7 +1151,9 @@ function observe(options = {}) {
884
1151
  meterProvider?.shutdown(),
885
1152
  loggerProvider?.shutdown()
886
1153
  ].filter((item) => Boolean(item)));
887
- return new InactiveObserveHandle(config);
1154
+ diagnostics.inactive();
1155
+ if (config.failFast) throw toError(error);
1156
+ return new InactiveObserveHandle(config, diagnostics);
888
1157
  }
889
1158
  }
890
1159