@statoss/sdk 0.3.0 → 0.5.0
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 +46 -0
- package/README.md +68 -10
- package/dist/cjs/client.d.ts +24 -1
- package/dist/cjs/client.js +181 -6
- package/dist/cjs/envelope.d.ts +20 -1
- package/dist/cjs/envelope.js +41 -1
- package/dist/cjs/index.d.ts +11 -3
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/logs.d.ts +133 -0
- package/dist/cjs/logs.js +235 -0
- package/dist/cjs/schema.d.ts +13 -1
- package/dist/cjs/schema.js +42 -0
- package/dist/cjs/transport.d.ts +6 -1
- package/dist/cjs/transport.js +23 -7
- package/dist/cjs/types.d.ts +9 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +24 -1
- package/dist/esm/client.js +183 -8
- package/dist/esm/envelope.d.ts +20 -1
- package/dist/esm/envelope.js +37 -1
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +8 -0
- package/dist/esm/logs.d.ts +133 -0
- package/dist/esm/logs.js +219 -0
- package/dist/esm/schema.d.ts +13 -1
- package/dist/esm/schema.js +41 -0
- package/dist/esm/transport.d.ts +6 -1
- package/dist/esm/transport.js +23 -7
- package/dist/esm/types.d.ts +9 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +4 -2
package/dist/esm/logs.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { isLevelName, isSentryLevel, LEVEL_RULE } from "./levels.js";
|
|
2
|
+
import { isObject, normalize, safely, safeString } from "./utils.js";
|
|
3
|
+
export const LOG_LEVELS = [
|
|
4
|
+
"trace",
|
|
5
|
+
"debug",
|
|
6
|
+
"info",
|
|
7
|
+
"warn",
|
|
8
|
+
"error",
|
|
9
|
+
"fatal",
|
|
10
|
+
];
|
|
11
|
+
/** What intake keeps of a line (docs/logs.md, In), in UTF-8 bytes: a body, and an attribute's value. */
|
|
12
|
+
export const MAX_BODY_BYTES = 64 * 1024;
|
|
13
|
+
export const MAX_ATTRIBUTE_BYTES = 8 * 1024;
|
|
14
|
+
/** A cut body's size in bytes before the cut, as intake records it. */
|
|
15
|
+
export const TRUNCATED = "statoss.truncated";
|
|
16
|
+
/** Each level's severity number: the start of its OpenTelemetry range. */
|
|
17
|
+
export const LOG_SEVERITY = {
|
|
18
|
+
trace: 1,
|
|
19
|
+
debug: 5,
|
|
20
|
+
info: 9,
|
|
21
|
+
warn: 13,
|
|
22
|
+
error: 17,
|
|
23
|
+
fatal: 21,
|
|
24
|
+
};
|
|
25
|
+
export function isLogLevel(value) {
|
|
26
|
+
return (typeof value === "string" &&
|
|
27
|
+
LOG_LEVELS.includes(value));
|
|
28
|
+
}
|
|
29
|
+
/** The level a range (a schema's `like`) or a Sentry level is logged at. */
|
|
30
|
+
export function logLevelOf(range) {
|
|
31
|
+
if (range === "warning")
|
|
32
|
+
return "warn";
|
|
33
|
+
return isLogLevel(range) ? range : undefined;
|
|
34
|
+
}
|
|
35
|
+
/** The range a severity number falls in. */
|
|
36
|
+
export function rangeOfSeverity(severity) {
|
|
37
|
+
if (severity >= 21)
|
|
38
|
+
return "fatal";
|
|
39
|
+
if (severity >= 17)
|
|
40
|
+
return "error";
|
|
41
|
+
if (severity >= 13)
|
|
42
|
+
return "warn";
|
|
43
|
+
if (severity >= 9)
|
|
44
|
+
return "info";
|
|
45
|
+
if (severity >= 5)
|
|
46
|
+
return "debug";
|
|
47
|
+
return "trace";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A message with parameters: logger.info(logger.fmt`Paid ${amount} for ${order}`).
|
|
51
|
+
* The line's body is the text; the template and each value go along as
|
|
52
|
+
* the attributes sentry.message.template and sentry.message.parameter.<n>.
|
|
53
|
+
*/
|
|
54
|
+
export function fmt(strings, ...values) {
|
|
55
|
+
const parts = Array.from(strings, (s, i) => s ?? strings.raw[i]);
|
|
56
|
+
let text = parts[0];
|
|
57
|
+
for (let i = 1; i < parts.length; i++)
|
|
58
|
+
text += `${bodyOf(values[i - 1])}${parts[i]}`;
|
|
59
|
+
return Object.freeze({
|
|
60
|
+
text,
|
|
61
|
+
template: parts.map((s) => s.replace(/%/g, "%%")).join("%s"),
|
|
62
|
+
values: Object.freeze([...values]),
|
|
63
|
+
toString: () => text,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
export function isLogMessage(value) {
|
|
67
|
+
return (isObject(value) &&
|
|
68
|
+
typeof value.text === "string" &&
|
|
69
|
+
typeof value.template === "string" &&
|
|
70
|
+
Array.isArray(value.values));
|
|
71
|
+
}
|
|
72
|
+
/** An attribute's value as Sentry types it; undefined and null are left out. */
|
|
73
|
+
export function serializeAttribute(value) {
|
|
74
|
+
if (value === undefined || value === null)
|
|
75
|
+
return undefined;
|
|
76
|
+
if (typeof value === "string")
|
|
77
|
+
return { value: cutBytes(value, MAX_ATTRIBUTE_BYTES), type: "string" };
|
|
78
|
+
if (typeof value === "boolean")
|
|
79
|
+
return { value, type: "boolean" };
|
|
80
|
+
if (typeof value === "number") {
|
|
81
|
+
if (!Number.isFinite(value))
|
|
82
|
+
return { value: safeString(value), type: "string" };
|
|
83
|
+
return Number.isInteger(value)
|
|
84
|
+
? { value, type: "integer" }
|
|
85
|
+
: { value, type: "double" };
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === "bigint") {
|
|
88
|
+
const n = Number(value);
|
|
89
|
+
return Number.isSafeInteger(n)
|
|
90
|
+
? { value: n, type: "integer" }
|
|
91
|
+
: { value: value.toString(), type: "string" };
|
|
92
|
+
}
|
|
93
|
+
const text = value instanceof String
|
|
94
|
+
? String(value)
|
|
95
|
+
: (safely(() => JSON.stringify(normalize(value, 4, 1024, 100))) ??
|
|
96
|
+
safeString(value));
|
|
97
|
+
return { value: cutBytes(text, MAX_ATTRIBUTE_BYTES), type: "string" };
|
|
98
|
+
}
|
|
99
|
+
/** UTF-8 bytes of one UTF-16 unit at i, and how many units it takes. */
|
|
100
|
+
function utf8At(s, i) {
|
|
101
|
+
const c = s.charCodeAt(i);
|
|
102
|
+
if (c < 0x80)
|
|
103
|
+
return [1, 1];
|
|
104
|
+
if (c < 0x800)
|
|
105
|
+
return [2, 1];
|
|
106
|
+
if (c >= 0xd800 &&
|
|
107
|
+
c <= 0xdbff &&
|
|
108
|
+
i + 1 < s.length &&
|
|
109
|
+
(s.charCodeAt(i + 1) & 0xfc00) === 0xdc00)
|
|
110
|
+
return [4, 2];
|
|
111
|
+
// A lone surrogate is written as U+FFFD, three bytes.
|
|
112
|
+
return [3, 1];
|
|
113
|
+
}
|
|
114
|
+
/** A string's length in UTF-8 bytes. */
|
|
115
|
+
export function utf8Length(s) {
|
|
116
|
+
let n = 0;
|
|
117
|
+
for (let i = 0; i < s.length;) {
|
|
118
|
+
const [bytes, units] = utf8At(s, i);
|
|
119
|
+
n += bytes;
|
|
120
|
+
i += units;
|
|
121
|
+
}
|
|
122
|
+
return n;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* A string cut to at most `max` UTF-8 bytes, the cut marked "...", as
|
|
126
|
+
* intake's cut writes it (intake/normalize.ts), never inside a character.
|
|
127
|
+
*/
|
|
128
|
+
export function cutBytes(s, max) {
|
|
129
|
+
if (s.length * 3 <= max || utf8Length(s) <= max)
|
|
130
|
+
return s;
|
|
131
|
+
let n = 0;
|
|
132
|
+
let i = 0;
|
|
133
|
+
while (i < s.length) {
|
|
134
|
+
const [bytes, units] = utf8At(s, i);
|
|
135
|
+
if (n + bytes > max - 3)
|
|
136
|
+
break;
|
|
137
|
+
n += bytes;
|
|
138
|
+
i += units;
|
|
139
|
+
}
|
|
140
|
+
// As intake: a lone high surrogate does not end a cut.
|
|
141
|
+
if (i > 0 && /[\ud800-\udbff]/.test(s[i - 1]))
|
|
142
|
+
i--;
|
|
143
|
+
return `${s.slice(0, i)}...`;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A body past 64 KiB is cut there, with its size in bytes as
|
|
147
|
+
* statoss.truncated, as intake cuts it and records it: one huge line must
|
|
148
|
+
* not take the batch it is sent in down with it.
|
|
149
|
+
*/
|
|
150
|
+
export function cutBody(body, attributes) {
|
|
151
|
+
if (body.length * 3 <= MAX_BODY_BYTES)
|
|
152
|
+
return body;
|
|
153
|
+
const size = utf8Length(body);
|
|
154
|
+
if (size <= MAX_BODY_BYTES)
|
|
155
|
+
return body;
|
|
156
|
+
attributes[TRUNCATED] = { value: size, type: "integer" };
|
|
157
|
+
return cutBytes(body, MAX_BODY_BYTES);
|
|
158
|
+
}
|
|
159
|
+
/** The message, or a value in it, as text: objects as JSON. */
|
|
160
|
+
export function bodyOf(message) {
|
|
161
|
+
if (typeof message === "string")
|
|
162
|
+
return message;
|
|
163
|
+
if (isLogMessage(message))
|
|
164
|
+
return message.text;
|
|
165
|
+
if (message instanceof String)
|
|
166
|
+
return String(message);
|
|
167
|
+
if (isObject(message) && !(message instanceof Error)) {
|
|
168
|
+
const json = safely(() => JSON.stringify(normalize(message, 4)));
|
|
169
|
+
if (json !== undefined)
|
|
170
|
+
return json;
|
|
171
|
+
}
|
|
172
|
+
return safeString(message);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* The level a line is logged at. One of the six (or a Sentry level) is
|
|
176
|
+
* itself; an app's own level is its range's, with its severity and its
|
|
177
|
+
* name for statoss.level; a name nobody declared ranks as error, as an
|
|
178
|
+
* event's does. Anything else is logged as info.
|
|
179
|
+
*/
|
|
180
|
+
export function chooseLogLevel(level, own, logger) {
|
|
181
|
+
if (isLogLevel(level))
|
|
182
|
+
return { level, severity: LOG_SEVERITY[level] };
|
|
183
|
+
if (isSentryLevel(level)) {
|
|
184
|
+
const as = logLevelOf(level) ?? "error";
|
|
185
|
+
return { level: as, severity: LOG_SEVERITY[as] };
|
|
186
|
+
}
|
|
187
|
+
if (isLevelName(level)) {
|
|
188
|
+
const found = own[level];
|
|
189
|
+
if (found)
|
|
190
|
+
return { ...found, custom: level };
|
|
191
|
+
logger.warn(`Level "${level}" is not in the schema or \`levels\`, so the line is logged as error.`);
|
|
192
|
+
return { level: "error", severity: LOG_SEVERITY.error, custom: level };
|
|
193
|
+
}
|
|
194
|
+
logger.warn(`Logged "${safeString(level)}" as info: use trace, debug, info, warn, error, fatal or a level name (${LEVEL_RULE}).`);
|
|
195
|
+
return { level: "info", severity: LOG_SEVERITY.info };
|
|
196
|
+
}
|
|
197
|
+
/** The ranges of init's `levels`: a Sentry level each. */
|
|
198
|
+
export function ownLevelsFromSentry(levels) {
|
|
199
|
+
const out = {};
|
|
200
|
+
for (const [name, sentAs] of Object.entries(levels)) {
|
|
201
|
+
const level = logLevelOf(sentAs) ?? "error";
|
|
202
|
+
out[name] = { level, severity: LOG_SEVERITY[level] };
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/** A logger that hands each line to `capture`. */
|
|
207
|
+
export function makeAppLogger(capture) {
|
|
208
|
+
const at = (level) => (message, attributes) => capture(level, message, attributes);
|
|
209
|
+
return Object.freeze({
|
|
210
|
+
trace: at("trace"),
|
|
211
|
+
debug: at("debug"),
|
|
212
|
+
info: at("info"),
|
|
213
|
+
warn: at("warn"),
|
|
214
|
+
error: at("error"),
|
|
215
|
+
fatal: at("fatal"),
|
|
216
|
+
log: (level, message, attributes) => capture(level, message, attributes),
|
|
217
|
+
fmt,
|
|
218
|
+
});
|
|
219
|
+
}
|
package/dist/esm/schema.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
|
+
import { type AppLogger, type OwnLogLevel } from "./logs.js";
|
|
2
3
|
import type { CaptureContext, Event, Level, SentryLevel } from "./types.js";
|
|
3
4
|
/** The ranges a level ranks in: Sentry's five and OpenTelemetry's trace. */
|
|
4
5
|
export declare const LIKES: readonly ["fatal", "error", "warning", "info", "debug", "trace"];
|
|
5
6
|
export type Like = (typeof LIKES)[number];
|
|
6
7
|
/** The colours a level or a category takes, by name. */
|
|
7
|
-
export declare const SCHEMA_COLORS: readonly ["crimson", "red", "orange", "amber", "yellow", "green", "aqua", "teal", "blue", "violet", "indigo", "magenta", "plum", "grey"];
|
|
8
|
+
export declare const SCHEMA_COLORS: readonly ["crimson", "red", "orange", "amber", "yellow", "green", "aqua", "teal", "blue", "violet", "indigo", "magenta", "plum", "grey", "ink", "ash"];
|
|
8
9
|
export type SchemaColor = (typeof SCHEMA_COLORS)[number];
|
|
9
10
|
/** What a rule tests: a tag by key, the logger, or one of the event's own fields. */
|
|
10
11
|
export declare const RULE_SUBJECTS: readonly ["tag", "logger", "exception", "culprit", "title", "message"];
|
|
@@ -190,7 +191,18 @@ export interface Handle<S = SchemaSpec> {
|
|
|
190
191
|
captureMessage(message: string, levelOrContext?: LevelOf<S> | SchemaContext<S>): string;
|
|
191
192
|
/** Sends an event you built in Sentry's schema. Returns the event id. */
|
|
192
193
|
captureEvent(event: Event, context?: SchemaContext<S>): string;
|
|
194
|
+
/**
|
|
195
|
+
* Log lines, sent when init has `logs: true`; `log` takes the schema's
|
|
196
|
+
* own levels.
|
|
197
|
+
*/
|
|
198
|
+
logger: AppLogger<S>;
|
|
193
199
|
}
|
|
200
|
+
/**
|
|
201
|
+
* How a line at each level of init's `schema` is logged: at its range's
|
|
202
|
+
* level (trace stays trace), with its severity when it gives one inside
|
|
203
|
+
* that range. Names readSchema refuses are left out.
|
|
204
|
+
*/
|
|
205
|
+
export declare function schemaLogLevels(schema: unknown): Record<string, OwnLogLevel>;
|
|
194
206
|
/**
|
|
195
207
|
* The Sentry level each level of init's `schema` is sent as. Built-in
|
|
196
208
|
* levels are left out; names the SDK cannot send are logged in debug mode.
|
package/dist/esm/schema.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CATEGORY_RULE, isCategoryName, isLevelName, isSentryLevel, LEVEL_RULE, } from "./levels.js";
|
|
2
|
+
import { LOG_SEVERITY, logLevelOf, rangeOfSeverity, } from "./logs.js";
|
|
2
3
|
import { isObject, safeString } from "./utils.js";
|
|
3
4
|
// The server's own lists (src/lib/app-schema.ts, src/lib/schema-colors.ts,
|
|
4
5
|
// src/db/schema.ts), kept equal by a test.
|
|
@@ -27,6 +28,8 @@ export const SCHEMA_COLORS = [
|
|
|
27
28
|
"magenta",
|
|
28
29
|
"plum",
|
|
29
30
|
"grey",
|
|
31
|
+
"ink",
|
|
32
|
+
"ash",
|
|
30
33
|
];
|
|
31
34
|
/** What a rule tests: a tag by key, the logger, or one of the event's own fields. */
|
|
32
35
|
export const RULE_SUBJECTS = [
|
|
@@ -74,6 +77,44 @@ function sentAs(name, value, logger) {
|
|
|
74
77
|
return "info";
|
|
75
78
|
return "debug";
|
|
76
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* How a line at each level of init's `schema` is logged: at its range's
|
|
82
|
+
* level (trace stays trace), with its severity when it gives one inside
|
|
83
|
+
* that range. Names readSchema refuses are left out.
|
|
84
|
+
*/
|
|
85
|
+
export function schemaLogLevels(schema) {
|
|
86
|
+
const out = {};
|
|
87
|
+
if (!isObject(schema) || Array.isArray(schema))
|
|
88
|
+
return out;
|
|
89
|
+
const { levels } = schema;
|
|
90
|
+
if (!isObject(levels) || Array.isArray(levels))
|
|
91
|
+
return out;
|
|
92
|
+
for (const [name, value] of Object.entries(levels)) {
|
|
93
|
+
if (isSentryLevel(name) || !isLevelName(name))
|
|
94
|
+
continue;
|
|
95
|
+
const settings = isObject(value) ? value : {};
|
|
96
|
+
const like = typeof value === "string" ? value : settings.like;
|
|
97
|
+
const given = settings.severity;
|
|
98
|
+
const severity = typeof given === "number" &&
|
|
99
|
+
Number.isInteger(given) &&
|
|
100
|
+
given >= 1 &&
|
|
101
|
+
given <= 24
|
|
102
|
+
? given
|
|
103
|
+
: undefined;
|
|
104
|
+
let level = "error";
|
|
105
|
+
if (typeof like === "string" && LIKES.includes(like))
|
|
106
|
+
level = logLevelOf(like) ?? "error";
|
|
107
|
+
else if (like === undefined && severity !== undefined)
|
|
108
|
+
level = rangeOfSeverity(severity);
|
|
109
|
+
out[name] = {
|
|
110
|
+
level,
|
|
111
|
+
severity: severity !== undefined && rangeOfSeverity(severity) === level
|
|
112
|
+
? severity
|
|
113
|
+
: LOG_SEVERITY[level],
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
77
118
|
/**
|
|
78
119
|
* The Sentry level each level of init's `schema` is sent as. Built-in
|
|
79
120
|
* levels are left out; names the SDK cannot send are logged in debug mode.
|
package/dist/esm/transport.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { type Envelope } from "./envelope.js";
|
|
|
2
2
|
import type { Logger } from "./logger.js";
|
|
3
3
|
import type { Transport } from "./types.js";
|
|
4
4
|
export declare const QUEUE_LIMIT = 30;
|
|
5
|
+
/** Of the queue, what log envelopes may take, so a flood of lines never pushes out an error. */
|
|
6
|
+
export declare const LOG_QUEUE_LIMIT = 10;
|
|
5
7
|
export declare const CONCURRENCY = 5;
|
|
6
8
|
/** The default transport: fetch, called on globalThis (Workers refuse any other this). */
|
|
7
9
|
export declare function fetchTransport(browserLike: boolean): Transport;
|
|
@@ -16,12 +18,15 @@ export interface SenderOptions {
|
|
|
16
18
|
}
|
|
17
19
|
/**
|
|
18
20
|
* Sends envelopes in order of arrival, a few at a time, keeping at most 30
|
|
19
|
-
* waiting or in flight
|
|
21
|
+
* waiting or in flight, of them at most 10 of log lines. Whatever cannot
|
|
22
|
+
* be sent is counted for client reports.
|
|
20
23
|
*/
|
|
21
24
|
export declare class Sender {
|
|
22
25
|
private readonly options;
|
|
23
26
|
private readonly queue;
|
|
24
27
|
private active;
|
|
28
|
+
/** Log envelopes waiting or in flight. */
|
|
29
|
+
private logs;
|
|
25
30
|
private limits;
|
|
26
31
|
private waiters;
|
|
27
32
|
private keepaliveBytes;
|
package/dist/esm/transport.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { itemCategory, serializeEnvelope } from "./envelope.js";
|
|
1
|
+
import { isLogEnvelope, itemCategory, itemQuantity, serializeEnvelope, } from "./envelope.js";
|
|
2
2
|
import { isRateLimited, updateRateLimits, } from "./ratelimit.js";
|
|
3
3
|
import { safely, unref } from "./utils.js";
|
|
4
4
|
export const QUEUE_LIMIT = 30;
|
|
5
|
+
/** Of the queue, what log envelopes may take, so a flood of lines never pushes out an error. */
|
|
6
|
+
export const LOG_QUEUE_LIMIT = 10;
|
|
5
7
|
export const CONCURRENCY = 5;
|
|
6
8
|
const REQUEST_TIMEOUT_MS = 30000;
|
|
7
9
|
// Browsers refuse keepalive requests past 64 KiB in flight in total.
|
|
@@ -57,13 +59,16 @@ function header(response, name) {
|
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* Sends envelopes in order of arrival, a few at a time, keeping at most 30
|
|
60
|
-
* waiting or in flight
|
|
62
|
+
* waiting or in flight, of them at most 10 of log lines. Whatever cannot
|
|
63
|
+
* be sent is counted for client reports.
|
|
61
64
|
*/
|
|
62
65
|
export class Sender {
|
|
63
66
|
constructor(options) {
|
|
64
67
|
this.options = options;
|
|
65
68
|
this.queue = [];
|
|
66
69
|
this.active = 0;
|
|
70
|
+
/** Log envelopes waiting or in flight. */
|
|
71
|
+
this.logs = 0;
|
|
67
72
|
this.limits = {};
|
|
68
73
|
this.waiters = [];
|
|
69
74
|
this.keepaliveBytes = 0;
|
|
@@ -73,11 +78,15 @@ export class Sender {
|
|
|
73
78
|
const allowed = this.withoutLimited(envelope);
|
|
74
79
|
if (!allowed)
|
|
75
80
|
return;
|
|
76
|
-
|
|
81
|
+
const log = isLogEnvelope(allowed);
|
|
82
|
+
if (this.queue.length + this.active >= QUEUE_LIMIT ||
|
|
83
|
+
(log && this.logs >= LOG_QUEUE_LIMIT)) {
|
|
77
84
|
this.options.logger.warn("Dropped an envelope: the send queue is full.");
|
|
78
85
|
this.lose(allowed, "queue_overflow");
|
|
79
86
|
return;
|
|
80
87
|
}
|
|
88
|
+
if (log)
|
|
89
|
+
this.logs++;
|
|
81
90
|
this.queue.push(allowed);
|
|
82
91
|
this.pump();
|
|
83
92
|
}
|
|
@@ -126,7 +135,7 @@ export class Sender {
|
|
|
126
135
|
if (!isRateLimited(this.limits, category, now))
|
|
127
136
|
return true;
|
|
128
137
|
if (category !== "internal")
|
|
129
|
-
this.options.recordLoss("ratelimit_backoff", category);
|
|
138
|
+
this.options.recordLoss("ratelimit_backoff", category, itemQuantity(item));
|
|
130
139
|
return false;
|
|
131
140
|
});
|
|
132
141
|
if (items.length < envelope[1].length) {
|
|
@@ -136,13 +145,20 @@ export class Sender {
|
|
|
136
145
|
}
|
|
137
146
|
pump() {
|
|
138
147
|
while (this.active < CONCURRENCY && this.queue.length > 0) {
|
|
148
|
+
const waited = this.queue.shift();
|
|
149
|
+
const log = isLogEnvelope(waited);
|
|
139
150
|
// A 429 may have come in while it waited.
|
|
140
|
-
const next = this.withoutLimited(
|
|
141
|
-
if (!next)
|
|
151
|
+
const next = this.withoutLimited(waited);
|
|
152
|
+
if (!next) {
|
|
153
|
+
if (log)
|
|
154
|
+
this.logs--;
|
|
142
155
|
continue;
|
|
156
|
+
}
|
|
143
157
|
this.active++;
|
|
144
158
|
const done = () => {
|
|
145
159
|
this.active--;
|
|
160
|
+
if (log)
|
|
161
|
+
this.logs--;
|
|
146
162
|
this.pump();
|
|
147
163
|
};
|
|
148
164
|
void this.deliver(next).then(done, done);
|
|
@@ -207,7 +223,7 @@ export class Sender {
|
|
|
207
223
|
const category = itemCategory(item);
|
|
208
224
|
// A lost client report is not reported again.
|
|
209
225
|
if (category !== "internal")
|
|
210
|
-
this.options.recordLoss(reason, category);
|
|
226
|
+
this.options.recordLoss(reason, category, itemQuantity(item));
|
|
211
227
|
}
|
|
212
228
|
}
|
|
213
229
|
}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Log } from "./logs.js";
|
|
1
2
|
/** The five levels Sentry knows. */
|
|
2
3
|
export type SentryLevel = "fatal" | "error" | "warning" | "info" | "debug";
|
|
3
4
|
/**
|
|
@@ -174,7 +175,10 @@ export interface InitOptions {
|
|
|
174
175
|
beforeBreadcrumb?: (breadcrumb: Breadcrumb, hint?: BreadcrumbHint) => Breadcrumb | null;
|
|
175
176
|
/** Defaults to 100, which is also the most kept. */
|
|
176
177
|
maxBreadcrumbs?: number;
|
|
177
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* When true, browsers ask the server to record the sender's IP address,
|
|
180
|
+
* and log lines carry the scope's user. Defaults to false.
|
|
181
|
+
*/
|
|
178
182
|
sendDefaultPii?: boolean;
|
|
179
183
|
/** Tags on every event. */
|
|
180
184
|
tags?: Record<string, Primitive>;
|
|
@@ -187,4 +191,8 @@ export interface InitOptions {
|
|
|
187
191
|
integrations?: IntegrationToggles;
|
|
188
192
|
/** Your own level names and the Sentry level each is sent as, such as { "payment-failed": "error" }. */
|
|
189
193
|
levels?: Record<string, SentryLevel>;
|
|
194
|
+
/** Sends the lines of `logger`, as Sentry's enableLogs does. Defaults to false. */
|
|
195
|
+
logs?: boolean;
|
|
196
|
+
/** Change or drop (return null) a log line before it is sent. */
|
|
197
|
+
beforeSendLog?: (log: Log) => Log | null;
|
|
190
198
|
}
|
package/dist/esm/version.d.ts
CHANGED
package/dist/esm/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statoss/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Error tracking for StatOSS: browsers, Node, Bun, Deno, Cloudflare Workers and Vercel Edge. Speaks Sentry's protocol.",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Error tracking and logs for StatOSS: browsers, Node, Bun, Deno, Cloudflare Workers and Vercel Edge. Speaks Sentry's protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "StatOSS",
|
|
7
7
|
"homepage": "https://statoss.com",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"statoss",
|
|
15
15
|
"errors",
|
|
16
16
|
"error-tracking",
|
|
17
|
+
"logs",
|
|
17
18
|
"sentry",
|
|
18
19
|
"monitoring"
|
|
19
20
|
],
|
|
@@ -38,6 +39,7 @@
|
|
|
38
39
|
"files": [
|
|
39
40
|
"dist",
|
|
40
41
|
"README.md",
|
|
42
|
+
"CHANGELOG.md",
|
|
41
43
|
"LICENSE"
|
|
42
44
|
],
|
|
43
45
|
"engines": {
|