@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/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
  }
@@ -386,7 +493,13 @@ var SEVERITY = {
386
493
  };
387
494
  function bodyValue(message) {
388
495
  if (message instanceof Error) return redactText(message.stack ?? `${message.name}: ${message.message}`);
389
- return redact(message);
496
+ const sanitized = redact(message);
497
+ if (sanitized === null || typeof sanitized !== "object") return sanitized;
498
+ try {
499
+ return JSON.stringify(sanitized, (_key, value) => typeof value === "bigint" ? value.toString() : value);
500
+ } catch {
501
+ return redactText(String(sanitized));
502
+ }
390
503
  }
391
504
  var NestLoggerInstrumentation = class {
392
505
  constructor(emitter, resourceAttributes = {}) {
@@ -449,7 +562,7 @@ var SEVERITY_NUMBER = {
449
562
  };
450
563
  var OpenTelemetryLogEmitter = class {
451
564
  logger;
452
- constructor(name = "@ryanzen9/nest-observe", version, logger) {
565
+ constructor(name = "@ryanzeng/nest-observe", version, logger) {
453
566
  this.logger = logger ?? import_api_logs.logs.getLogger(name, version);
454
567
  }
455
568
  emit(record) {
@@ -493,9 +606,14 @@ var HttpRequestMetrics = class {
493
606
  errorCount;
494
607
  startedAt = /* @__PURE__ */ new WeakMap();
495
608
  start(request) {
609
+ if (this.startedAt.has(request)) return false;
496
610
  this.startedAt.set(request, process.hrtime.bigint());
611
+ return true;
497
612
  }
498
613
  record(request, response) {
614
+ const started = this.startedAt.get(request);
615
+ if (started === void 0) return false;
616
+ this.startedAt.delete(request);
499
617
  const statusCode = Number.isFinite(response.statusCode) ? response.statusCode : 0;
500
618
  const attributes = {
501
619
  "service.name": this.serviceName,
@@ -504,12 +622,9 @@ var HttpRequestMetrics = class {
504
622
  "http.response.status_code": statusCode
505
623
  };
506
624
  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
- }
625
+ this.requestDuration.record(Number(process.hrtime.bigint() - started) / 1e9, attributes);
512
626
  if (statusCode >= 500) this.errorCount.add(1, attributes);
627
+ return true;
513
628
  }
514
629
  };
515
630
 
@@ -595,21 +710,133 @@ var RuntimeMetrics = class {
595
710
  };
596
711
 
597
712
  // src/resource.ts
598
- var import_node_os2 = require("os");
599
713
  var import_resources = require("@opentelemetry/resources");
600
- var SDK_NAME = "@ryanzen9/nest-observe";
601
- var SDK_VERSION = "0.1.0";
714
+ var import_node_os2 = require("os");
715
+
716
+ // package.json
717
+ var package_default = {
718
+ name: "@ryanzeng/nest-observe",
719
+ version: "0.1.2",
720
+ description: "Zero-config, vendor-neutral OpenTelemetry observability for NestJS",
721
+ keywords: [
722
+ "nestjs",
723
+ "opentelemetry",
724
+ "observability",
725
+ "otlp",
726
+ "tracing",
727
+ "metrics",
728
+ "logging"
729
+ ],
730
+ license: "MIT",
731
+ homepage: "https://github.com/ryanzen9/nest-observe#readme",
732
+ bugs: {
733
+ url: "https://github.com/ryanzen9/nest-observe/issues"
734
+ },
735
+ repository: {
736
+ type: "git",
737
+ url: "git+https://github.com/ryanzen9/nest-observe.git"
738
+ },
739
+ packageManager: "pnpm@10.13.1",
740
+ sideEffects: [
741
+ "./dist/register.js",
742
+ "./dist/register.mjs"
743
+ ],
744
+ main: "./dist/index.js",
745
+ module: "./dist/index.mjs",
746
+ types: "./dist/index.d.ts",
747
+ exports: {
748
+ ".": {
749
+ import: {
750
+ types: "./dist/index.d.mts",
751
+ default: "./dist/index.mjs"
752
+ },
753
+ require: {
754
+ types: "./dist/index.d.ts",
755
+ default: "./dist/index.js"
756
+ }
757
+ },
758
+ "./register": {
759
+ import: {
760
+ types: "./dist/register.d.mts",
761
+ default: "./dist/register.mjs"
762
+ },
763
+ require: {
764
+ types: "./dist/register.d.ts",
765
+ default: "./dist/register.js"
766
+ }
767
+ }
768
+ },
769
+ files: [
770
+ "dist",
771
+ "README.md",
772
+ "CHANGELOG.md",
773
+ "LICENSE"
774
+ ],
775
+ scripts: {
776
+ build: "tsup",
777
+ test: "vitest run",
778
+ "test:watch": "vitest",
779
+ typecheck: "tsc --noEmit",
780
+ check: "pnpm typecheck && pnpm test && pnpm build"
781
+ },
782
+ engines: {
783
+ node: ">=20"
784
+ },
785
+ peerDependencies: {
786
+ "@nestjs/common": ">=10 <13",
787
+ "@nestjs/core": ">=10 <13",
788
+ "reflect-metadata": ">=0.1.12 <1",
789
+ rxjs: "^7.1.0"
790
+ },
791
+ dependencies: {
792
+ "@opentelemetry/api": "^1.9.0",
793
+ "@opentelemetry/api-logs": "^0.221.0",
794
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.221.0",
795
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.221.0",
796
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0",
797
+ "@opentelemetry/instrumentation": "^0.221.0",
798
+ "@opentelemetry/instrumentation-http": "^0.221.0",
799
+ "@opentelemetry/instrumentation-nestjs-core": "^0.67.0",
800
+ "@opentelemetry/resources": "^2.10.0",
801
+ "@opentelemetry/sdk-logs": "^0.221.0",
802
+ "@opentelemetry/sdk-metrics": "^2.10.0",
803
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
804
+ "@opentelemetry/sdk-trace-node": "^2.10.0",
805
+ "@prisma/instrumentation": "^7.10.0"
806
+ },
807
+ devDependencies: {
808
+ "@nestjs/common": "^12.0.1",
809
+ "@nestjs/core": "^12.0.1",
810
+ "@nestjs/testing": "^12.0.1",
811
+ "@opentelemetry/sdk-trace-base": "^2.10.0",
812
+ "@types/node": "^24.0.0",
813
+ "reflect-metadata": "^0.2.2",
814
+ rxjs: "^7.8.2",
815
+ tsup: "^8.5.1",
816
+ typescript: "^5.9.3",
817
+ vitest: "^4.1.11"
818
+ },
819
+ publishConfig: {
820
+ access: "public"
821
+ }
822
+ };
823
+
824
+ // src/resource.ts
825
+ var SDK_NAME = package_default.name;
826
+ var SDK_VERSION = package_default.version;
602
827
  function createObserveResource(config) {
603
- return (0, import_resources.defaultResource)().merge((0, import_resources.resourceFromAttributes)({
604
- ...config.resourceAttributes,
605
- "service.name": config.serviceName,
606
- "service.version": config.serviceVersion,
607
- "deployment.environment.name": config.environment,
608
- "service.instance.id": config.instanceId,
609
- "telemetry.sdk.name": SDK_NAME,
610
- "telemetry.sdk.version": SDK_VERSION,
611
- "host.name": config.resourceAttributes["host.name"] ?? (0, import_node_os2.hostname)()
612
- }));
828
+ return (0, import_resources.defaultResource)().merge(
829
+ (0, import_resources.resourceFromAttributes)({
830
+ ...config.resourceAttributes,
831
+ "service.name": config.serviceName,
832
+ "service.version": config.serviceVersion,
833
+ "deployment.environment.name": config.environment,
834
+ "service.instance.id": config.instanceId,
835
+ "telemetry.sdk.name": SDK_NAME,
836
+ "telemetry.sdk.version": SDK_VERSION,
837
+ "host.name": config.resourceAttributes["host.name"] ?? (0, import_node_os2.hostname)()
838
+ })
839
+ );
613
840
  }
614
841
 
615
842
  // src/security/span-redaction-processor.ts
@@ -653,14 +880,23 @@ var SpanRedactionProcessor = class {
653
880
 
654
881
  // src/sdk.ts
655
882
  var InactiveObserveHandle = class {
656
- constructor(config) {
883
+ constructor(config, diagnostics) {
657
884
  this.config = config;
885
+ this.diagnostics = diagnostics;
658
886
  }
659
887
  config;
888
+ diagnostics;
660
889
  started = false;
661
890
  tracerProvider = void 0;
662
891
  meterProvider = void 0;
663
892
  loggerProvider = void 0;
893
+ httpRequestMetrics = void 0;
894
+ get status() {
895
+ return this.diagnostics.status;
896
+ }
897
+ get lastError() {
898
+ return this.diagnostics.lastError;
899
+ }
664
900
  forceFlush() {
665
901
  return Promise.resolve();
666
902
  }
@@ -669,52 +905,73 @@ var InactiveObserveHandle = class {
669
905
  }
670
906
  };
671
907
  var ActiveObserveHandle = class {
672
- constructor(config, tracerProvider, meterProvider, loggerProvider, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations) {
908
+ constructor(config, tracerProvider, meterProvider, loggerProvider, httpRequestMetrics, runtimeMetrics, loggerInstrumentation, exceptionCapture, instrumentations, diagnostics) {
673
909
  this.config = config;
674
910
  this.tracerProvider = tracerProvider;
675
911
  this.meterProvider = meterProvider;
676
912
  this.loggerProvider = loggerProvider;
913
+ this.httpRequestMetrics = httpRequestMetrics;
677
914
  this.runtimeMetrics = runtimeMetrics;
678
915
  this.loggerInstrumentation = loggerInstrumentation;
679
916
  this.exceptionCapture = exceptionCapture;
680
917
  this.instrumentations = instrumentations;
918
+ this.diagnostics = diagnostics;
681
919
  }
682
920
  config;
683
921
  tracerProvider;
684
922
  meterProvider;
685
923
  loggerProvider;
924
+ httpRequestMetrics;
686
925
  runtimeMetrics;
687
926
  loggerInstrumentation;
688
927
  exceptionCapture;
689
928
  instrumentations;
929
+ diagnostics;
690
930
  started = true;
691
931
  stopped = false;
932
+ get status() {
933
+ return this.diagnostics.status;
934
+ }
935
+ get lastError() {
936
+ return this.diagnostics.lastError;
937
+ }
692
938
  async forceFlush() {
693
- await Promise.allSettled([
694
- this.tracerProvider?.forceFlush(),
695
- this.meterProvider?.forceFlush(),
696
- this.loggerProvider?.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })
697
- ].filter((item) => Boolean(item)));
939
+ await this.runSafely("forceFlush", [
940
+ ...this.config.traces && this.tracerProvider ? [["traces", () => this.tracerProvider.forceFlush()]] : [],
941
+ ...this.config.metrics && this.meterProvider ? [["metrics", () => this.meterProvider.forceFlush()]] : [],
942
+ ...this.config.logs && this.loggerProvider ? [["logs", () => this.loggerProvider.forceFlush({ timeoutMillis: this.config.exportTimeoutMillis })]] : []
943
+ ]);
698
944
  }
699
945
  async shutdown() {
700
946
  if (this.stopped) return;
701
947
  this.stopped = true;
702
948
  this.loggerInstrumentation?.disable();
703
- this.runtimeMetrics?.stop();
704
949
  this.exceptionCapture.stop();
705
950
  for (const instrumentation of this.instrumentations) {
706
951
  try {
707
952
  instrumentation.disable();
708
- } catch {
953
+ } catch (error) {
954
+ this.diagnostics.failure("sdk", "shutdown", error);
709
955
  }
710
956
  }
711
957
  await this.forceFlush();
712
- await Promise.allSettled([
713
- this.tracerProvider?.shutdown(),
714
- this.meterProvider?.shutdown(),
715
- this.loggerProvider?.shutdown()
716
- ].filter((item) => Boolean(item)));
958
+ this.runtimeMetrics?.stop();
959
+ await this.runSafely("shutdown", [
960
+ ...this.tracerProvider ? [["traces", () => this.tracerProvider.shutdown()]] : [],
961
+ ...this.meterProvider ? [["metrics", () => this.meterProvider.shutdown()]] : [],
962
+ ...this.loggerProvider ? [["logs", () => this.loggerProvider.shutdown()]] : []
963
+ ]);
717
964
  if (activeRuntime === this) activeRuntime = void 0;
965
+ this.diagnostics.stop();
966
+ }
967
+ async runSafely(stage, operations) {
968
+ await Promise.all(operations.map(async ([signal, operation]) => {
969
+ try {
970
+ await operation();
971
+ } catch (error) {
972
+ this.diagnostics.failure(signal, stage, error);
973
+ }
974
+ }));
718
975
  }
719
976
  };
720
977
  var activeRuntime;
@@ -725,12 +982,16 @@ function exporterConfig(url, config) {
725
982
  timeoutMillis: config.exportTimeoutMillis
726
983
  };
727
984
  }
728
- function createTraceProvider(config, resource) {
985
+ function createTraceProvider(config, resource, diagnostics) {
729
986
  if (!config.traces && !config.metrics) return void 0;
730
987
  const spanProcessors = [new SpanRedactionProcessor()];
731
988
  if (config.traces) {
732
989
  const exporter = config.exporters?.span ?? new import_exporter_trace_otlp_proto.OTLPTraceExporter(exporterConfig(config.endpoints.traces, config));
733
- spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(exporter, {
990
+ spanProcessors.push(new import_sdk_trace_base.BatchSpanProcessor(withExporterDiagnostics(
991
+ exporter,
992
+ "traces",
993
+ diagnostics
994
+ ), {
734
995
  exportTimeoutMillis: config.exportTimeoutMillis,
735
996
  maxQueueSize: 2048,
736
997
  maxExportBatchSize: 512
@@ -745,14 +1006,18 @@ function createTraceProvider(config, resource) {
745
1006
  provider.register();
746
1007
  return provider;
747
1008
  }
748
- function createMeterProvider(config, resource) {
1009
+ function createMeterProvider(config, resource, diagnostics) {
749
1010
  if (!config.metrics) return void 0;
750
1011
  let reader;
751
1012
  if (config.exporters?.metricReader) {
752
1013
  reader = config.exporters.metricReader;
753
1014
  } else {
754
1015
  reader = new import_sdk_metrics.PeriodicExportingMetricReader({
755
- exporter: new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1016
+ exporter: withExporterDiagnostics(
1017
+ new import_exporter_metrics_otlp_proto.OTLPMetricExporter(exporterConfig(config.endpoints.metrics, config)),
1018
+ "metrics",
1019
+ diagnostics
1020
+ ),
756
1021
  exportIntervalMillis: config.metricExportIntervalMillis,
757
1022
  exportTimeoutMillis: Math.min(config.exportTimeoutMillis, config.metricExportIntervalMillis),
758
1023
  cardinalityLimits: { default: 2e3, histogram: 2e3 }
@@ -770,13 +1035,13 @@ function createMeterProvider(config, resource) {
770
1035
  import_api5.metrics.setGlobalMeterProvider(provider);
771
1036
  return provider;
772
1037
  }
773
- function createLoggerProvider(config, resource) {
1038
+ function createLoggerProvider(config, resource, diagnostics) {
774
1039
  if (!config.logs) return void 0;
775
1040
  const exporter = config.exporters?.log ?? new import_exporter_logs_otlp_proto.OTLPLogExporter(exporterConfig(config.endpoints.logs, config));
776
1041
  const provider = new import_sdk_logs.LoggerProvider({
777
1042
  resource,
778
1043
  processors: [new import_sdk_logs.BatchLogRecordProcessor({
779
- exporter,
1044
+ exporter: withExporterDiagnostics(exporter, "logs", diagnostics),
780
1045
  exportTimeoutMillis: config.exportTimeoutMillis,
781
1046
  maxQueueSize: 2048,
782
1047
  maxExportBatchSize: 512
@@ -788,7 +1053,11 @@ function createLoggerProvider(config, resource) {
788
1053
  function observe(options = {}) {
789
1054
  if (activeRuntime?.started) return activeRuntime;
790
1055
  const config = resolveObserveConfig(options);
791
- if (!config.enabled) return new InactiveObserveHandle(config);
1056
+ const diagnostics = new ObserveDiagnostics(config.diagnosticLogging, options.onError);
1057
+ if (!config.enabled) {
1058
+ diagnostics.inactive();
1059
+ return new InactiveObserveHandle(config, diagnostics);
1060
+ }
792
1061
  let tracerProvider;
793
1062
  let meterProvider;
794
1063
  let loggerProvider;
@@ -798,10 +1067,11 @@ function observe(options = {}) {
798
1067
  const instrumentations = [];
799
1068
  try {
800
1069
  const resource = createObserveResource(config);
801
- meterProvider = createMeterProvider(config, resource);
1070
+ meterProvider = createMeterProvider(config, resource, diagnostics);
802
1071
  const meter = meterProvider?.getMeter(SDK_NAME, SDK_VERSION);
803
- tracerProvider = createTraceProvider(config, resource);
804
- loggerProvider = createLoggerProvider(config, resource);
1072
+ const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
1073
+ tracerProvider = createTraceProvider(config, resource, diagnostics);
1074
+ loggerProvider = createLoggerProvider(config, resource, diagnostics);
805
1075
  runtimeMetrics = meter ? new RuntimeMetrics(meter) : void 0;
806
1076
  runtimeMetrics?.start();
807
1077
  const otelLogger = loggerProvider?.getLogger(SDK_NAME, SDK_VERSION);
@@ -816,7 +1086,6 @@ function observe(options = {}) {
816
1086
  exceptionCapture.start();
817
1087
  if (config.traces || config.metrics) {
818
1088
  const safeHeaders = config.allowedHeaders.filter((header) => !isSensitiveKey(header));
819
- const httpRequestMetrics = meter ? new HttpRequestMetrics(meter, config.serviceName) : void 0;
820
1089
  instrumentations.push(new import_instrumentation_http.HttpInstrumentation({
821
1090
  requireParentforOutgoingSpans: false,
822
1091
  headersToSpanAttributes: {
@@ -860,14 +1129,18 @@ function observe(options = {}) {
860
1129
  tracerProvider,
861
1130
  meterProvider,
862
1131
  loggerProvider,
1132
+ httpRequestMetrics,
863
1133
  runtimeMetrics,
864
1134
  loggerInstrumentation,
865
1135
  exceptionCapture,
866
- instrumentations
1136
+ instrumentations,
1137
+ diagnostics
867
1138
  );
1139
+ diagnostics.activate();
868
1140
  activeRuntime = runtime;
869
1141
  return runtime;
870
- } catch {
1142
+ } catch (error) {
1143
+ diagnostics.failure("sdk", "initialization", error);
871
1144
  loggerInstrumentation?.disable();
872
1145
  runtimeMetrics?.stop();
873
1146
  exceptionCapture?.stop();
@@ -882,7 +1155,9 @@ function observe(options = {}) {
882
1155
  meterProvider?.shutdown(),
883
1156
  loggerProvider?.shutdown()
884
1157
  ].filter((item) => Boolean(item)));
885
- return new InactiveObserveHandle(config);
1158
+ diagnostics.inactive();
1159
+ if (config.failFast) throw toError(error);
1160
+ return new InactiveObserveHandle(config, diagnostics);
886
1161
  }
887
1162
  }
888
1163