@statoss/sdk 0.2.0 → 0.4.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 +41 -0
- package/README.md +151 -23
- package/dist/cjs/client.d.ts +29 -2
- package/dist/cjs/client.js +211 -4
- package/dist/cjs/envelope.d.ts +20 -1
- package/dist/cjs/envelope.js +41 -1
- package/dist/cjs/index.d.ts +19 -2
- package/dist/cjs/index.js +19 -6
- package/dist/cjs/levels.d.ts +4 -1
- package/dist/cjs/levels.js +23 -12
- package/dist/cjs/logs.d.ts +133 -0
- package/dist/cjs/logs.js +235 -0
- package/dist/cjs/schema.d.ts +211 -0
- package/dist/cjs/schema.js +159 -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 +29 -2
- package/dist/esm/client.js +212 -5
- package/dist/esm/envelope.d.ts +20 -1
- package/dist/esm/envelope.js +37 -1
- package/dist/esm/index.d.ts +19 -2
- package/dist/esm/index.js +17 -5
- package/dist/esm/levels.d.ts +4 -1
- package/dist/esm/levels.js +20 -10
- package/dist/esm/logs.d.ts +133 -0
- package/dist/esm/logs.js +219 -0
- package/dist/esm/schema.d.ts +211 -0
- package/dist/esm/schema.js +153 -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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import type { Logger } from "./logger.js";
|
|
2
|
+
import { type AppLogger, type OwnLogLevel } from "./logs.js";
|
|
3
|
+
import type { CaptureContext, Event, Level, SentryLevel } from "./types.js";
|
|
4
|
+
/** The ranges a level ranks in: Sentry's five and OpenTelemetry's trace. */
|
|
5
|
+
export declare const LIKES: readonly ["fatal", "error", "warning", "info", "debug", "trace"];
|
|
6
|
+
export type Like = (typeof LIKES)[number];
|
|
7
|
+
/** The colours a level or a category takes, by name. */
|
|
8
|
+
export declare const SCHEMA_COLORS: readonly ["crimson", "red", "orange", "amber", "yellow", "green", "aqua", "teal", "blue", "violet", "indigo", "magenta", "plum", "grey"];
|
|
9
|
+
export type SchemaColor = (typeof SCHEMA_COLORS)[number];
|
|
10
|
+
/** What a rule tests: a tag by key, the logger, or one of the event's own fields. */
|
|
11
|
+
export declare const RULE_SUBJECTS: readonly ["tag", "logger", "exception", "culprit", "title", "message"];
|
|
12
|
+
export type RuleSubject = (typeof RULE_SUBJECTS)[number];
|
|
13
|
+
export declare const RULE_MATCHES: readonly ["is", "starts", "contains"];
|
|
14
|
+
export type RuleMatch = (typeof RULE_MATCHES)[number];
|
|
15
|
+
export declare const FIELD_TYPES: readonly ["text", "number"];
|
|
16
|
+
export type FieldType = (typeof FIELD_TYPES)[number];
|
|
17
|
+
/** Each range's severities, as OpenTelemetry numbers them. */
|
|
18
|
+
interface Severities {
|
|
19
|
+
fatal: 21 | 22 | 23 | 24;
|
|
20
|
+
error: 17 | 18 | 19 | 20;
|
|
21
|
+
warning: 13 | 14 | 15 | 16;
|
|
22
|
+
info: 9 | 10 | 11 | 12;
|
|
23
|
+
debug: 5 | 6 | 7 | 8;
|
|
24
|
+
trace: 1 | 2 | 3 | 4;
|
|
25
|
+
}
|
|
26
|
+
export type Severity = Severities[Like];
|
|
27
|
+
/** A level of your own. Null clears a setting. */
|
|
28
|
+
export interface LevelSettings {
|
|
29
|
+
/** The range it ranks in. Without it (and without severity) it ranks as error. */
|
|
30
|
+
like?: Like;
|
|
31
|
+
/** 1 to 24, inside the range of `like`. */
|
|
32
|
+
severity?: Severity;
|
|
33
|
+
label?: string | null;
|
|
34
|
+
color?: SchemaColor | null;
|
|
35
|
+
}
|
|
36
|
+
/** A category. Null clears a setting. */
|
|
37
|
+
export interface CategorySettings {
|
|
38
|
+
label?: string | null;
|
|
39
|
+
color?: SchemaColor | null;
|
|
40
|
+
/** Left out of filters and lists; its events still carry it. */
|
|
41
|
+
hidden?: boolean;
|
|
42
|
+
/** A team member's email. */
|
|
43
|
+
owner?: string | null;
|
|
44
|
+
}
|
|
45
|
+
/** A field: a tag key. Null clears a setting. */
|
|
46
|
+
export interface FieldSettings {
|
|
47
|
+
label?: string | null;
|
|
48
|
+
type?: FieldType;
|
|
49
|
+
unit?: string | null;
|
|
50
|
+
/** Gets a picker in Explore's filters. */
|
|
51
|
+
filterable?: boolean;
|
|
52
|
+
}
|
|
53
|
+
type RuleTests = {
|
|
54
|
+
subject: "tag";
|
|
55
|
+
tagKey: string;
|
|
56
|
+
} | {
|
|
57
|
+
subject: Exclude<RuleSubject, "tag">;
|
|
58
|
+
tagKey?: undefined;
|
|
59
|
+
};
|
|
60
|
+
type RuleSets<L extends string, C extends string> = {
|
|
61
|
+
level: L;
|
|
62
|
+
category?: C;
|
|
63
|
+
} | {
|
|
64
|
+
level?: L;
|
|
65
|
+
category: C;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Sets a level, a category or both for events that match: the first rule
|
|
69
|
+
* that matches wins. `match` defaults to "is".
|
|
70
|
+
*/
|
|
71
|
+
export type SchemaRule<L extends string = string, C extends string = string> = RuleTests & {
|
|
72
|
+
match?: RuleMatch;
|
|
73
|
+
value: string;
|
|
74
|
+
} & RuleSets<L, C>;
|
|
75
|
+
/** An app's levels, categories, fields and rules, as the server takes them. */
|
|
76
|
+
export interface SchemaSpec {
|
|
77
|
+
/** Each name with the range it ranks in, or with its settings. */
|
|
78
|
+
levels?: {
|
|
79
|
+
readonly [name: string]: Like | LevelSettings;
|
|
80
|
+
};
|
|
81
|
+
categories?: readonly string[] | {
|
|
82
|
+
readonly [name: string]: CategorySettings;
|
|
83
|
+
};
|
|
84
|
+
fields?: readonly string[] | {
|
|
85
|
+
readonly [name: string]: FieldSettings;
|
|
86
|
+
};
|
|
87
|
+
rules?: readonly SchemaRule[];
|
|
88
|
+
}
|
|
89
|
+
type NamesIn<T> = T extends readonly (infer N)[] ? N & string : T extends object ? keyof T & string : never;
|
|
90
|
+
type LevelNames<S> = S extends {
|
|
91
|
+
levels?: infer L;
|
|
92
|
+
} ? NamesIn<NonNullable<L>> : never;
|
|
93
|
+
type CategoryNames<S> = S extends {
|
|
94
|
+
categories?: infer C;
|
|
95
|
+
} ? NamesIn<NonNullable<C>> : never;
|
|
96
|
+
type FieldNames<S> = S extends {
|
|
97
|
+
fields?: infer F;
|
|
98
|
+
} ? NamesIn<NonNullable<F>> : never;
|
|
99
|
+
/** A Sentry level or a level of the schema. */
|
|
100
|
+
export type LevelOf<S> = string extends LevelNames<S> ? Level : SentryLevel | LevelNames<S>;
|
|
101
|
+
/** A category of the schema. */
|
|
102
|
+
export type CategoryOf<S> = CategoryNames<S>;
|
|
103
|
+
/** A field of the schema. */
|
|
104
|
+
export type FieldOf<S> = FieldNames<S>;
|
|
105
|
+
type NameChar = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "." | "-" | "_";
|
|
106
|
+
/** True for 1 to Max of NameChar. A name only known as string passes. */
|
|
107
|
+
type IsName<S extends string, Max extends number, Seen extends 0[] = []> = string extends S ? true : S extends `${infer C}${infer Rest}` ? C extends NameChar ? Seen["length"] extends Max ? false : IsName<Rest, Max, [...Seen, 0]> : false : Seen extends [] ? false : true;
|
|
108
|
+
type LevelRule = "Level names are 1 to 32 lowercase letters, digits, dots, dashes or underscores";
|
|
109
|
+
type CategoryRule = "Category names are 1 to 64 lowercase letters, digits, dots, dashes or underscores";
|
|
110
|
+
type FieldRule = "Field names are not empty and do not start statoss.";
|
|
111
|
+
type Only<V, Keys extends PropertyKey, Message extends string> = {
|
|
112
|
+
[P in Exclude<keyof V, Keys>]: {
|
|
113
|
+
[M in Message]: true;
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
type LevelCheck<V> = V extends string ? Like : {
|
|
117
|
+
like?: Like;
|
|
118
|
+
severity?: V extends {
|
|
119
|
+
like: infer K extends Like;
|
|
120
|
+
} ? Severities[K] : Severity;
|
|
121
|
+
label?: string | null;
|
|
122
|
+
color?: SchemaColor | null;
|
|
123
|
+
} & Only<V, keyof LevelSettings, "A level takes like, severity, label and color">;
|
|
124
|
+
/** Sentry's own levels rank as themselves: a label and a colour only. */
|
|
125
|
+
type BuiltinLevelCheck<K extends SentryLevel, V> = V extends string ? K : {
|
|
126
|
+
like?: K;
|
|
127
|
+
label?: string | null;
|
|
128
|
+
color?: SchemaColor | null;
|
|
129
|
+
} & Only<V, "like" | "label" | "color", "A built-in level takes label and color, and ranks as itself">;
|
|
130
|
+
type CheckLevels<T> = {
|
|
131
|
+
[K in keyof T]: K extends SentryLevel ? BuiltinLevelCheck<K, T[K]> : K extends string ? IsName<K, 32> extends true ? LevelCheck<T[K]> : {
|
|
132
|
+
[M in LevelRule]: true;
|
|
133
|
+
} : never;
|
|
134
|
+
};
|
|
135
|
+
type CheckCategories<T> = T extends readonly unknown[] ? {
|
|
136
|
+
[I in keyof T]: T[I] extends string ? IsName<T[I], 64> extends true ? T[I] : {
|
|
137
|
+
[M in CategoryRule]: true;
|
|
138
|
+
} : string;
|
|
139
|
+
} : {
|
|
140
|
+
[K in keyof T]: K extends string ? IsName<K, 64> extends true ? CategorySettings & Only<T[K], keyof CategorySettings, "A category takes label, color, hidden and owner"> : {
|
|
141
|
+
[M in CategoryRule]: true;
|
|
142
|
+
} : never;
|
|
143
|
+
};
|
|
144
|
+
type IsField<K extends string> = K extends "" | `statoss.${string}` ? false : true;
|
|
145
|
+
type CheckFields<T> = T extends readonly unknown[] ? {
|
|
146
|
+
[I in keyof T]: T[I] extends string ? IsField<T[I]> extends true ? T[I] : {
|
|
147
|
+
[M in FieldRule]: true;
|
|
148
|
+
} : string;
|
|
149
|
+
} : {
|
|
150
|
+
[K in keyof T]: K extends string ? IsField<K> extends true ? FieldSettings & Only<T[K], keyof FieldSettings, "A field takes label, type, unit and filterable"> : {
|
|
151
|
+
[M in FieldRule]: true;
|
|
152
|
+
} : never;
|
|
153
|
+
};
|
|
154
|
+
type CheckRules<T, L extends string, C extends string> = {
|
|
155
|
+
[I in keyof T]: SchemaRule<L, C> & Only<T[I], "subject" | "tagKey" | "match" | "value" | "level" | "category", "A rule takes subject, tagKey, match, value, level and category">;
|
|
156
|
+
};
|
|
157
|
+
/** What defineSchema holds a schema to, read from the schema itself. */
|
|
158
|
+
export type CheckSchema<S> = {
|
|
159
|
+
[K in keyof S]: K extends "levels" ? CheckLevels<S[K]> : K extends "categories" ? CheckCategories<S[K]> : K extends "fields" ? CheckFields<S[K]> : K extends "rules" ? CheckRules<S[K], SentryLevel | LevelNames<S>, CategoryNames<S>> : {
|
|
160
|
+
"A schema takes levels, categories, fields and rules": true;
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Declares an app's levels, categories, fields and rules. Returns the schema
|
|
165
|
+
* as given: plain data in the shape the server takes, so it can be sent as
|
|
166
|
+
* it is. Its names become types: pass it to init as `schema` and the capture
|
|
167
|
+
* functions init returns take only these names.
|
|
168
|
+
*/
|
|
169
|
+
export declare function defineSchema<const S extends SchemaSpec & CheckSchema<S>>(schema: S): S;
|
|
170
|
+
/** A field's value; sent as a tag. */
|
|
171
|
+
export type FieldValue = string | number | boolean;
|
|
172
|
+
/** What the capture functions of init's handle take, typed by the schema. */
|
|
173
|
+
export interface SchemaContext<S = SchemaSpec> extends Omit<CaptureContext, "level" | "category"> {
|
|
174
|
+
/** A Sentry level or a level of the schema. */
|
|
175
|
+
level?: LevelOf<S>;
|
|
176
|
+
/** A category of the schema, sent as the `statoss.category` tag. */
|
|
177
|
+
category?: CategoryOf<S>;
|
|
178
|
+
/**
|
|
179
|
+
* Fields of the schema, sent as tags; a schema that declares none takes
|
|
180
|
+
* none here.
|
|
181
|
+
*/
|
|
182
|
+
fields?: [FieldOf<S>] extends [never] ? never : {
|
|
183
|
+
[K in FieldOf<S>]?: FieldValue;
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** The capture functions, typed by the schema given to init. */
|
|
187
|
+
export interface Handle<S = SchemaSpec> {
|
|
188
|
+
/** Sends an error (or anything thrown). Returns the event id. */
|
|
189
|
+
captureException(error: unknown, context?: SchemaContext<S>): string;
|
|
190
|
+
/** Sends a message, with a level or a context. Returns the event id. */
|
|
191
|
+
captureMessage(message: string, levelOrContext?: LevelOf<S> | SchemaContext<S>): string;
|
|
192
|
+
/** Sends an event you built in Sentry's schema. Returns the event id. */
|
|
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>;
|
|
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>;
|
|
206
|
+
/**
|
|
207
|
+
* The Sentry level each level of init's `schema` is sent as. Built-in
|
|
208
|
+
* levels are left out; names the SDK cannot send are logged in debug mode.
|
|
209
|
+
*/
|
|
210
|
+
export declare function readSchema(schema: unknown, logger: Logger): Record<string, SentryLevel>;
|
|
211
|
+
export {};
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIELD_TYPES = exports.RULE_MATCHES = exports.RULE_SUBJECTS = exports.SCHEMA_COLORS = exports.LIKES = void 0;
|
|
4
|
+
exports.defineSchema = defineSchema;
|
|
5
|
+
exports.schemaLogLevels = schemaLogLevels;
|
|
6
|
+
exports.readSchema = readSchema;
|
|
7
|
+
const levels_js_1 = require("./levels.js");
|
|
8
|
+
const logs_js_1 = require("./logs.js");
|
|
9
|
+
const utils_js_1 = require("./utils.js");
|
|
10
|
+
// The server's own lists (src/lib/app-schema.ts, src/lib/schema-colors.ts,
|
|
11
|
+
// src/db/schema.ts), kept equal by a test.
|
|
12
|
+
/** The ranges a level ranks in: Sentry's five and OpenTelemetry's trace. */
|
|
13
|
+
exports.LIKES = [
|
|
14
|
+
"fatal",
|
|
15
|
+
"error",
|
|
16
|
+
"warning",
|
|
17
|
+
"info",
|
|
18
|
+
"debug",
|
|
19
|
+
"trace",
|
|
20
|
+
];
|
|
21
|
+
/** The colours a level or a category takes, by name. */
|
|
22
|
+
exports.SCHEMA_COLORS = [
|
|
23
|
+
"crimson",
|
|
24
|
+
"red",
|
|
25
|
+
"orange",
|
|
26
|
+
"amber",
|
|
27
|
+
"yellow",
|
|
28
|
+
"green",
|
|
29
|
+
"aqua",
|
|
30
|
+
"teal",
|
|
31
|
+
"blue",
|
|
32
|
+
"violet",
|
|
33
|
+
"indigo",
|
|
34
|
+
"magenta",
|
|
35
|
+
"plum",
|
|
36
|
+
"grey",
|
|
37
|
+
];
|
|
38
|
+
/** What a rule tests: a tag by key, the logger, or one of the event's own fields. */
|
|
39
|
+
exports.RULE_SUBJECTS = [
|
|
40
|
+
"tag",
|
|
41
|
+
"logger",
|
|
42
|
+
"exception",
|
|
43
|
+
"culprit",
|
|
44
|
+
"title",
|
|
45
|
+
"message",
|
|
46
|
+
];
|
|
47
|
+
exports.RULE_MATCHES = ["is", "starts", "contains"];
|
|
48
|
+
exports.FIELD_TYPES = ["text", "number"];
|
|
49
|
+
/**
|
|
50
|
+
* Declares an app's levels, categories, fields and rules. Returns the schema
|
|
51
|
+
* as given: plain data in the shape the server takes, so it can be sent as
|
|
52
|
+
* it is. Its names become types: pass it to init as `schema` and the capture
|
|
53
|
+
* functions init returns take only these names.
|
|
54
|
+
*/
|
|
55
|
+
function defineSchema(schema) {
|
|
56
|
+
return schema;
|
|
57
|
+
}
|
|
58
|
+
// At runtime
|
|
59
|
+
/** The Sentry level a level of the schema is sent as. */
|
|
60
|
+
function sentAs(name, value, logger) {
|
|
61
|
+
const settings = (0, utils_js_1.isObject)(value) ? value : {};
|
|
62
|
+
const like = typeof value === "string" ? value : settings.like;
|
|
63
|
+
if ((0, levels_js_1.isSentryLevel)(like))
|
|
64
|
+
return like;
|
|
65
|
+
if (like === "trace")
|
|
66
|
+
return "debug";
|
|
67
|
+
if (like !== undefined || (typeof value !== "string" && !(0, utils_js_1.isObject)(value))) {
|
|
68
|
+
logger.warn(`Level "${name}" in the schema: like should be ${exports.LIKES.join(", ")}; sent as error.`);
|
|
69
|
+
return "error";
|
|
70
|
+
}
|
|
71
|
+
const severity = settings.severity;
|
|
72
|
+
if (typeof severity !== "number" || severity < 1 || severity > 24)
|
|
73
|
+
return "error";
|
|
74
|
+
if (severity >= 21)
|
|
75
|
+
return "fatal";
|
|
76
|
+
if (severity >= 17)
|
|
77
|
+
return "error";
|
|
78
|
+
if (severity >= 13)
|
|
79
|
+
return "warning";
|
|
80
|
+
if (severity >= 9)
|
|
81
|
+
return "info";
|
|
82
|
+
return "debug";
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* How a line at each level of init's `schema` is logged: at its range's
|
|
86
|
+
* level (trace stays trace), with its severity when it gives one inside
|
|
87
|
+
* that range. Names readSchema refuses are left out.
|
|
88
|
+
*/
|
|
89
|
+
function schemaLogLevels(schema) {
|
|
90
|
+
const out = {};
|
|
91
|
+
if (!(0, utils_js_1.isObject)(schema) || Array.isArray(schema))
|
|
92
|
+
return out;
|
|
93
|
+
const { levels } = schema;
|
|
94
|
+
if (!(0, utils_js_1.isObject)(levels) || Array.isArray(levels))
|
|
95
|
+
return out;
|
|
96
|
+
for (const [name, value] of Object.entries(levels)) {
|
|
97
|
+
if ((0, levels_js_1.isSentryLevel)(name) || !(0, levels_js_1.isLevelName)(name))
|
|
98
|
+
continue;
|
|
99
|
+
const settings = (0, utils_js_1.isObject)(value) ? value : {};
|
|
100
|
+
const like = typeof value === "string" ? value : settings.like;
|
|
101
|
+
const given = settings.severity;
|
|
102
|
+
const severity = typeof given === "number" &&
|
|
103
|
+
Number.isInteger(given) &&
|
|
104
|
+
given >= 1 &&
|
|
105
|
+
given <= 24
|
|
106
|
+
? given
|
|
107
|
+
: undefined;
|
|
108
|
+
let level = "error";
|
|
109
|
+
if (typeof like === "string" && exports.LIKES.includes(like))
|
|
110
|
+
level = (0, logs_js_1.logLevelOf)(like) ?? "error";
|
|
111
|
+
else if (like === undefined && severity !== undefined)
|
|
112
|
+
level = (0, logs_js_1.rangeOfSeverity)(severity);
|
|
113
|
+
out[name] = {
|
|
114
|
+
level,
|
|
115
|
+
severity: severity !== undefined && (0, logs_js_1.rangeOfSeverity)(severity) === level
|
|
116
|
+
? severity
|
|
117
|
+
: logs_js_1.LOG_SEVERITY[level],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The Sentry level each level of init's `schema` is sent as. Built-in
|
|
124
|
+
* levels are left out; names the SDK cannot send are logged in debug mode.
|
|
125
|
+
*/
|
|
126
|
+
function readSchema(schema, logger) {
|
|
127
|
+
const out = {};
|
|
128
|
+
if (schema === undefined || schema === null)
|
|
129
|
+
return out;
|
|
130
|
+
if (!(0, utils_js_1.isObject)(schema) || Array.isArray(schema)) {
|
|
131
|
+
logger.warn("Ignored `schema`: pass what defineSchema returns.");
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
const { levels, categories } = schema;
|
|
135
|
+
if ((0, utils_js_1.isObject)(levels) && !Array.isArray(levels)) {
|
|
136
|
+
for (const [name, value] of Object.entries(levels)) {
|
|
137
|
+
// A built-in level, given a label or a colour.
|
|
138
|
+
if ((0, levels_js_1.isSentryLevel)(name))
|
|
139
|
+
continue;
|
|
140
|
+
if ((0, levels_js_1.isLevelName)(name))
|
|
141
|
+
out[name] = sentAs(name, value, logger);
|
|
142
|
+
else
|
|
143
|
+
logger.warn(`Ignored level "${name}" in the schema: ${levels_js_1.LEVEL_RULE}.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (levels !== undefined && levels !== null) {
|
|
147
|
+
logger.warn('Ignored the schema\'s levels: they should be an object such as { "payment-failed": "error" }.');
|
|
148
|
+
}
|
|
149
|
+
const names = Array.isArray(categories)
|
|
150
|
+
? categories
|
|
151
|
+
: (0, utils_js_1.isObject)(categories)
|
|
152
|
+
? Object.keys(categories)
|
|
153
|
+
: [];
|
|
154
|
+
for (const name of names) {
|
|
155
|
+
if (!(0, levels_js_1.isCategoryName)(name))
|
|
156
|
+
logger.warn(`Category "${(0, utils_js_1.safeString)(name)}" in the schema cannot be sent: ${levels_js_1.CATEGORY_RULE}.`);
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
package/dist/cjs/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/cjs/transport.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Sender = exports.CONCURRENCY = exports.QUEUE_LIMIT = void 0;
|
|
3
|
+
exports.Sender = exports.CONCURRENCY = exports.LOG_QUEUE_LIMIT = exports.QUEUE_LIMIT = void 0;
|
|
4
4
|
exports.fetchTransport = fetchTransport;
|
|
5
5
|
const envelope_js_1 = require("./envelope.js");
|
|
6
6
|
const ratelimit_js_1 = require("./ratelimit.js");
|
|
7
7
|
const utils_js_1 = require("./utils.js");
|
|
8
8
|
exports.QUEUE_LIMIT = 30;
|
|
9
|
+
/** Of the queue, what log envelopes may take, so a flood of lines never pushes out an error. */
|
|
10
|
+
exports.LOG_QUEUE_LIMIT = 10;
|
|
9
11
|
exports.CONCURRENCY = 5;
|
|
10
12
|
const REQUEST_TIMEOUT_MS = 30000;
|
|
11
13
|
// Browsers refuse keepalive requests past 64 KiB in flight in total.
|
|
@@ -61,13 +63,16 @@ function header(response, name) {
|
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
65
|
* Sends envelopes in order of arrival, a few at a time, keeping at most 30
|
|
64
|
-
* waiting or in flight
|
|
66
|
+
* waiting or in flight, of them at most 10 of log lines. Whatever cannot
|
|
67
|
+
* be sent is counted for client reports.
|
|
65
68
|
*/
|
|
66
69
|
class Sender {
|
|
67
70
|
constructor(options) {
|
|
68
71
|
this.options = options;
|
|
69
72
|
this.queue = [];
|
|
70
73
|
this.active = 0;
|
|
74
|
+
/** Log envelopes waiting or in flight. */
|
|
75
|
+
this.logs = 0;
|
|
71
76
|
this.limits = {};
|
|
72
77
|
this.waiters = [];
|
|
73
78
|
this.keepaliveBytes = 0;
|
|
@@ -77,11 +82,15 @@ class Sender {
|
|
|
77
82
|
const allowed = this.withoutLimited(envelope);
|
|
78
83
|
if (!allowed)
|
|
79
84
|
return;
|
|
80
|
-
|
|
85
|
+
const log = (0, envelope_js_1.isLogEnvelope)(allowed);
|
|
86
|
+
if (this.queue.length + this.active >= exports.QUEUE_LIMIT ||
|
|
87
|
+
(log && this.logs >= exports.LOG_QUEUE_LIMIT)) {
|
|
81
88
|
this.options.logger.warn("Dropped an envelope: the send queue is full.");
|
|
82
89
|
this.lose(allowed, "queue_overflow");
|
|
83
90
|
return;
|
|
84
91
|
}
|
|
92
|
+
if (log)
|
|
93
|
+
this.logs++;
|
|
85
94
|
this.queue.push(allowed);
|
|
86
95
|
this.pump();
|
|
87
96
|
}
|
|
@@ -130,7 +139,7 @@ class Sender {
|
|
|
130
139
|
if (!(0, ratelimit_js_1.isRateLimited)(this.limits, category, now))
|
|
131
140
|
return true;
|
|
132
141
|
if (category !== "internal")
|
|
133
|
-
this.options.recordLoss("ratelimit_backoff", category);
|
|
142
|
+
this.options.recordLoss("ratelimit_backoff", category, (0, envelope_js_1.itemQuantity)(item));
|
|
134
143
|
return false;
|
|
135
144
|
});
|
|
136
145
|
if (items.length < envelope[1].length) {
|
|
@@ -140,13 +149,20 @@ class Sender {
|
|
|
140
149
|
}
|
|
141
150
|
pump() {
|
|
142
151
|
while (this.active < exports.CONCURRENCY && this.queue.length > 0) {
|
|
152
|
+
const waited = this.queue.shift();
|
|
153
|
+
const log = (0, envelope_js_1.isLogEnvelope)(waited);
|
|
143
154
|
// A 429 may have come in while it waited.
|
|
144
|
-
const next = this.withoutLimited(
|
|
145
|
-
if (!next)
|
|
155
|
+
const next = this.withoutLimited(waited);
|
|
156
|
+
if (!next) {
|
|
157
|
+
if (log)
|
|
158
|
+
this.logs--;
|
|
146
159
|
continue;
|
|
160
|
+
}
|
|
147
161
|
this.active++;
|
|
148
162
|
const done = () => {
|
|
149
163
|
this.active--;
|
|
164
|
+
if (log)
|
|
165
|
+
this.logs--;
|
|
150
166
|
this.pump();
|
|
151
167
|
};
|
|
152
168
|
void this.deliver(next).then(done, done);
|
|
@@ -211,7 +227,7 @@ class Sender {
|
|
|
211
227
|
const category = (0, envelope_js_1.itemCategory)(item);
|
|
212
228
|
// A lost client report is not reported again.
|
|
213
229
|
if (category !== "internal")
|
|
214
|
-
this.options.recordLoss(reason, category);
|
|
230
|
+
this.options.recordLoss(reason, category, (0, envelope_js_1.itemQuantity)(item));
|
|
215
231
|
}
|
|
216
232
|
}
|
|
217
233
|
}
|
package/dist/cjs/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/cjs/version.d.ts
CHANGED
package/dist/cjs/version.js
CHANGED
|
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.SDK_PACKAGE = exports.SDK_VERSION = exports.SDK_NAME = void 0;
|
|
4
4
|
exports.SDK_NAME = "statoss.javascript";
|
|
5
5
|
// Kept equal to package.json by a test.
|
|
6
|
-
exports.SDK_VERSION = "0.
|
|
6
|
+
exports.SDK_VERSION = "0.4.0";
|
|
7
7
|
exports.SDK_PACKAGE = "npm:@statoss/sdk";
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { type Logger } from "./logger.js";
|
|
|
2
2
|
import { type RuntimeInfo } from "./runtime.js";
|
|
3
3
|
import { type Scope } from "./scope.js";
|
|
4
4
|
import type { Breadcrumb, BreadcrumbHint, CaptureContext, Event, InitOptions } from "./types.js";
|
|
5
|
+
/** init's options as the client reads them: init may pass a schema. */
|
|
6
|
+
export type ClientOptions = InitOptions & {
|
|
7
|
+
schema?: unknown;
|
|
8
|
+
};
|
|
5
9
|
export declare class Client {
|
|
6
10
|
readonly logger: Logger;
|
|
7
11
|
readonly runtime: RuntimeInfo;
|
|
@@ -12,6 +16,8 @@ export declare class Client {
|
|
|
12
16
|
private readonly endpoint?;
|
|
13
17
|
private readonly sender?;
|
|
14
18
|
private readonly levels;
|
|
19
|
+
private readonly logLevels;
|
|
20
|
+
private readonly logsOn;
|
|
15
21
|
private readonly sampleRate;
|
|
16
22
|
private readonly maxBreadcrumbs;
|
|
17
23
|
private readonly discarded;
|
|
@@ -20,9 +26,22 @@ export declare class Client {
|
|
|
20
26
|
private reportTimer?;
|
|
21
27
|
private previousSignature?;
|
|
22
28
|
private closed;
|
|
23
|
-
|
|
29
|
+
private logBuffer;
|
|
30
|
+
private logChars;
|
|
31
|
+
private logTimer?;
|
|
32
|
+
private saidLogsOff;
|
|
33
|
+
/** The trace id of every log line: one per client, as Sentry's SDKs do without tracing. */
|
|
34
|
+
private logTraceId;
|
|
35
|
+
constructor(options: ClientOptions);
|
|
24
36
|
/** Installs the integrations that apply to this runtime. */
|
|
25
37
|
setup(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Keeps a log line for the next envelope of lines. The level is one of
|
|
40
|
+
* the six, a Sentry level, or the app's own (sent at its range's level
|
|
41
|
+
* with statoss.level); attributes go along typed, as Sentry's logger
|
|
42
|
+
* sends them.
|
|
43
|
+
*/
|
|
44
|
+
captureLog(level: unknown, message: unknown, attributes: unknown, scope: Scope): void;
|
|
26
45
|
captureException(error: unknown, context: CaptureContext | undefined, scope: Scope): string;
|
|
27
46
|
captureMessage(message: unknown, context: CaptureContext | undefined, scope: Scope): string;
|
|
28
47
|
captureEvent(event: Event, context: CaptureContext | undefined, scope: Scope): string;
|
|
@@ -37,8 +56,16 @@ export declare class Client {
|
|
|
37
56
|
flush(timeoutMs?: number): Promise<boolean>;
|
|
38
57
|
/** Flushes, then removes the integrations. Nothing is captured afterwards. */
|
|
39
58
|
close(timeoutMs?: number): Promise<boolean>;
|
|
40
|
-
/** Removes the integrations at once, without waiting. */
|
|
59
|
+
/** Removes the integrations at once, without waiting; log lines kept go out. */
|
|
41
60
|
teardown(): void;
|
|
61
|
+
private keepLog;
|
|
62
|
+
/** Sends the log lines kept, as one envelope. */
|
|
63
|
+
private sendLogs;
|
|
64
|
+
/**
|
|
65
|
+
* In Node, Bun and Deno: sends the lines kept when the process runs out
|
|
66
|
+
* of work, since the timer that would send them does not keep it alive.
|
|
67
|
+
*/
|
|
68
|
+
private sendLogsBeforeExit;
|
|
42
69
|
private flushReportsWhenHidden;
|
|
43
70
|
private capture;
|
|
44
71
|
/** Merges scope, context and options into the event, in Sentry's field order. */
|