@logbrew/sdk 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -2
- package/core.cjs +153 -50
- package/index.d.ts +15 -2
- package/package.json +1 -1
- package/react-native.d.ts +1 -0
package/README.md
CHANGED
|
@@ -502,9 +502,13 @@ const client = LogBrewClient.create({
|
|
|
502
502
|
});
|
|
503
503
|
```
|
|
504
504
|
|
|
505
|
-
When either queue limit is full, LogBrew drops the incoming event and keeps earlier context unchanged. `queue_overflow` means the event-count limit was reached, `queue_bytes_overflow` means the compact event-byte limit was reached, and `event_too_large` means one event could not fit a request body by itself.
|
|
505
|
+
When either queue limit is full, LogBrew drops the incoming event and keeps earlier context unchanged. `queue_overflow` means the event-count limit was reached, `queue_bytes_overflow` means the compact event-byte limit was reached, and `event_too_large` means one event could not fit a request body by itself.
|
|
506
506
|
|
|
507
|
-
|
|
507
|
+
A fatal crash report (an issue with `fatal` or `critical` level) is the exception. When it does not fit, LogBrew removes the oldest queued events that are not fatal until it fits, and counts each removed event as `evicted_for_fatal`. It never removes events in a batch that is being sent or retried, and it removes nothing if the fatal still cannot fit. So a full queue drops a fatal only when everything else in it is fatal or in a batch that is being sent; that fatal is counted as `queue_overflow` or `queue_bytes_overflow`. A fatal larger than one request body is still dropped as `event_too_large`. Drop callbacks are advisory and must not interrupt application logging. The default queue remains memory-only; automatic delivery reduces normal flush work, while the app still calls `shutdown()` at its owned graceful-lifecycle boundary.
|
|
508
|
+
|
|
509
|
+
LogBrew sees these drops too. The next request body carries the per-reason counts since the last accepted report, such as `"client_report":{"dropped_events":{"queue_overflow":3}}`. The client clears exactly those counts after a `2xx` response for that request; drops recorded while it is in flight, and counts in a failed or retried request, wait for the next one. A report never sends a request of its own, is left out when nothing was dropped, lists at most 16 reasons, caps each count at 1,000,000,000, and fits inside `maxBatchBytes`. Counts live in memory only, so drops not yet reported are lost when the process restarts.
|
|
510
|
+
|
|
511
|
+
Server runtimes can supply an explicit synchronous `eventStore` when they need restart recovery. The core loads and revalidates compact records during client creation, persists each accepted event before adding it to memory, persists an accepted prefix before removing it from memory, and closes the store only after successful shutdown. `purgePendingEvents()` clears both layers only while no flush or shutdown is queued or active. Implementations must provide synchronous `load`, `append`, `acknowledge`, `purge`, and `close` methods; use the encrypted `persistentQueue` adapter in `@logbrew/node` instead of writing a filesystem adapter from scratch. A store can also provide a synchronous `remove(indexes)` that deletes the records at those ascending `load()` positions. Fatal eviction needs it: a full queue on a store without `remove` drops the fatal as before.
|
|
508
512
|
|
|
509
513
|
Flush requests are also bounded. Core and Node default to 100 events and 256 KiB of exact UTF-8 JSON per request. The browser factory defaults to 64 KiB so its normal request body cannot exceed the existing keepalive transport ceiling. A flush sends one queue snapshot in order, splitting it on either limit. The response reports total `attempts` and acknowledged `batches`. Existing clients that only set `maxQueueSize` keep working; the byte and batch settings are additive and are also accepted by the Node and browser client factories.
|
|
510
514
|
|
package/core.cjs
CHANGED
|
@@ -17,6 +17,14 @@ const SEVERITY_ALIASES = new Map([
|
|
|
17
17
|
["critical", "critical"]
|
|
18
18
|
]);
|
|
19
19
|
const SEVERITY_VALUES = new Set(SEVERITY_ALIASES.keys());
|
|
20
|
+
const EVENT_ADMISSIONS = Object.freeze({
|
|
21
|
+
admitted: Object.freeze({ status: "admitted" }),
|
|
22
|
+
filtered: Object.freeze({ status: "filtered" }),
|
|
23
|
+
unavailable: Object.freeze({ status: "unavailable" }),
|
|
24
|
+
event_too_large: Object.freeze({ status: "dropped", reason: "event_too_large" }),
|
|
25
|
+
queue_overflow: Object.freeze({ status: "dropped", reason: "queue_overflow" }),
|
|
26
|
+
queue_bytes_overflow: Object.freeze({ status: "dropped", reason: "queue_bytes_overflow" })
|
|
27
|
+
});
|
|
20
28
|
const SPAN_STATUSES = new Set(["ok", "error"]);
|
|
21
29
|
const ACTION_STATUSES = new Set(["queued", "running", "success", "failure"]);
|
|
22
30
|
const PRODUCT_ANALYTICS_SCHEMA_VERSION = 1;
|
|
@@ -83,6 +91,8 @@ const DEFAULT_MAX_BATCH_BYTES = 256 * 1024;
|
|
|
83
91
|
const DEFAULT_DELIVERY_INTERVAL_MS = 5000;
|
|
84
92
|
const MAX_DELIVERY_INTERVAL_MS = 60 * 1000;
|
|
85
93
|
const DEFAULT_DELIVERY_QUEUE_THRESHOLD = 50;
|
|
94
|
+
const MAX_CLIENT_REPORT_REASONS = 16;
|
|
95
|
+
const MAX_CLIENT_REPORT_COUNT = 1_000_000_000;
|
|
86
96
|
const DELIVERY_HEALTH_SCHEMA_VERSION = 1;
|
|
87
97
|
const EVENT_QUEUE_FACTORY = Symbol.for("@logbrew/sdk.eventQueueFactory");
|
|
88
98
|
const CONTEXT_PROVIDER = Symbol.for("@logbrew/sdk.contextProvider");
|
|
@@ -197,6 +207,8 @@ class RecordingTransport {
|
|
|
197
207
|
}
|
|
198
208
|
|
|
199
209
|
class LogBrewClient {
|
|
210
|
+
static #issueCapture = LogBrewClient.prototype.issue;
|
|
211
|
+
|
|
200
212
|
static create({
|
|
201
213
|
apiKey,
|
|
202
214
|
sdkName,
|
|
@@ -356,9 +368,12 @@ class LogBrewClient {
|
|
|
356
368
|
this.droppedEventCount = 0;
|
|
357
369
|
this.droppedEventsByReason = {
|
|
358
370
|
event_too_large: 0,
|
|
371
|
+
evicted_for_fatal: 0,
|
|
359
372
|
queue_bytes_overflow: 0,
|
|
360
373
|
queue_overflow: 0
|
|
361
374
|
};
|
|
375
|
+
// In memory only: counts not yet acknowledged by a 2xx are lost on restart.
|
|
376
|
+
this.unreportedDropsByReason = {};
|
|
362
377
|
this.lastDropReason = "none";
|
|
363
378
|
this.storage = resolvedEventStore ? "persistent" : "memory";
|
|
364
379
|
this.hydratedEventCount = recovered.events.length;
|
|
@@ -380,7 +395,7 @@ class LogBrewClient {
|
|
|
380
395
|
this.lastAttemptAtUnixMs = 0;
|
|
381
396
|
this.lastAcceptedAtUnixMs = 0;
|
|
382
397
|
this.lastDroppedAtUnixMs = 0;
|
|
383
|
-
this.
|
|
398
|
+
this.unacknowledgedBatch = undefined;
|
|
384
399
|
this.issueBreadcrumbs = [];
|
|
385
400
|
this.issueBreadcrumbsTruncated = false;
|
|
386
401
|
this.#scheduleAutomaticDelivery();
|
|
@@ -478,7 +493,7 @@ class LogBrewClient {
|
|
|
478
493
|
}
|
|
479
494
|
this.#clearDeliveryTimer();
|
|
480
495
|
this.automaticFlushPending = false;
|
|
481
|
-
this.
|
|
496
|
+
this.unacknowledgedBatch = undefined;
|
|
482
497
|
this.events.splice(0, this.events.length);
|
|
483
498
|
this.serializedEvents.splice(0, this.serializedEvents.length);
|
|
484
499
|
this.serializedEventBytes.splice(0, this.serializedEventBytes.length);
|
|
@@ -517,18 +532,16 @@ class LogBrewClient {
|
|
|
517
532
|
}
|
|
518
533
|
|
|
519
534
|
issue(id, timestamp, attributes) {
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
};
|
|
531
|
-
this.#pushEvent("issue", id, timestamp, validateIssue(withBreadcrumbs));
|
|
535
|
+
this.#pushIssue(id, timestamp, attributes);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
issueWithAdmission(id, timestamp, attributes) {
|
|
539
|
+
const issue = this.issue;
|
|
540
|
+
if (issue !== LogBrewClient.#issueCapture) {
|
|
541
|
+
issue.call(this, id, timestamp, attributes);
|
|
542
|
+
return EVENT_ADMISSIONS.unavailable;
|
|
543
|
+
}
|
|
544
|
+
return this.#pushIssue(id, timestamp, attributes);
|
|
532
545
|
}
|
|
533
546
|
|
|
534
547
|
log(id, timestamp, attributes) {
|
|
@@ -617,6 +630,21 @@ class LogBrewClient {
|
|
|
617
630
|
}
|
|
618
631
|
}
|
|
619
632
|
|
|
633
|
+
#pushIssue(id, timestamp, attributes) {
|
|
634
|
+
const withBreadcrumbs = attributes.breadcrumbs !== undefined || this.issueBreadcrumbs.length === 0
|
|
635
|
+
? attributes
|
|
636
|
+
: {
|
|
637
|
+
...attributes,
|
|
638
|
+
breadcrumbs: this.issueBreadcrumbs,
|
|
639
|
+
...(
|
|
640
|
+
attributes.breadcrumbsTruncated === true || this.issueBreadcrumbsTruncated
|
|
641
|
+
? { breadcrumbsTruncated: true }
|
|
642
|
+
: {}
|
|
643
|
+
)
|
|
644
|
+
};
|
|
645
|
+
return this.#pushEvent("issue", id, timestamp, validateIssue(withBreadcrumbs));
|
|
646
|
+
}
|
|
647
|
+
|
|
620
648
|
#pushEvent(eventType, id, timestamp, attributes) {
|
|
621
649
|
if (this.closed) {
|
|
622
650
|
throw new SdkError("shutdown_error", "client is already shut down");
|
|
@@ -635,39 +663,92 @@ class LogBrewClient {
|
|
|
635
663
|
: { ...attributes, context };
|
|
636
664
|
const event = { type: eventType, id, timestamp, attributes: eventAttributes };
|
|
637
665
|
if (this.eventFilter && this.eventFilter(cloneEvent(event)) === false) {
|
|
638
|
-
return;
|
|
666
|
+
return EVENT_ADMISSIONS.filtered;
|
|
639
667
|
}
|
|
640
668
|
const serializedEvent = JSON.stringify(event);
|
|
641
669
|
const eventBytes = utf8ByteLength(serializedEvent);
|
|
642
670
|
if (this.batchPrefixBytes + eventBytes + this.batchSuffixBytes > this.maxBatchBytes) {
|
|
643
671
|
this.#recordDroppedEvent(event, "event_too_large");
|
|
644
|
-
return;
|
|
672
|
+
return EVENT_ADMISSIONS.event_too_large;
|
|
645
673
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
674
|
+
const fatal = isFatalEvent(event);
|
|
675
|
+
if (fatal) {
|
|
676
|
+
this.#synchronizeEventStore();
|
|
649
677
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
678
|
+
const overflow = this.#overflowReason(this.events.length, this.queuedEventBytes + eventBytes);
|
|
679
|
+
const evicted = overflow && fatal ? this.#evictForFatal(eventBytes) : [];
|
|
680
|
+
if (overflow && evicted.length === 0) {
|
|
681
|
+
this.#recordDroppedEvent(event, overflow);
|
|
682
|
+
return EVENT_ADMISSIONS[overflow];
|
|
653
683
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
684
|
+
try {
|
|
685
|
+
if (this.eventStore) {
|
|
686
|
+
requireSynchronousStoreResult("append", this.eventStore.append({
|
|
687
|
+
event: cloneEvent(event),
|
|
688
|
+
eventBytes,
|
|
689
|
+
serializedEvent
|
|
690
|
+
}));
|
|
691
|
+
}
|
|
692
|
+
this.events.push(event);
|
|
693
|
+
this.serializedEvents.push(serializedEvent);
|
|
694
|
+
this.serializedEventBytes.push(eventBytes);
|
|
695
|
+
this.queuedEventBytes += eventBytes;
|
|
696
|
+
} finally {
|
|
697
|
+
// Report evictions only after admission so drop callbacks cannot refill the freed room.
|
|
698
|
+
for (const evictedEvent of evicted) {
|
|
699
|
+
this.#recordDroppedEvent(evictedEvent, "evicted_for_fatal");
|
|
700
|
+
}
|
|
660
701
|
}
|
|
661
|
-
this.events.push(event);
|
|
662
|
-
this.serializedEvents.push(serializedEvent);
|
|
663
|
-
this.serializedEventBytes.push(eventBytes);
|
|
664
|
-
this.queuedEventBytes += eventBytes;
|
|
665
702
|
this.#scheduleAutomaticDelivery();
|
|
703
|
+
return EVENT_ADMISSIONS.admitted;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
#overflowReason(count, bytes) {
|
|
707
|
+
if (count >= this.maxQueueSize) {
|
|
708
|
+
return "queue_overflow";
|
|
709
|
+
}
|
|
710
|
+
return bytes > this.maxQueueBytes ? "queue_bytes_overflow" : undefined;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Evicts the oldest non-fatal events outside the unacknowledged batch, only when that makes room.
|
|
714
|
+
#evictForFatal(eventBytes) {
|
|
715
|
+
if (this.eventStore && typeof this.eventStore.remove !== "function") {
|
|
716
|
+
return [];
|
|
717
|
+
}
|
|
718
|
+
const indexes = [];
|
|
719
|
+
let count = this.events.length;
|
|
720
|
+
let bytes = this.queuedEventBytes + eventBytes;
|
|
721
|
+
for (
|
|
722
|
+
let index = this.unacknowledgedBatch?.eventsCount ?? 0;
|
|
723
|
+
index < this.events.length && this.#overflowReason(count, bytes);
|
|
724
|
+
index += 1
|
|
725
|
+
) {
|
|
726
|
+
if (!isFatalEvent(this.events[index])) {
|
|
727
|
+
indexes.push(index);
|
|
728
|
+
count -= 1;
|
|
729
|
+
bytes -= this.serializedEventBytes[index];
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if (this.#overflowReason(count, bytes)) {
|
|
733
|
+
return [];
|
|
734
|
+
}
|
|
735
|
+
if (this.eventStore) {
|
|
736
|
+
requireSynchronousStoreResult("remove", this.eventStore.remove(indexes));
|
|
737
|
+
}
|
|
738
|
+
const evicted = indexes.map((index) => this.events[index]);
|
|
739
|
+
for (let position = indexes.length - 1; position >= 0; position -= 1) {
|
|
740
|
+
const index = indexes[position];
|
|
741
|
+
this.events.splice(index, 1);
|
|
742
|
+
this.serializedEvents.splice(index, 1);
|
|
743
|
+
this.queuedEventBytes -= this.serializedEventBytes.splice(index, 1)[0];
|
|
744
|
+
}
|
|
745
|
+
return evicted;
|
|
666
746
|
}
|
|
667
747
|
|
|
668
748
|
#recordDroppedEvent(event, reason) {
|
|
669
749
|
this.droppedEventCount = incrementBounded(this.droppedEventCount);
|
|
670
750
|
this.droppedEventsByReason[reason] = incrementBounded(this.droppedEventsByReason[reason]);
|
|
751
|
+
this.unreportedDropsByReason[reason] = Math.min(MAX_CLIENT_REPORT_COUNT, (this.unreportedDropsByReason[reason] ?? 0) + 1);
|
|
671
752
|
this.lastDropReason = reason;
|
|
672
753
|
this.lastDroppedAtUnixMs = nextBoundedTimestamp(this.lastDroppedAtUnixMs);
|
|
673
754
|
if (!this.onEventDropped) {
|
|
@@ -825,15 +906,17 @@ class LogBrewClient {
|
|
|
825
906
|
let batches = 0;
|
|
826
907
|
let statusCode = 204;
|
|
827
908
|
while (remainingEvents > 0) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
909
|
+
// Eviction for a fatal event can shrink the snapshot while a batch is in flight.
|
|
910
|
+
this.unacknowledgedBatch ??= Object.freeze(
|
|
911
|
+
this.#nextBatch(Math.min(remainingEvents, this.events.length, this.maxBatchEvents))
|
|
912
|
+
);
|
|
913
|
+
const batch = this.unacknowledgedBatch;
|
|
914
|
+
const response = await this.#sendBatch(transport, batch.body);
|
|
915
|
+
this.unacknowledgedBatch = undefined;
|
|
916
|
+
// Clear only what this body carried; drops recorded while it was in flight stay pending.
|
|
917
|
+
for (const [reason, count] of Object.entries(batch.droppedEvents ?? {})) {
|
|
918
|
+
this.unreportedDropsByReason[reason] -= count;
|
|
835
919
|
}
|
|
836
|
-
this.failedBatch = undefined;
|
|
837
920
|
this.#acknowledge(batch.eventsCount);
|
|
838
921
|
this.acceptedBatchCount = incrementBounded(this.acceptedBatchCount);
|
|
839
922
|
remainingEvents -= batch.eventsCount;
|
|
@@ -846,10 +929,13 @@ class LogBrewClient {
|
|
|
846
929
|
}
|
|
847
930
|
|
|
848
931
|
#synchronizeEventStore() {
|
|
849
|
-
if (!this.eventStore)
|
|
932
|
+
if (!this.eventStore) {
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
850
935
|
const recovered = loadStoredEvents(this);
|
|
851
|
-
if (!isOrderedSubsequence(this.serializedEvents, recovered.serializedEvents))
|
|
936
|
+
if (!isOrderedSubsequence(this.serializedEvents, recovered.serializedEvents)) {
|
|
852
937
|
throw new SdkError("persistence_error", "event store lost a queued record");
|
|
938
|
+
}
|
|
853
939
|
this.events = recovered.events;
|
|
854
940
|
this.serializedEvents = recovered.serializedEvents;
|
|
855
941
|
this.serializedEventBytes = recovered.serializedEventBytes;
|
|
@@ -857,7 +943,25 @@ class LogBrewClient {
|
|
|
857
943
|
}
|
|
858
944
|
|
|
859
945
|
#nextBatch(maxEvents) {
|
|
860
|
-
|
|
946
|
+
const reported = Object.entries(this.unreportedDropsByReason).filter(([, count]) => count > 0);
|
|
947
|
+
const droppedEvents = reported.length > 0
|
|
948
|
+
? Object.fromEntries(reported.slice(0, MAX_CLIENT_REPORT_REASONS))
|
|
949
|
+
: undefined;
|
|
950
|
+
const reportSuffix = droppedEvents && `],"client_report":${JSON.stringify({ dropped_events: droppedEvents })}}`;
|
|
951
|
+
const reportedEvents = reportSuffix ? this.#batchEventCount(maxEvents, utf8ByteLength(reportSuffix)) : 0;
|
|
952
|
+
const eventsCount = reportedEvents || this.#batchEventCount(maxEvents, this.batchSuffixBytes);
|
|
953
|
+
if (eventsCount === 0) {
|
|
954
|
+
throw new SdkError("transport_error", "queued event cannot fit the configured batch byte limit");
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
body: `${this.batchPrefix}${this.serializedEvents.slice(0, eventsCount).join(",")}${reportedEvents ? reportSuffix : this.batchSuffix}`,
|
|
958
|
+
droppedEvents: reportedEvents ? droppedEvents : undefined,
|
|
959
|
+
eventsCount
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
#batchEventCount(maxEvents, suffixBytes) {
|
|
964
|
+
let bodyBytes = this.batchPrefixBytes + suffixBytes;
|
|
861
965
|
let eventsCount = 0;
|
|
862
966
|
const eventIds = new Set();
|
|
863
967
|
for (let index = 0; index < maxEvents; index += 1) {
|
|
@@ -873,13 +977,7 @@ class LogBrewClient {
|
|
|
873
977
|
bodyBytes = nextBodyBytes;
|
|
874
978
|
eventsCount += 1;
|
|
875
979
|
}
|
|
876
|
-
|
|
877
|
-
throw new SdkError("transport_error", "queued event cannot fit the configured batch byte limit");
|
|
878
|
-
}
|
|
879
|
-
return {
|
|
880
|
-
body: `${this.batchPrefix}${this.serializedEvents.slice(0, eventsCount).join(",")}${this.batchSuffix}`,
|
|
881
|
-
eventsCount
|
|
882
|
-
};
|
|
980
|
+
return eventsCount;
|
|
883
981
|
}
|
|
884
982
|
|
|
885
983
|
#acknowledge(eventsCount) {
|
|
@@ -1054,6 +1152,11 @@ function utf8ByteLength(value) {
|
|
|
1054
1152
|
return bytes;
|
|
1055
1153
|
}
|
|
1056
1154
|
|
|
1155
|
+
// Fatal input severity is stored as critical; a critical issue is a crash report.
|
|
1156
|
+
function isFatalEvent(event) {
|
|
1157
|
+
return event.type === "issue" && event.attributes.level === "critical";
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1057
1160
|
function incrementBounded(value) {
|
|
1058
1161
|
return value >= Number.MAX_SAFE_INTEGER ? Number.MAX_SAFE_INTEGER : value + 1;
|
|
1059
1162
|
}
|
package/index.d.ts
CHANGED
|
@@ -749,13 +749,15 @@ export type EventStore = {
|
|
|
749
749
|
load(): StoredEvent[];
|
|
750
750
|
append(record: StoredEvent): void;
|
|
751
751
|
acknowledge(count: number): void;
|
|
752
|
+
/** Removes the records at ascending `load()` indexes; without it, fatal issues cannot evict queued events. */
|
|
753
|
+
remove?(indexes: number[]): void;
|
|
752
754
|
purge(): void;
|
|
753
755
|
close(): void;
|
|
754
756
|
};
|
|
755
757
|
|
|
756
758
|
/** Queue drop notification emitted when a bounded in-memory queue is full. */
|
|
757
759
|
export type DroppedEvent = {
|
|
758
|
-
reason: "event_too_large" | "queue_bytes_overflow" | "queue_overflow";
|
|
760
|
+
reason: "event_too_large" | "evicted_for_fatal" | "queue_bytes_overflow" | "queue_overflow";
|
|
759
761
|
eventType: Event["type"];
|
|
760
762
|
eventId: string;
|
|
761
763
|
droppedEvents: number;
|
|
@@ -794,10 +796,11 @@ export type DeliveryHealthSnapshot = Readonly<{
|
|
|
794
796
|
droppedEvents: number;
|
|
795
797
|
droppedByReason: Readonly<{
|
|
796
798
|
event_too_large: number;
|
|
799
|
+
evicted_for_fatal: number;
|
|
797
800
|
queue_bytes_overflow: number;
|
|
798
801
|
queue_overflow: number;
|
|
799
802
|
}>;
|
|
800
|
-
lastDropReason: "none" | "event_too_large" | "queue_bytes_overflow" | "queue_overflow";
|
|
803
|
+
lastDropReason: "none" | "event_too_large" | "evicted_for_fatal" | "queue_bytes_overflow" | "queue_overflow";
|
|
801
804
|
scheduled: boolean;
|
|
802
805
|
inFlight: boolean;
|
|
803
806
|
coalesced: boolean;
|
|
@@ -849,6 +852,14 @@ export declare class RecordingTransport {
|
|
|
849
852
|
send(apiKey: string, body: string): Promise<TransportResponse>;
|
|
850
853
|
}
|
|
851
854
|
|
|
855
|
+
/** Synchronous queue admission only; this result does not report delivery or retention. */
|
|
856
|
+
export type IssueAdmission = Readonly<
|
|
857
|
+
| { status: "admitted" }
|
|
858
|
+
| { status: "filtered" }
|
|
859
|
+
| { status: "dropped"; reason: DroppedEvent["reason"] }
|
|
860
|
+
| { status: "unavailable" }
|
|
861
|
+
>;
|
|
862
|
+
|
|
852
863
|
/** Buffered public client for validating, previewing, and flushing LogBrew events. */
|
|
853
864
|
export declare class LogBrewClient {
|
|
854
865
|
/** Create a client from public SDK identity, retry, and API key settings. */
|
|
@@ -899,6 +910,8 @@ export declare class LogBrewClient {
|
|
|
899
910
|
release(id: string, timestamp: string, attributes: ReleaseAttributes): void;
|
|
900
911
|
environment(id: string, timestamp: string, attributes: EnvironmentAttributes): void;
|
|
901
912
|
issue(id: string, timestamp: string, attributes: IssueAttributes): void;
|
|
913
|
+
/** Capture once and report queue admission. Custom issue overrides run once and return unavailable. */
|
|
914
|
+
issueWithAdmission(id: string, timestamp: string, attributes: IssueAttributes): IssueAdmission;
|
|
902
915
|
log(id: string, timestamp: string, attributes: LogAttributes): void;
|
|
903
916
|
span(id: string, timestamp: string, attributes: SpanAttributes): void;
|
|
904
917
|
action(id: string, timestamp: string, attributes: ActionAttributes): void;
|
package/package.json
CHANGED