@getstrata/core 2.2.8 → 2.2.10
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/CHANGELOG.md +12 -0
- package/dist/core/database/acquisitionMetrics.d.ts +23 -0
- package/dist/core/database/baseRepository.d.ts +6 -0
- package/dist/core/database/postgresReadOnlyCollector.d.ts +7 -0
- package/dist/core/database/transaction.d.ts +11 -1
- package/dist/core/queue/failedJobMetrics.d.ts +17 -0
- package/dist/core/queue/queueMetrics.d.ts +4 -0
- package/dist/core/tracing/health.d.ts +27 -0
- package/dist/core/tracing/otel.d.ts +2 -0
- package/dist/core/tracing/tracingMiddleware.d.ts +2 -0
- package/dist/entries/audit/exportAuditLogs.js +80 -5
- package/dist/entries/auth/scimAuthMiddleware.js +80 -5
- package/dist/entries/http/response.js +3 -0
- package/dist/entries/jobs/exportAuditLogsJob.js +80 -5
- package/dist/entries/queue/queueMetrics.js +1018 -7
- package/dist/entries/tenant/databaseTenantContext.js +80 -5
- package/dist/entries/tenant/tenantDatabaseScope.js +80 -5
- package/dist/entries/tracing/tracingMiddleware.js +188 -5
- package/dist/framework/public-api.d.ts +7 -0
- package/dist/index.js +474 -98
- package/package.json +2 -1
|
@@ -129,6 +129,9 @@ function unwrapTransactionRollback(error, depth = 0) {
|
|
|
129
129
|
}
|
|
130
130
|
return unwrapTransactionRollback(error.cause, depth + 1);
|
|
131
131
|
}
|
|
132
|
+
function hasActiveTransaction() {
|
|
133
|
+
return hasActiveDatabaseConnection() && (transactionScopes.getStore()?.rollback.length ?? 0) > 0;
|
|
134
|
+
}
|
|
132
135
|
function requestTransactionRollback() {
|
|
133
136
|
const state = transactionScopes.getStore();
|
|
134
137
|
const index = state ? state.rollback.length - 1 : -1;
|
|
@@ -344,6 +347,55 @@ var init_deferredModelEvents = __esm(() => {
|
|
|
344
347
|
deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
|
|
345
348
|
});
|
|
346
349
|
|
|
350
|
+
// ../../src/core/database/acquisitionMetrics.ts
|
|
351
|
+
async function observeTransactionAcquisition(metrics, acquire, signal) {
|
|
352
|
+
const state = states.get(metrics);
|
|
353
|
+
if (!state)
|
|
354
|
+
throw new TypeError("Transaction acquisition metrics must be created by the framework factory.");
|
|
355
|
+
const started = performance.now();
|
|
356
|
+
state.inflight++;
|
|
357
|
+
let outcome = "acquired";
|
|
358
|
+
try {
|
|
359
|
+
return await acquire();
|
|
360
|
+
} catch (error) {
|
|
361
|
+
outcome = signal?.aborted ? "aborted" : "failed";
|
|
362
|
+
throw error;
|
|
363
|
+
} finally {
|
|
364
|
+
state.inflight--;
|
|
365
|
+
const elapsed = Math.max(0, performance.now() - started) / 1000;
|
|
366
|
+
const histogram = state.outcomes[outcomes.indexOf(outcome)];
|
|
367
|
+
if (histogram) {
|
|
368
|
+
histogram.count++;
|
|
369
|
+
histogram.sumSeconds += elapsed;
|
|
370
|
+
for (let index = 0;index < ACQUISITION_BUCKETS_SECONDS.length; index++) {
|
|
371
|
+
const bound = ACQUISITION_BUCKETS_SECONDS[index];
|
|
372
|
+
if (bound !== undefined && elapsed <= bound)
|
|
373
|
+
histogram.buckets[index] = (histogram.buckets[index] ?? 0) + 1;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
var ACQUISITION_BUCKETS_SECONDS, outcomes, metricsBrand, states;
|
|
379
|
+
var init_acquisitionMetrics = __esm(() => {
|
|
380
|
+
ACQUISITION_BUCKETS_SECONDS = [
|
|
381
|
+
0.001,
|
|
382
|
+
0.005,
|
|
383
|
+
0.01,
|
|
384
|
+
0.025,
|
|
385
|
+
0.05,
|
|
386
|
+
0.1,
|
|
387
|
+
0.25,
|
|
388
|
+
0.5,
|
|
389
|
+
1,
|
|
390
|
+
2.5,
|
|
391
|
+
5,
|
|
392
|
+
10
|
|
393
|
+
];
|
|
394
|
+
outcomes = ["acquired", "failed", "aborted"];
|
|
395
|
+
metricsBrand = Symbol("transaction acquisition metrics");
|
|
396
|
+
states = new WeakMap;
|
|
397
|
+
});
|
|
398
|
+
|
|
347
399
|
// ../../src/core/database/connection.ts
|
|
348
400
|
function createDatabaseConnection(source) {
|
|
349
401
|
return {
|
|
@@ -697,28 +749,51 @@ async function runInSavepoint(connection, operation) {
|
|
|
697
749
|
function supportsTransactions(connection) {
|
|
698
750
|
return typeof connection.begin === "function";
|
|
699
751
|
}
|
|
700
|
-
async function runInTransaction(operation) {
|
|
752
|
+
async function runInTransaction(operation, options = {}) {
|
|
753
|
+
const signal = options.acquisitionSignal;
|
|
754
|
+
signal?.throwIfAborted();
|
|
755
|
+
const admittedOperation = (connection) => {
|
|
756
|
+
signal?.throwIfAborted();
|
|
757
|
+
return operation(connection);
|
|
758
|
+
};
|
|
701
759
|
const pool = resolveRepositoryConnection();
|
|
702
760
|
if (hasActiveDatabaseConnection()) {
|
|
703
|
-
return await runInSavepoint(getActiveDatabaseConnection(pool),
|
|
761
|
+
return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
|
|
704
762
|
}
|
|
705
763
|
if (!supportsTransactions(pool)) {
|
|
706
764
|
throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
|
|
707
765
|
}
|
|
708
766
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
767
|
+
let reserved;
|
|
768
|
+
if (signal || options.acquisitionMetrics) {
|
|
769
|
+
if (!pool.reserve)
|
|
770
|
+
throw new Error(signal ? "Active database connection does not support cancellable transaction acquisition." : "Active database connection does not support observable transaction acquisition.");
|
|
771
|
+
const reserve = pool.reserve.bind(pool);
|
|
772
|
+
reserved = options.acquisitionMetrics ? await observeTransactionAcquisition(options.acquisitionMetrics, () => reserve({ signal }), signal) : await reserve({ signal });
|
|
773
|
+
}
|
|
774
|
+
const connection = reserved ?? pool;
|
|
775
|
+
try {
|
|
776
|
+
signal?.throwIfAborted();
|
|
777
|
+
if (!supportsTransactions(connection))
|
|
778
|
+
throw new Error("Reserved database connection does not support transactions.");
|
|
779
|
+
return await connection.begin(async (transaction) => {
|
|
780
|
+
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
781
|
+
});
|
|
782
|
+
} finally {
|
|
783
|
+
await reserved?.release();
|
|
784
|
+
}
|
|
712
785
|
}));
|
|
713
786
|
}
|
|
714
787
|
var transactionFrame, outerFrames;
|
|
715
788
|
var init_transaction = __esm(() => {
|
|
716
789
|
init_deferredModelEvents();
|
|
717
790
|
init_asyncContextStore();
|
|
791
|
+
init_acquisitionMetrics();
|
|
718
792
|
init_connection();
|
|
719
793
|
init_connectionContext();
|
|
720
794
|
init_repositoryConnection();
|
|
721
795
|
init_transactionControl();
|
|
796
|
+
init_acquisitionMetrics();
|
|
722
797
|
transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
|
|
723
798
|
outerFrames = new WeakMap;
|
|
724
799
|
});
|
|
@@ -52,6 +52,9 @@ function unwrapTransactionRollback(error, depth = 0) {
|
|
|
52
52
|
}
|
|
53
53
|
return unwrapTransactionRollback(error.cause, depth + 1);
|
|
54
54
|
}
|
|
55
|
+
function hasActiveTransaction() {
|
|
56
|
+
return hasActiveDatabaseConnection() && (transactionScopes.getStore()?.rollback.length ?? 0) > 0;
|
|
57
|
+
}
|
|
55
58
|
function requestTransactionRollback() {
|
|
56
59
|
const state = transactionScopes.getStore();
|
|
57
60
|
const index = state ? state.rollback.length - 1 : -1;
|
|
@@ -278,6 +281,55 @@ var init_deferredModelEvents = __esm(() => {
|
|
|
278
281
|
deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
|
|
279
282
|
});
|
|
280
283
|
|
|
284
|
+
// ../../src/core/database/acquisitionMetrics.ts
|
|
285
|
+
async function observeTransactionAcquisition(metrics, acquire, signal) {
|
|
286
|
+
const state = states.get(metrics);
|
|
287
|
+
if (!state)
|
|
288
|
+
throw new TypeError("Transaction acquisition metrics must be created by the framework factory.");
|
|
289
|
+
const started = performance.now();
|
|
290
|
+
state.inflight++;
|
|
291
|
+
let outcome = "acquired";
|
|
292
|
+
try {
|
|
293
|
+
return await acquire();
|
|
294
|
+
} catch (error) {
|
|
295
|
+
outcome = signal?.aborted ? "aborted" : "failed";
|
|
296
|
+
throw error;
|
|
297
|
+
} finally {
|
|
298
|
+
state.inflight--;
|
|
299
|
+
const elapsed = Math.max(0, performance.now() - started) / 1000;
|
|
300
|
+
const histogram = state.outcomes[outcomes.indexOf(outcome)];
|
|
301
|
+
if (histogram) {
|
|
302
|
+
histogram.count++;
|
|
303
|
+
histogram.sumSeconds += elapsed;
|
|
304
|
+
for (let index = 0;index < ACQUISITION_BUCKETS_SECONDS.length; index++) {
|
|
305
|
+
const bound = ACQUISITION_BUCKETS_SECONDS[index];
|
|
306
|
+
if (bound !== undefined && elapsed <= bound)
|
|
307
|
+
histogram.buckets[index] = (histogram.buckets[index] ?? 0) + 1;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
var ACQUISITION_BUCKETS_SECONDS, outcomes, metricsBrand, states;
|
|
313
|
+
var init_acquisitionMetrics = __esm(() => {
|
|
314
|
+
ACQUISITION_BUCKETS_SECONDS = [
|
|
315
|
+
0.001,
|
|
316
|
+
0.005,
|
|
317
|
+
0.01,
|
|
318
|
+
0.025,
|
|
319
|
+
0.05,
|
|
320
|
+
0.1,
|
|
321
|
+
0.25,
|
|
322
|
+
0.5,
|
|
323
|
+
1,
|
|
324
|
+
2.5,
|
|
325
|
+
5,
|
|
326
|
+
10
|
|
327
|
+
];
|
|
328
|
+
outcomes = ["acquired", "failed", "aborted"];
|
|
329
|
+
metricsBrand = Symbol("transaction acquisition metrics");
|
|
330
|
+
states = new WeakMap;
|
|
331
|
+
});
|
|
332
|
+
|
|
281
333
|
// ../../src/core/database/connection.ts
|
|
282
334
|
function createDatabaseConnection(source) {
|
|
283
335
|
return {
|
|
@@ -631,28 +683,51 @@ async function runInSavepoint(connection, operation) {
|
|
|
631
683
|
function supportsTransactions(connection) {
|
|
632
684
|
return typeof connection.begin === "function";
|
|
633
685
|
}
|
|
634
|
-
async function runInTransaction(operation) {
|
|
686
|
+
async function runInTransaction(operation, options = {}) {
|
|
687
|
+
const signal = options.acquisitionSignal;
|
|
688
|
+
signal?.throwIfAborted();
|
|
689
|
+
const admittedOperation = (connection) => {
|
|
690
|
+
signal?.throwIfAborted();
|
|
691
|
+
return operation(connection);
|
|
692
|
+
};
|
|
635
693
|
const pool = resolveRepositoryConnection();
|
|
636
694
|
if (hasActiveDatabaseConnection()) {
|
|
637
|
-
return await runInSavepoint(getActiveDatabaseConnection(pool),
|
|
695
|
+
return await runInSavepoint(getActiveDatabaseConnection(pool), admittedOperation);
|
|
638
696
|
}
|
|
639
697
|
if (!supportsTransactions(pool)) {
|
|
640
698
|
throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
|
|
641
699
|
}
|
|
642
700
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
701
|
+
let reserved;
|
|
702
|
+
if (signal || options.acquisitionMetrics) {
|
|
703
|
+
if (!pool.reserve)
|
|
704
|
+
throw new Error(signal ? "Active database connection does not support cancellable transaction acquisition." : "Active database connection does not support observable transaction acquisition.");
|
|
705
|
+
const reserve = pool.reserve.bind(pool);
|
|
706
|
+
reserved = options.acquisitionMetrics ? await observeTransactionAcquisition(options.acquisitionMetrics, () => reserve({ signal }), signal) : await reserve({ signal });
|
|
707
|
+
}
|
|
708
|
+
const connection = reserved ?? pool;
|
|
709
|
+
try {
|
|
710
|
+
signal?.throwIfAborted();
|
|
711
|
+
if (!supportsTransactions(connection))
|
|
712
|
+
throw new Error("Reserved database connection does not support transactions.");
|
|
713
|
+
return await connection.begin(async (transaction) => {
|
|
714
|
+
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
715
|
+
});
|
|
716
|
+
} finally {
|
|
717
|
+
await reserved?.release();
|
|
718
|
+
}
|
|
646
719
|
}));
|
|
647
720
|
}
|
|
648
721
|
var transactionFrame, outerFrames;
|
|
649
722
|
var init_transaction = __esm(() => {
|
|
650
723
|
init_deferredModelEvents();
|
|
651
724
|
init_asyncContextStore();
|
|
725
|
+
init_acquisitionMetrics();
|
|
652
726
|
init_connection();
|
|
653
727
|
init_connectionContext();
|
|
654
728
|
init_repositoryConnection();
|
|
655
729
|
init_transactionControl();
|
|
730
|
+
init_acquisitionMetrics();
|
|
656
731
|
transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
|
|
657
732
|
outerFrames = new WeakMap;
|
|
658
733
|
});
|
|
@@ -48,10 +48,10 @@ import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-ho
|
|
|
48
48
|
import { W3CTraceContextPropagator } from "@opentelemetry/core";
|
|
49
49
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
50
50
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
51
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace";
|
|
51
52
|
import {
|
|
52
53
|
AlwaysOffSampler,
|
|
53
54
|
AlwaysOnSampler,
|
|
54
|
-
BatchSpanProcessor,
|
|
55
55
|
ParentBasedSampler,
|
|
56
56
|
TraceIdRatioBasedSampler
|
|
57
57
|
} from "@opentelemetry/sdk-trace-base";
|
|
@@ -143,6 +143,172 @@ function sdkClientClassName() {
|
|
|
143
143
|
return fromName ? `${fromName}Client` : "AppClient";
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// ../../src/core/tracing/health.ts
|
|
147
|
+
import {
|
|
148
|
+
createNoopMeter
|
|
149
|
+
} from "@opentelemetry/api";
|
|
150
|
+
import { ExportResultCode } from "@opentelemetry/core";
|
|
151
|
+
function observeExportDeadline(state, timeoutMillis) {
|
|
152
|
+
const deadline = setTimeout(() => {
|
|
153
|
+
state.exportDeadlineExceededBatches++;
|
|
154
|
+
}, timeoutMillis);
|
|
155
|
+
deadline.unref();
|
|
156
|
+
return deadline;
|
|
157
|
+
}
|
|
158
|
+
function createTracingHealth(enabled, exporterConfigured, timeoutMillis) {
|
|
159
|
+
const state = {
|
|
160
|
+
enabled,
|
|
161
|
+
exporterConfigured,
|
|
162
|
+
active: true,
|
|
163
|
+
queueSize: null,
|
|
164
|
+
queueCapacity: null,
|
|
165
|
+
droppedSpans: 0,
|
|
166
|
+
exportedBatches: 0,
|
|
167
|
+
failedBatches: 0,
|
|
168
|
+
exportedSpans: 0,
|
|
169
|
+
failedSpans: 0,
|
|
170
|
+
exportDeadlineExceededBatches: 0,
|
|
171
|
+
flushFailures: 0,
|
|
172
|
+
shutdownFailures: 0
|
|
173
|
+
};
|
|
174
|
+
let exportQueueFullSpans = 0;
|
|
175
|
+
let queueCallback;
|
|
176
|
+
const noop = createNoopMeter();
|
|
177
|
+
const meter = {
|
|
178
|
+
createGauge: noop.createGauge.bind(noop),
|
|
179
|
+
createHistogram: noop.createHistogram.bind(noop),
|
|
180
|
+
createObservableGauge: noop.createObservableGauge.bind(noop),
|
|
181
|
+
createObservableCounter: noop.createObservableCounter.bind(noop),
|
|
182
|
+
addBatchObservableCallback: noop.addBatchObservableCallback.bind(noop),
|
|
183
|
+
removeBatchObservableCallback: noop.removeBatchObservableCallback.bind(noop),
|
|
184
|
+
createCounter(name, options) {
|
|
185
|
+
if (name !== "otel.sdk.processor.span.processed")
|
|
186
|
+
return noop.createCounter(name, options);
|
|
187
|
+
return {
|
|
188
|
+
add(value, attributes) {
|
|
189
|
+
if (attributes?.["error.type"] === "queue_full")
|
|
190
|
+
state.droppedSpans += value;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
createUpDownCounter(name, options) {
|
|
195
|
+
if (name !== "otel.sdk.processor.span.queue.capacity")
|
|
196
|
+
return noop.createUpDownCounter(name, options);
|
|
197
|
+
return {
|
|
198
|
+
add(value) {
|
|
199
|
+
state.queueCapacity = (state.queueCapacity ?? 0) + value;
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
createObservableUpDownCounter(name, options) {
|
|
204
|
+
if (name !== "otel.sdk.processor.span.queue.size")
|
|
205
|
+
return noop.createObservableUpDownCounter(name, options);
|
|
206
|
+
return {
|
|
207
|
+
addCallback(callback) {
|
|
208
|
+
queueCallback = callback;
|
|
209
|
+
},
|
|
210
|
+
removeCallback(callback) {
|
|
211
|
+
if (queueCallback === callback)
|
|
212
|
+
queueCallback = undefined;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
const meterProvider = { getMeter: () => meter };
|
|
218
|
+
return {
|
|
219
|
+
state,
|
|
220
|
+
meterProvider,
|
|
221
|
+
snapshot() {
|
|
222
|
+
let queueSize = null;
|
|
223
|
+
queueCallback?.({
|
|
224
|
+
observe(value) {
|
|
225
|
+
queueSize = value;
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
...state,
|
|
230
|
+
queueSize,
|
|
231
|
+
droppedSpans: Math.max(0, state.droppedSpans - exportQueueFullSpans)
|
|
232
|
+
};
|
|
233
|
+
},
|
|
234
|
+
wrap(exporter) {
|
|
235
|
+
return {
|
|
236
|
+
export(spans, callback) {
|
|
237
|
+
const count = spans.length;
|
|
238
|
+
let completed = false;
|
|
239
|
+
const deadline = observeExportDeadline(state, timeoutMillis);
|
|
240
|
+
const finish = (success) => {
|
|
241
|
+
completed = true;
|
|
242
|
+
clearTimeout(deadline);
|
|
243
|
+
if (success) {
|
|
244
|
+
state.exportedBatches++;
|
|
245
|
+
state.exportedSpans += count;
|
|
246
|
+
} else {
|
|
247
|
+
state.failedBatches++;
|
|
248
|
+
state.failedSpans += count;
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
try {
|
|
252
|
+
exporter.export(spans, (result) => {
|
|
253
|
+
if (completed)
|
|
254
|
+
return;
|
|
255
|
+
finish(result.code === ExportResultCode.SUCCESS);
|
|
256
|
+
if (result.error?.name === "queue_full")
|
|
257
|
+
exportQueueFullSpans += count;
|
|
258
|
+
callback(result);
|
|
259
|
+
});
|
|
260
|
+
} catch (error) {
|
|
261
|
+
if (!completed)
|
|
262
|
+
finish(false);
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
shutdown: () => exporter.shutdown(),
|
|
267
|
+
...exporter.forceFlush ? { forceFlush: exporter.forceFlush.bind(exporter) } : {}
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function renderTracingMetrics(snapshot) {
|
|
273
|
+
for (const key of ["enabled", "exporterConfigured", "active"])
|
|
274
|
+
if (typeof snapshot[key] !== "boolean")
|
|
275
|
+
throw new TypeError("Invalid tracing metrics.");
|
|
276
|
+
const values = {
|
|
277
|
+
strata_tracing_enabled: Number(snapshot.enabled),
|
|
278
|
+
strata_tracing_exporter_configured: Number(snapshot.exporterConfigured),
|
|
279
|
+
strata_tracing_active: Number(snapshot.active),
|
|
280
|
+
strata_tracing_queue_size: snapshot.queueSize,
|
|
281
|
+
strata_tracing_queue_capacity: snapshot.queueCapacity,
|
|
282
|
+
strata_tracing_dropped_spans_total: snapshot.droppedSpans,
|
|
283
|
+
strata_tracing_exported_batches_total: snapshot.exportedBatches,
|
|
284
|
+
strata_tracing_failed_batches_total: snapshot.failedBatches,
|
|
285
|
+
strata_tracing_exported_spans_total: snapshot.exportedSpans,
|
|
286
|
+
strata_tracing_failed_spans_total: snapshot.failedSpans,
|
|
287
|
+
strata_tracing_export_deadline_exceeded_batches_total: snapshot.exportDeadlineExceededBatches,
|
|
288
|
+
strata_tracing_flush_failures_total: snapshot.flushFailures,
|
|
289
|
+
strata_tracing_shutdown_failures_total: snapshot.shutdownFailures
|
|
290
|
+
};
|
|
291
|
+
let output = `# HELP strata_tracing_collector_success Whether tracing observation succeeded.
|
|
292
|
+
# TYPE strata_tracing_collector_success gauge
|
|
293
|
+
strata_tracing_collector_success 1
|
|
294
|
+
`;
|
|
295
|
+
for (const [name, value] of Object.entries(values)) {
|
|
296
|
+
if (value === null) {
|
|
297
|
+
if (name === "strata_tracing_queue_size" || name === "strata_tracing_queue_capacity")
|
|
298
|
+
continue;
|
|
299
|
+
throw new TypeError("Invalid tracing metrics.");
|
|
300
|
+
}
|
|
301
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
302
|
+
throw new TypeError("Invalid tracing metrics.");
|
|
303
|
+
const type = name.endsWith("_total") ? "counter" : "gauge";
|
|
304
|
+
output += `# HELP ${name} Process-local tracing ${name.slice(15).replaceAll("_", " ")}.
|
|
305
|
+
# TYPE ${name} ${type}
|
|
306
|
+
${name} ${value}
|
|
307
|
+
`;
|
|
308
|
+
}
|
|
309
|
+
return output;
|
|
310
|
+
}
|
|
311
|
+
|
|
146
312
|
// ../../src/core/tracing/otel.ts
|
|
147
313
|
function numberOption(value, fallback, name, max = 1e6) {
|
|
148
314
|
const result = value ?? fallback;
|
|
@@ -193,11 +359,14 @@ function createTracingRuntime(options = {}) {
|
|
|
193
359
|
throw new TypeError("Invalid tracing collector endpoint.");
|
|
194
360
|
}
|
|
195
361
|
const exporter = disabled ? undefined : options.exporter ?? (endpoint ? new OTLPTraceExporter({ url: endpoint, timeoutMillis: timeout, concurrencyLimit: 1 }) : undefined);
|
|
362
|
+
const health = createTracingHealth(!disabled, exporter !== undefined, timeout);
|
|
196
363
|
const provider = new NodeTracerProvider({
|
|
197
364
|
resource: resourceFromAttributes({ "service.name": options.serviceName ?? otelServiceName() }),
|
|
198
365
|
sampler,
|
|
199
366
|
spanProcessors: exporter ? [
|
|
200
|
-
new BatchSpanProcessor(
|
|
367
|
+
new BatchSpanProcessor({
|
|
368
|
+
exporter: health.wrap(exporter),
|
|
369
|
+
selfObsMeterProvider: health.meterProvider,
|
|
201
370
|
maxQueueSize: queueSize,
|
|
202
371
|
maxExportBatchSize: batchSize,
|
|
203
372
|
scheduledDelayMillis: delay,
|
|
@@ -217,8 +386,21 @@ function createTracingRuntime(options = {}) {
|
|
|
217
386
|
tracer: provider.getTracer("@getstrata/http"),
|
|
218
387
|
propagator: new W3CTraceContextPropagator,
|
|
219
388
|
run: (parent, callback) => context.with(parent, callback),
|
|
220
|
-
|
|
221
|
-
|
|
389
|
+
metrics: () => health.snapshot(),
|
|
390
|
+
async forceFlush() {
|
|
391
|
+
try {
|
|
392
|
+
await provider.forceFlush();
|
|
393
|
+
} catch (error) {
|
|
394
|
+
health.state.flushFailures++;
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
shutdown: () => shutdown ??= provider.shutdown().catch((error) => {
|
|
399
|
+
health.state.shutdownFailures++;
|
|
400
|
+
throw error;
|
|
401
|
+
}).finally(() => {
|
|
402
|
+
health.state.active = false;
|
|
403
|
+
})
|
|
222
404
|
};
|
|
223
405
|
}
|
|
224
406
|
var runtimeKey = Symbol.for("@getstrata/otel-runtime");
|
|
@@ -319,5 +501,6 @@ export {
|
|
|
319
501
|
acquireTracingRuntime,
|
|
320
502
|
createTracingMiddleware,
|
|
321
503
|
createTracingRuntime,
|
|
322
|
-
getTracingRuntime
|
|
504
|
+
getTracingRuntime,
|
|
505
|
+
renderTracingMetrics
|
|
323
506
|
};
|
|
@@ -32,6 +32,8 @@ export { CACHE_TAGS } from "../core/cache/tags.ts";
|
|
|
32
32
|
export { ConfigStore, createServiceToken, ServiceContainer, type ServiceToken, } from "../core/contracts/container.ts";
|
|
33
33
|
export type { ServiceProvider } from "../core/contracts/di.ts";
|
|
34
34
|
export { resolveService } from "../core/contracts/di.ts";
|
|
35
|
+
export type { TransactionAcquisitionMetrics, TransactionAcquisitionSnapshot, } from "../core/database/acquisitionMetrics.ts";
|
|
36
|
+
export { createTransactionAcquisitionMetrics, renderTransactionAcquisitionMetrics, } from "../core/database/acquisitionMetrics.ts";
|
|
35
37
|
export type { DatabaseConnection } from "../core/database/baseRepository.ts";
|
|
36
38
|
export { default as BaseRepository } from "../core/database/baseRepository.ts";
|
|
37
39
|
export { bindDatabaseConnection, getBoundDatabaseConnection, resetBoundDatabaseConnection, } from "../core/database/bindConnection.ts";
|
|
@@ -64,6 +66,7 @@ export { loadSeedersFromDirectory, runSeedersFromDirectory, } from "../core/data
|
|
|
64
66
|
export type { Seeder, SeederDatabase } from "../core/database/seeders/types.ts";
|
|
65
67
|
export { createSqliteConnection } from "../core/database/sqliteConnection.ts";
|
|
66
68
|
export { defineTable } from "../core/database/table.ts";
|
|
69
|
+
export type { TransactionOptions } from "../core/database/transaction.ts";
|
|
67
70
|
export { hasActiveTransaction, requestTransactionRollback, runInTransaction, } from "../core/database/transaction.ts";
|
|
68
71
|
export type { ModelWhere, ModelWriteValues, QueryJoin, QueryJoinOn, QueryOptions, QueryOrder, QuerySelectItem, QueryWhere, } from "../core/database/types.ts";
|
|
69
72
|
export type { WhereNode } from "../core/database/whereBuilder.ts";
|
|
@@ -127,6 +130,8 @@ export { PrometheusRegistry, prometheusRegistry } from "../core/metrics/promethe
|
|
|
127
130
|
export type { DatabaseNotificationPayload, DatabaseNotificationStore, MailNotificationMessage, Notifiable, NotificationChannelName, } from "../core/notifications/index.ts";
|
|
128
131
|
export { createNotificationDispatcher, Notification, NotificationDispatcher, } from "../core/notifications/index.ts";
|
|
129
132
|
export type { CursorPaginatedResult, KeysetColumn, KeysetCursor, KeysetOptions, PaginatedResult, PaginationMeta, } from "../core/pagination/index.ts";
|
|
133
|
+
export type { FailedJobMetricsCollector, FailedJobMetricsOptions, FailedJobMetricsSnapshot, } from "../core/queue/failedJobMetrics.ts";
|
|
134
|
+
export { createFailedJobMetricsCollector, renderFailedJobMetrics, } from "../core/queue/failedJobMetrics.ts";
|
|
130
135
|
export type { Queue, QueuePriority } from "../core/queue/index.ts";
|
|
131
136
|
export { AsyncQueue, createQueue, Job, SyncQueue } from "../core/queue/index.ts";
|
|
132
137
|
export { JobRegistry } from "../core/queue/jobRegistry.ts";
|
|
@@ -154,6 +159,8 @@ export { currentTenant, currentTenantId, runWithTenant, tenantContext, } from ".
|
|
|
154
159
|
export { isInsideTenantDatabaseScope, runWithTenantDatabase, } from "../core/tenant/tenantDatabaseScope.ts";
|
|
155
160
|
export type { PublicTenancyOptions, TenantMiddlewareOptions, TenantResolver, } from "../core/tenant/tenantMiddleware.ts";
|
|
156
161
|
export { auditChecksum, createTenantMiddleware, DEFAULT_TENANT, normalizeTenantHostname, resolveUserTenantId, } from "../core/tenant/tenantMiddleware.ts";
|
|
162
|
+
export type { TracingMetricsSnapshot } from "../core/tracing/health.ts";
|
|
163
|
+
export { renderTracingMetrics } from "../core/tracing/health.ts";
|
|
157
164
|
export type { TraceContext } from "../core/tracing/traceContext.ts";
|
|
158
165
|
export { currentTraceId, runWithTraceContext, traceContextStorage, } from "../core/tracing/traceContext.ts";
|
|
159
166
|
export type { TracingOptions, TracingRuntime } from "../core/tracing/tracingMiddleware.ts";
|