datanotes 1.2.1 → 1.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/SECURITY.md +4 -1
- package/dist/api/index.cjs +194 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.js +194 -0
- package/dist/api/otlp.d.ts +65 -0
- package/dist/datanotes.cjs +261 -89
- package/package.json +1 -1
package/SECURITY.md
CHANGED
|
@@ -47,7 +47,10 @@ Nothing leaves the machine unless you turn it on:
|
|
|
47
47
|
- **Semantic search** sends note text to the embedding provider you configure (`semanticEnabled`,
|
|
48
48
|
off by default; provider `semanticProvider` / `semanticBaseUrl`). A local provider (Ollama,
|
|
49
49
|
LM Studio) keeps it on the machine.
|
|
50
|
-
- **Telemetry** sends logs to the OTLP endpoint you configure (`telemetryEnabled`, off by default)
|
|
50
|
+
- **Telemetry** sends logs to the OTLP endpoint you configure (`telemetryEnabled`, off by default):
|
|
51
|
+
operations, routes, note paths, actors, tool names and error messages (which may quote a value),
|
|
52
|
+
not note bodies. `telemetryHeaders` (an API key) is
|
|
53
|
+
stored in the settings file; `OTEL_EXPORTER_OTLP_HEADERS` keeps it out.
|
|
51
54
|
- **Webhooks** (created by the owner) post change events to the URLs they name.
|
|
52
55
|
- **Apps' MCP endpoints** (registered by the owner): the engine sends each app's `mcp_token`, and
|
|
53
56
|
the arguments and caller of every call of its tools, to the app's `mcp_url`, which may be remote.
|
package/dist/api/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(index_exports, {
|
|
|
26
26
|
DatanotesError: () => DatanotesError,
|
|
27
27
|
OPERATIONS: () => OPERATIONS,
|
|
28
28
|
OperationInputError: () => OperationInputError,
|
|
29
|
+
OtlpLogExporter: () => OtlpLogExporter,
|
|
29
30
|
ROUTE_PREFIX: () => ROUTE_PREFIX,
|
|
30
31
|
describeBatch: () => describeBatch,
|
|
31
32
|
describeRefusal: () => describeRefusal,
|
|
@@ -33,6 +34,8 @@ __export(index_exports, {
|
|
|
33
34
|
fetchTransport: () => fetchTransport,
|
|
34
35
|
operationRequest: () => operationRequest,
|
|
35
36
|
operationRoute: () => operationRoute,
|
|
37
|
+
otlpFromEnv: () => otlpFromEnv,
|
|
38
|
+
parseOtlpHeaders: () => parseOtlpHeaders,
|
|
36
39
|
resultOf: () => resultOf
|
|
37
40
|
});
|
|
38
41
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -3062,6 +3065,197 @@ function estimateTokens(text) {
|
|
|
3062
3065
|
return n;
|
|
3063
3066
|
}
|
|
3064
3067
|
|
|
3068
|
+
// src/api/otlp.ts
|
|
3069
|
+
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
3070
|
+
function attr(key, v) {
|
|
3071
|
+
if (v === void 0 || v === null) return null;
|
|
3072
|
+
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
3073
|
+
if (typeof v === "number") return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
3074
|
+
return { key, value: { stringValue: v } };
|
|
3075
|
+
}
|
|
3076
|
+
var attrs = (a) => Object.entries(a).map(([k, v]) => attr(k, v)).filter((x) => x !== null);
|
|
3077
|
+
var fetchSend = async (url, body, headers) => {
|
|
3078
|
+
const res = await fetch(url, { method: "POST", headers, body });
|
|
3079
|
+
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
3080
|
+
};
|
|
3081
|
+
var Pb = class {
|
|
3082
|
+
constructor() {
|
|
3083
|
+
this.parts = [];
|
|
3084
|
+
}
|
|
3085
|
+
varint(v) {
|
|
3086
|
+
let x = BigInt.asUintN(64, v);
|
|
3087
|
+
const b128 = BigInt(128);
|
|
3088
|
+
const low7 = BigInt(127);
|
|
3089
|
+
const seven = BigInt(7);
|
|
3090
|
+
while (x >= b128) {
|
|
3091
|
+
this.parts.push(Number(x & low7) | 128);
|
|
3092
|
+
x >>= seven;
|
|
3093
|
+
}
|
|
3094
|
+
this.parts.push(Number(x));
|
|
3095
|
+
}
|
|
3096
|
+
tag(field, wire) {
|
|
3097
|
+
this.varint(BigInt(field << 3 | wire));
|
|
3098
|
+
}
|
|
3099
|
+
uint(field, v) {
|
|
3100
|
+
this.tag(field, 0);
|
|
3101
|
+
this.varint(BigInt(v));
|
|
3102
|
+
return this;
|
|
3103
|
+
}
|
|
3104
|
+
fixed64(field, v) {
|
|
3105
|
+
this.tag(field, 1);
|
|
3106
|
+
for (let i = 0; i < 8; i++) this.parts.push(Number(v >> BigInt(8 * i) & BigInt(255)));
|
|
3107
|
+
return this;
|
|
3108
|
+
}
|
|
3109
|
+
double(field, v) {
|
|
3110
|
+
this.tag(field, 1);
|
|
3111
|
+
const b = new DataView(new ArrayBuffer(8));
|
|
3112
|
+
b.setFloat64(0, v, true);
|
|
3113
|
+
for (let i = 0; i < 8; i++) this.parts.push(b.getUint8(i));
|
|
3114
|
+
return this;
|
|
3115
|
+
}
|
|
3116
|
+
bytes(field, data) {
|
|
3117
|
+
this.tag(field, 2);
|
|
3118
|
+
this.varint(BigInt(data.length));
|
|
3119
|
+
for (const x of data) this.parts.push(x);
|
|
3120
|
+
return this;
|
|
3121
|
+
}
|
|
3122
|
+
string(field, s) {
|
|
3123
|
+
return this.bytes(field, new TextEncoder().encode(s));
|
|
3124
|
+
}
|
|
3125
|
+
message(field, m) {
|
|
3126
|
+
return this.bytes(field, m.finish());
|
|
3127
|
+
}
|
|
3128
|
+
finish() {
|
|
3129
|
+
return Uint8Array.from(this.parts);
|
|
3130
|
+
}
|
|
3131
|
+
};
|
|
3132
|
+
function pbValue(v) {
|
|
3133
|
+
if (typeof v === "string") return new Pb().string(1, v);
|
|
3134
|
+
if (typeof v === "boolean") return new Pb().uint(2, v ? 1 : 0);
|
|
3135
|
+
return Number.isInteger(v) ? new Pb().uint(3, BigInt(v)) : new Pb().double(4, v);
|
|
3136
|
+
}
|
|
3137
|
+
function pbAttrs(m, field, a) {
|
|
3138
|
+
for (const [k, v] of Object.entries(a)) {
|
|
3139
|
+
if (v === void 0 || v === null) continue;
|
|
3140
|
+
m.message(field, new Pb().string(1, k).message(2, pbValue(v)));
|
|
3141
|
+
}
|
|
3142
|
+
return m;
|
|
3143
|
+
}
|
|
3144
|
+
var OtlpLogExporter = class {
|
|
3145
|
+
constructor(o) {
|
|
3146
|
+
this.o = o;
|
|
3147
|
+
this.buffer = [];
|
|
3148
|
+
this.inflight = Promise.resolve();
|
|
3149
|
+
this.timer = null;
|
|
3150
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
3151
|
+
this.sent = 0;
|
|
3152
|
+
this.dropped = 0;
|
|
3153
|
+
const every = o.flushMs ?? 5e3;
|
|
3154
|
+
if (every > 0) {
|
|
3155
|
+
this.timer = setInterval(() => void this.flush(), every);
|
|
3156
|
+
this.timer.unref?.();
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
log(severity, body, attributes = {}) {
|
|
3160
|
+
const ms = (this.o.now ?? Date.now)();
|
|
3161
|
+
this.buffer.push({ timeUnixNano: `${Math.round(ms)}000000`, severity, body, attributes });
|
|
3162
|
+
if (this.buffer.length >= (this.o.maxBatch ?? 50)) void this.flush();
|
|
3163
|
+
}
|
|
3164
|
+
/** Send what is buffered. Never rejects. */
|
|
3165
|
+
flush() {
|
|
3166
|
+
if (this.buffer.length === 0) return this.inflight;
|
|
3167
|
+
const batch = this.buffer;
|
|
3168
|
+
this.buffer = [];
|
|
3169
|
+
const resource = { "service.name": this.o.serviceName, "service.version": this.o.serviceVersion, ...this.o.resource };
|
|
3170
|
+
const protobuf = (this.o.protocol ?? "http/protobuf") === "http/protobuf";
|
|
3171
|
+
const body = protobuf ? this.protobuf(resource, batch) : JSON.stringify({
|
|
3172
|
+
resourceLogs: [
|
|
3173
|
+
{
|
|
3174
|
+
resource: { attributes: attrs(resource) },
|
|
3175
|
+
scopeLogs: [
|
|
3176
|
+
{
|
|
3177
|
+
scope: { name: this.o.serviceName },
|
|
3178
|
+
logRecords: batch.map((r) => ({
|
|
3179
|
+
timeUnixNano: r.timeUnixNano,
|
|
3180
|
+
observedTimeUnixNano: r.timeUnixNano,
|
|
3181
|
+
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
3182
|
+
severityText: r.severity,
|
|
3183
|
+
body: { stringValue: r.body },
|
|
3184
|
+
attributes: attrs(r.attributes)
|
|
3185
|
+
}))
|
|
3186
|
+
}
|
|
3187
|
+
]
|
|
3188
|
+
}
|
|
3189
|
+
]
|
|
3190
|
+
});
|
|
3191
|
+
const url = `${this.o.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
3192
|
+
const send = this.o.send ?? fetchSend;
|
|
3193
|
+
this.inflight = this.inflight.then(
|
|
3194
|
+
() => send(url, body, { "Content-Type": protobuf ? "application/x-protobuf" : "application/json", ...this.o.headers ?? {} }).then(
|
|
3195
|
+
() => void (this.sent += batch.length),
|
|
3196
|
+
() => void (this.dropped += batch.length)
|
|
3197
|
+
)
|
|
3198
|
+
);
|
|
3199
|
+
return this.inflight;
|
|
3200
|
+
}
|
|
3201
|
+
protobuf(resource, batch) {
|
|
3202
|
+
const scope = new Pb().message(1, new Pb().string(1, this.o.serviceName));
|
|
3203
|
+
for (const r of batch) {
|
|
3204
|
+
const t = BigInt(r.timeUnixNano);
|
|
3205
|
+
const rec = new Pb().fixed64(1, t).uint(2, SEVERITY_NUMBER[r.severity]).string(3, r.severity).message(5, new Pb().string(1, r.body));
|
|
3206
|
+
pbAttrs(rec, 6, r.attributes).fixed64(11, t);
|
|
3207
|
+
scope.message(2, rec);
|
|
3208
|
+
}
|
|
3209
|
+
const resourceLogs = new Pb().message(1, pbAttrs(new Pb(), 1, resource)).message(2, scope);
|
|
3210
|
+
return new Pb().message(1, resourceLogs).finish();
|
|
3211
|
+
}
|
|
3212
|
+
/** Stop the timer and send what is left. */
|
|
3213
|
+
async close() {
|
|
3214
|
+
if (this.timer) clearInterval(this.timer);
|
|
3215
|
+
this.timer = null;
|
|
3216
|
+
await this.flush();
|
|
3217
|
+
}
|
|
3218
|
+
};
|
|
3219
|
+
function parseOtlpHeaders(text) {
|
|
3220
|
+
const out = {};
|
|
3221
|
+
for (const part of (text ?? "").split(",")) {
|
|
3222
|
+
const i = part.indexOf("=");
|
|
3223
|
+
if (i <= 0) continue;
|
|
3224
|
+
const key = part.slice(0, i).trim();
|
|
3225
|
+
const value = part.slice(i + 1).trim();
|
|
3226
|
+
if (!key) continue;
|
|
3227
|
+
try {
|
|
3228
|
+
out[key] = decodeURIComponent(value);
|
|
3229
|
+
} catch {
|
|
3230
|
+
out[key] = value;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
return out;
|
|
3234
|
+
}
|
|
3235
|
+
var parseResourceAttributes = parseOtlpHeaders;
|
|
3236
|
+
var truthy = (v) => (v ?? "").trim().toLowerCase() === "true";
|
|
3237
|
+
function otlpFromEnv(env, defaults) {
|
|
3238
|
+
if (truthy(env.OTEL_SDK_DISABLED) || (env.OTEL_LOGS_EXPORTER ?? "").trim().toLowerCase() === "none") return null;
|
|
3239
|
+
const logs = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT?.trim();
|
|
3240
|
+
const base = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
3241
|
+
const endpoint = logs ? logs.replace(/\/v1\/logs\/?$/, "") : base;
|
|
3242
|
+
if (!endpoint) return null;
|
|
3243
|
+
const protocol = otlpProtocol(env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL);
|
|
3244
|
+
const resource = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
|
|
3245
|
+
return {
|
|
3246
|
+
endpoint,
|
|
3247
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || resource["service.name"] || defaults.serviceName,
|
|
3248
|
+
...defaults.serviceVersion ? { serviceVersion: defaults.serviceVersion } : {},
|
|
3249
|
+
...protocol ? { protocol } : {},
|
|
3250
|
+
...Object.keys(resource).length ? { resource } : {},
|
|
3251
|
+
headers: { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_LOGS_HEADERS) }
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
function otlpProtocol(v) {
|
|
3255
|
+
const p = (v ?? "").trim().toLowerCase();
|
|
3256
|
+
return p === "http/protobuf" || p === "http/json" ? p : void 0;
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3065
3259
|
// src/api/client.ts
|
|
3066
3260
|
var ACTOR_HEADER = "X-Datanotes-Actor";
|
|
3067
3261
|
var DatanotesError = class extends Error {
|
package/dist/api/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export { OPERATIONS, type OperationDef } from "./catalog";
|
|
|
6
6
|
export { operationRequest, operationRoute, CALLABLE_OPERATIONS, OperationInputError, ROUTE_PREFIX, type OperationRequest } from "./requests";
|
|
7
7
|
export { resultOf, describeRefusal, describeBatch, type OperationResult } from "./results";
|
|
8
8
|
export { estimateTokens } from "./tokens";
|
|
9
|
+
export { OtlpLogExporter, otlpFromEnv, parseOtlpHeaders, type OtlpOptions, type Severity, type AttrValue } from "./otlp";
|
|
9
10
|
export { DatanotesClient, DatanotesError, fetchTransport, ACTOR_HEADER, type ActorInfo, type AppTool, type ClientOptions, type HttpRequest, type HttpResponse, type ToolCallRecord, type Transport, } from "./client";
|
package/dist/api/index.js
CHANGED
|
@@ -3023,6 +3023,197 @@ function estimateTokens(text) {
|
|
|
3023
3023
|
return n;
|
|
3024
3024
|
}
|
|
3025
3025
|
|
|
3026
|
+
// src/api/otlp.ts
|
|
3027
|
+
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
3028
|
+
function attr(key, v) {
|
|
3029
|
+
if (v === void 0 || v === null) return null;
|
|
3030
|
+
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
3031
|
+
if (typeof v === "number") return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
3032
|
+
return { key, value: { stringValue: v } };
|
|
3033
|
+
}
|
|
3034
|
+
var attrs = (a) => Object.entries(a).map(([k, v]) => attr(k, v)).filter((x) => x !== null);
|
|
3035
|
+
var fetchSend = async (url, body, headers) => {
|
|
3036
|
+
const res = await fetch(url, { method: "POST", headers, body });
|
|
3037
|
+
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
3038
|
+
};
|
|
3039
|
+
var Pb = class {
|
|
3040
|
+
constructor() {
|
|
3041
|
+
this.parts = [];
|
|
3042
|
+
}
|
|
3043
|
+
varint(v) {
|
|
3044
|
+
let x = BigInt.asUintN(64, v);
|
|
3045
|
+
const b128 = BigInt(128);
|
|
3046
|
+
const low7 = BigInt(127);
|
|
3047
|
+
const seven = BigInt(7);
|
|
3048
|
+
while (x >= b128) {
|
|
3049
|
+
this.parts.push(Number(x & low7) | 128);
|
|
3050
|
+
x >>= seven;
|
|
3051
|
+
}
|
|
3052
|
+
this.parts.push(Number(x));
|
|
3053
|
+
}
|
|
3054
|
+
tag(field, wire) {
|
|
3055
|
+
this.varint(BigInt(field << 3 | wire));
|
|
3056
|
+
}
|
|
3057
|
+
uint(field, v) {
|
|
3058
|
+
this.tag(field, 0);
|
|
3059
|
+
this.varint(BigInt(v));
|
|
3060
|
+
return this;
|
|
3061
|
+
}
|
|
3062
|
+
fixed64(field, v) {
|
|
3063
|
+
this.tag(field, 1);
|
|
3064
|
+
for (let i = 0; i < 8; i++) this.parts.push(Number(v >> BigInt(8 * i) & BigInt(255)));
|
|
3065
|
+
return this;
|
|
3066
|
+
}
|
|
3067
|
+
double(field, v) {
|
|
3068
|
+
this.tag(field, 1);
|
|
3069
|
+
const b = new DataView(new ArrayBuffer(8));
|
|
3070
|
+
b.setFloat64(0, v, true);
|
|
3071
|
+
for (let i = 0; i < 8; i++) this.parts.push(b.getUint8(i));
|
|
3072
|
+
return this;
|
|
3073
|
+
}
|
|
3074
|
+
bytes(field, data) {
|
|
3075
|
+
this.tag(field, 2);
|
|
3076
|
+
this.varint(BigInt(data.length));
|
|
3077
|
+
for (const x of data) this.parts.push(x);
|
|
3078
|
+
return this;
|
|
3079
|
+
}
|
|
3080
|
+
string(field, s) {
|
|
3081
|
+
return this.bytes(field, new TextEncoder().encode(s));
|
|
3082
|
+
}
|
|
3083
|
+
message(field, m) {
|
|
3084
|
+
return this.bytes(field, m.finish());
|
|
3085
|
+
}
|
|
3086
|
+
finish() {
|
|
3087
|
+
return Uint8Array.from(this.parts);
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
function pbValue(v) {
|
|
3091
|
+
if (typeof v === "string") return new Pb().string(1, v);
|
|
3092
|
+
if (typeof v === "boolean") return new Pb().uint(2, v ? 1 : 0);
|
|
3093
|
+
return Number.isInteger(v) ? new Pb().uint(3, BigInt(v)) : new Pb().double(4, v);
|
|
3094
|
+
}
|
|
3095
|
+
function pbAttrs(m, field, a) {
|
|
3096
|
+
for (const [k, v] of Object.entries(a)) {
|
|
3097
|
+
if (v === void 0 || v === null) continue;
|
|
3098
|
+
m.message(field, new Pb().string(1, k).message(2, pbValue(v)));
|
|
3099
|
+
}
|
|
3100
|
+
return m;
|
|
3101
|
+
}
|
|
3102
|
+
var OtlpLogExporter = class {
|
|
3103
|
+
constructor(o) {
|
|
3104
|
+
this.o = o;
|
|
3105
|
+
this.buffer = [];
|
|
3106
|
+
this.inflight = Promise.resolve();
|
|
3107
|
+
this.timer = null;
|
|
3108
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
3109
|
+
this.sent = 0;
|
|
3110
|
+
this.dropped = 0;
|
|
3111
|
+
const every = o.flushMs ?? 5e3;
|
|
3112
|
+
if (every > 0) {
|
|
3113
|
+
this.timer = setInterval(() => void this.flush(), every);
|
|
3114
|
+
this.timer.unref?.();
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
log(severity, body, attributes = {}) {
|
|
3118
|
+
const ms = (this.o.now ?? Date.now)();
|
|
3119
|
+
this.buffer.push({ timeUnixNano: `${Math.round(ms)}000000`, severity, body, attributes });
|
|
3120
|
+
if (this.buffer.length >= (this.o.maxBatch ?? 50)) void this.flush();
|
|
3121
|
+
}
|
|
3122
|
+
/** Send what is buffered. Never rejects. */
|
|
3123
|
+
flush() {
|
|
3124
|
+
if (this.buffer.length === 0) return this.inflight;
|
|
3125
|
+
const batch = this.buffer;
|
|
3126
|
+
this.buffer = [];
|
|
3127
|
+
const resource = { "service.name": this.o.serviceName, "service.version": this.o.serviceVersion, ...this.o.resource };
|
|
3128
|
+
const protobuf = (this.o.protocol ?? "http/protobuf") === "http/protobuf";
|
|
3129
|
+
const body = protobuf ? this.protobuf(resource, batch) : JSON.stringify({
|
|
3130
|
+
resourceLogs: [
|
|
3131
|
+
{
|
|
3132
|
+
resource: { attributes: attrs(resource) },
|
|
3133
|
+
scopeLogs: [
|
|
3134
|
+
{
|
|
3135
|
+
scope: { name: this.o.serviceName },
|
|
3136
|
+
logRecords: batch.map((r) => ({
|
|
3137
|
+
timeUnixNano: r.timeUnixNano,
|
|
3138
|
+
observedTimeUnixNano: r.timeUnixNano,
|
|
3139
|
+
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
3140
|
+
severityText: r.severity,
|
|
3141
|
+
body: { stringValue: r.body },
|
|
3142
|
+
attributes: attrs(r.attributes)
|
|
3143
|
+
}))
|
|
3144
|
+
}
|
|
3145
|
+
]
|
|
3146
|
+
}
|
|
3147
|
+
]
|
|
3148
|
+
});
|
|
3149
|
+
const url = `${this.o.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
3150
|
+
const send = this.o.send ?? fetchSend;
|
|
3151
|
+
this.inflight = this.inflight.then(
|
|
3152
|
+
() => send(url, body, { "Content-Type": protobuf ? "application/x-protobuf" : "application/json", ...this.o.headers ?? {} }).then(
|
|
3153
|
+
() => void (this.sent += batch.length),
|
|
3154
|
+
() => void (this.dropped += batch.length)
|
|
3155
|
+
)
|
|
3156
|
+
);
|
|
3157
|
+
return this.inflight;
|
|
3158
|
+
}
|
|
3159
|
+
protobuf(resource, batch) {
|
|
3160
|
+
const scope = new Pb().message(1, new Pb().string(1, this.o.serviceName));
|
|
3161
|
+
for (const r of batch) {
|
|
3162
|
+
const t = BigInt(r.timeUnixNano);
|
|
3163
|
+
const rec = new Pb().fixed64(1, t).uint(2, SEVERITY_NUMBER[r.severity]).string(3, r.severity).message(5, new Pb().string(1, r.body));
|
|
3164
|
+
pbAttrs(rec, 6, r.attributes).fixed64(11, t);
|
|
3165
|
+
scope.message(2, rec);
|
|
3166
|
+
}
|
|
3167
|
+
const resourceLogs = new Pb().message(1, pbAttrs(new Pb(), 1, resource)).message(2, scope);
|
|
3168
|
+
return new Pb().message(1, resourceLogs).finish();
|
|
3169
|
+
}
|
|
3170
|
+
/** Stop the timer and send what is left. */
|
|
3171
|
+
async close() {
|
|
3172
|
+
if (this.timer) clearInterval(this.timer);
|
|
3173
|
+
this.timer = null;
|
|
3174
|
+
await this.flush();
|
|
3175
|
+
}
|
|
3176
|
+
};
|
|
3177
|
+
function parseOtlpHeaders(text) {
|
|
3178
|
+
const out = {};
|
|
3179
|
+
for (const part of (text ?? "").split(",")) {
|
|
3180
|
+
const i = part.indexOf("=");
|
|
3181
|
+
if (i <= 0) continue;
|
|
3182
|
+
const key = part.slice(0, i).trim();
|
|
3183
|
+
const value = part.slice(i + 1).trim();
|
|
3184
|
+
if (!key) continue;
|
|
3185
|
+
try {
|
|
3186
|
+
out[key] = decodeURIComponent(value);
|
|
3187
|
+
} catch {
|
|
3188
|
+
out[key] = value;
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
return out;
|
|
3192
|
+
}
|
|
3193
|
+
var parseResourceAttributes = parseOtlpHeaders;
|
|
3194
|
+
var truthy = (v) => (v ?? "").trim().toLowerCase() === "true";
|
|
3195
|
+
function otlpFromEnv(env, defaults) {
|
|
3196
|
+
if (truthy(env.OTEL_SDK_DISABLED) || (env.OTEL_LOGS_EXPORTER ?? "").trim().toLowerCase() === "none") return null;
|
|
3197
|
+
const logs = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT?.trim();
|
|
3198
|
+
const base = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
3199
|
+
const endpoint = logs ? logs.replace(/\/v1\/logs\/?$/, "") : base;
|
|
3200
|
+
if (!endpoint) return null;
|
|
3201
|
+
const protocol = otlpProtocol(env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL);
|
|
3202
|
+
const resource = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
|
|
3203
|
+
return {
|
|
3204
|
+
endpoint,
|
|
3205
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || resource["service.name"] || defaults.serviceName,
|
|
3206
|
+
...defaults.serviceVersion ? { serviceVersion: defaults.serviceVersion } : {},
|
|
3207
|
+
...protocol ? { protocol } : {},
|
|
3208
|
+
...Object.keys(resource).length ? { resource } : {},
|
|
3209
|
+
headers: { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_LOGS_HEADERS) }
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
3212
|
+
function otlpProtocol(v) {
|
|
3213
|
+
const p = (v ?? "").trim().toLowerCase();
|
|
3214
|
+
return p === "http/protobuf" || p === "http/json" ? p : void 0;
|
|
3215
|
+
}
|
|
3216
|
+
|
|
3026
3217
|
// src/api/client.ts
|
|
3027
3218
|
var ACTOR_HEADER = "X-Datanotes-Actor";
|
|
3028
3219
|
var DatanotesError = class extends Error {
|
|
@@ -3130,6 +3321,7 @@ export {
|
|
|
3130
3321
|
DatanotesError,
|
|
3131
3322
|
OPERATIONS,
|
|
3132
3323
|
OperationInputError,
|
|
3324
|
+
OtlpLogExporter,
|
|
3133
3325
|
ROUTE_PREFIX,
|
|
3134
3326
|
describeBatch,
|
|
3135
3327
|
describeRefusal,
|
|
@@ -3137,5 +3329,7 @@ export {
|
|
|
3137
3329
|
fetchTransport,
|
|
3138
3330
|
operationRequest,
|
|
3139
3331
|
operationRoute,
|
|
3332
|
+
otlpFromEnv,
|
|
3333
|
+
parseOtlpHeaders,
|
|
3140
3334
|
resultOf
|
|
3141
3335
|
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry log export over OTLP/HTTP, vendor-neutral: records are buffered and POSTed to
|
|
3
|
+
* `<endpoint>/v1/logs` as `http/protobuf` (the OTLP default) or `http/json`, which every
|
|
4
|
+
* OpenTelemetry collector and OTLP backend reads. No dependency, no SDK. Export failures are
|
|
5
|
+
* counted, never thrown: logging must not break the program. (OTLP over gRPC is not supported:
|
|
6
|
+
* use the collector's HTTP port, 4318 by default.)
|
|
7
|
+
*
|
|
8
|
+
* Configure it in code, or from the standard OpenTelemetry variables with `otlpFromEnv`.
|
|
9
|
+
*/
|
|
10
|
+
export type Severity = "DEBUG" | "INFO" | "WARN" | "ERROR";
|
|
11
|
+
export type AttrValue = string | number | boolean | null | undefined;
|
|
12
|
+
export type OtlpProtocol = "http/protobuf" | "http/json";
|
|
13
|
+
export interface OtlpOptions {
|
|
14
|
+
/** Collector base URL, e.g. `http://127.0.0.1:4318` (`/v1/logs` is added). */
|
|
15
|
+
endpoint: string;
|
|
16
|
+
/** `service.name` of the records. */
|
|
17
|
+
serviceName: string;
|
|
18
|
+
serviceVersion?: string;
|
|
19
|
+
/** `http/protobuf` (default) or `http/json`. */
|
|
20
|
+
protocol?: OtlpProtocol;
|
|
21
|
+
/** Extra headers, e.g. an API key or `Authorization`, as the backend asks. */
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
/** More resource attributes (e.g. `host.name`). */
|
|
24
|
+
resource?: Record<string, AttrValue>;
|
|
25
|
+
/** Send at most this many records per request (default 50); a full buffer is sent at once. */
|
|
26
|
+
maxBatch?: number;
|
|
27
|
+
/** Send the buffer every this many ms (default 5000; 0: only on `flush`). */
|
|
28
|
+
flushMs?: number;
|
|
29
|
+
/** POST a body; resolves on 2xx (default: fetch). `headers` include the Content-Type. */
|
|
30
|
+
send?: (url: string, body: string | Uint8Array, headers: Record<string, string>) => Promise<void>;
|
|
31
|
+
now?: () => number;
|
|
32
|
+
}
|
|
33
|
+
export declare class OtlpLogExporter {
|
|
34
|
+
private readonly o;
|
|
35
|
+
private buffer;
|
|
36
|
+
private inflight;
|
|
37
|
+
private timer;
|
|
38
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
39
|
+
sent: number;
|
|
40
|
+
dropped: number;
|
|
41
|
+
constructor(o: OtlpOptions);
|
|
42
|
+
log(severity: Severity, body: string, attributes?: Record<string, AttrValue>): void;
|
|
43
|
+
/** Send what is buffered. Never rejects. */
|
|
44
|
+
flush(): Promise<void>;
|
|
45
|
+
private protobuf;
|
|
46
|
+
/** Stop the timer and send what is left. */
|
|
47
|
+
close(): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
/** `k1=v1,k2=v2` (values URL-encoded), the format of `OTEL_EXPORTER_OTLP_HEADERS`. */
|
|
50
|
+
export declare function parseOtlpHeaders(text: string | undefined): Record<string, string>;
|
|
51
|
+
/** `k1=v1,k2=v2` resource attributes, the format of `OTEL_RESOURCE_ATTRIBUTES`. */
|
|
52
|
+
export declare const parseResourceAttributes: typeof parseOtlpHeaders;
|
|
53
|
+
/**
|
|
54
|
+
* Exporter options from the standard OpenTelemetry variables, or null when export is off (no
|
|
55
|
+
* endpoint, `OTEL_SDK_DISABLED=true` or `OTEL_LOGS_EXPORTER=none`):
|
|
56
|
+
* `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` (full URL of /v1/logs) or `OTEL_EXPORTER_OTLP_ENDPOINT`;
|
|
57
|
+
* `OTEL_EXPORTER_OTLP_[LOGS_]HEADERS`; `OTEL_EXPORTER_OTLP_[LOGS_]PROTOCOL` (`http/protobuf`,
|
|
58
|
+
* `http/json`); `OTEL_SERVICE_NAME`; `OTEL_RESOURCE_ATTRIBUTES`.
|
|
59
|
+
*/
|
|
60
|
+
export declare function otlpFromEnv(env: Record<string, string | undefined>, defaults: {
|
|
61
|
+
serviceName: string;
|
|
62
|
+
serviceVersion?: string;
|
|
63
|
+
}): OtlpOptions | null;
|
|
64
|
+
/** `http/protobuf` or `http/json` from a setting or variable; undefined for anything else. */
|
|
65
|
+
export declare function otlpProtocol(v: string | undefined): OtlpProtocol | undefined;
|
package/dist/datanotes.cjs
CHANGED
|
@@ -7425,6 +7425,8 @@ var DEFAULT_SETTINGS = {
|
|
|
7425
7425
|
telemetryEnabled: false,
|
|
7426
7426
|
telemetryEndpoint: "http://127.0.0.1:4318",
|
|
7427
7427
|
telemetryServiceName: "datanotes",
|
|
7428
|
+
telemetryHeaders: {},
|
|
7429
|
+
telemetryProtocol: "http/protobuf",
|
|
7428
7430
|
autoRecompute: true,
|
|
7429
7431
|
viewNotes: true,
|
|
7430
7432
|
semanticEnabled: false,
|
|
@@ -7450,6 +7452,8 @@ var KEYS = [
|
|
|
7450
7452
|
"telemetryEnabled",
|
|
7451
7453
|
"telemetryEndpoint",
|
|
7452
7454
|
"telemetryServiceName",
|
|
7455
|
+
"telemetryHeaders",
|
|
7456
|
+
"telemetryProtocol",
|
|
7453
7457
|
"autoRecompute",
|
|
7454
7458
|
"viewNotes",
|
|
7455
7459
|
"semanticEnabled",
|
|
@@ -7500,6 +7504,18 @@ function mergeWithDefaults(loaded) {
|
|
|
7500
7504
|
}
|
|
7501
7505
|
break;
|
|
7502
7506
|
}
|
|
7507
|
+
case "telemetryProtocol": {
|
|
7508
|
+
if (raw === "http/protobuf" || raw === "http/json") out.telemetryProtocol = raw;
|
|
7509
|
+
break;
|
|
7510
|
+
}
|
|
7511
|
+
case "telemetryHeaders": {
|
|
7512
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7513
|
+
out.telemetryHeaders = Object.fromEntries(
|
|
7514
|
+
Object.entries(raw).filter(([k, v]) => /^[A-Za-z0-9!#$%&'*+.^_`|~-]+$/.test(k) && typeof v === "string")
|
|
7515
|
+
);
|
|
7516
|
+
}
|
|
7517
|
+
break;
|
|
7518
|
+
}
|
|
7503
7519
|
case "telemetryServiceName": {
|
|
7504
7520
|
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
7505
7521
|
out.telemetryServiceName = raw.trim();
|
|
@@ -7634,89 +7650,12 @@ async function storeRename(app, from, to) {
|
|
|
7634
7650
|
}
|
|
7635
7651
|
|
|
7636
7652
|
// src/core/telemetry.ts
|
|
7637
|
-
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
7638
7653
|
var NOOP_TELEMETRY = {
|
|
7639
7654
|
log: () => {
|
|
7640
7655
|
},
|
|
7641
7656
|
flush: async () => {
|
|
7642
7657
|
}
|
|
7643
7658
|
};
|
|
7644
|
-
function otlpAttr(key, v) {
|
|
7645
|
-
if (v === void 0 || v === null) return null;
|
|
7646
|
-
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
7647
|
-
if (typeof v === "number") {
|
|
7648
|
-
return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
7649
|
-
}
|
|
7650
|
-
return { key, value: { stringValue: v } };
|
|
7651
|
-
}
|
|
7652
|
-
function attrs(a) {
|
|
7653
|
-
return Object.entries(a).map(([k, v]) => otlpAttr(k, v)).filter((x) => x != null);
|
|
7654
|
-
}
|
|
7655
|
-
function otlpLogsPayload(serviceName, records, resource = {}) {
|
|
7656
|
-
return JSON.stringify({
|
|
7657
|
-
resourceLogs: [
|
|
7658
|
-
{
|
|
7659
|
-
resource: { attributes: attrs({ "service.name": serviceName, ...resource }) },
|
|
7660
|
-
scopeLogs: [
|
|
7661
|
-
{
|
|
7662
|
-
scope: { name: "obsidian-datanotes" },
|
|
7663
|
-
logRecords: records.map((r) => ({
|
|
7664
|
-
timeUnixNano: r.timeUnixNano,
|
|
7665
|
-
observedTimeUnixNano: r.timeUnixNano,
|
|
7666
|
-
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
7667
|
-
severityText: r.severity,
|
|
7668
|
-
body: { stringValue: r.body },
|
|
7669
|
-
attributes: attrs(r.attributes)
|
|
7670
|
-
}))
|
|
7671
|
-
}
|
|
7672
|
-
]
|
|
7673
|
-
}
|
|
7674
|
-
]
|
|
7675
|
-
});
|
|
7676
|
-
}
|
|
7677
|
-
var OtlpLogger = class {
|
|
7678
|
-
constructor(opts) {
|
|
7679
|
-
this.opts = opts;
|
|
7680
|
-
this.buffer = [];
|
|
7681
|
-
this.inflight = Promise.resolve();
|
|
7682
|
-
this.dropped = 0;
|
|
7683
|
-
this.sent = 0;
|
|
7684
|
-
}
|
|
7685
|
-
log(severity, body, attributes = {}) {
|
|
7686
|
-
if (!this.opts.getSettings().telemetryEnabled) return;
|
|
7687
|
-
const ms = (this.opts.now ?? Date.now)();
|
|
7688
|
-
this.buffer.push({
|
|
7689
|
-
timeUnixNano: `${Math.round(ms)}000000`,
|
|
7690
|
-
severity,
|
|
7691
|
-
body,
|
|
7692
|
-
attributes
|
|
7693
|
-
});
|
|
7694
|
-
if (this.buffer.length >= (this.opts.maxBatch ?? 50)) void this.flush();
|
|
7695
|
-
}
|
|
7696
|
-
/** Send buffered records. Never rejects. */
|
|
7697
|
-
flush() {
|
|
7698
|
-
const settings = this.opts.getSettings();
|
|
7699
|
-
if (!settings.telemetryEnabled || this.buffer.length === 0) {
|
|
7700
|
-
this.buffer = [];
|
|
7701
|
-
return this.inflight;
|
|
7702
|
-
}
|
|
7703
|
-
const batch = this.buffer;
|
|
7704
|
-
this.buffer = [];
|
|
7705
|
-
const url = `${settings.telemetryEndpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
7706
|
-
const body = otlpLogsPayload(settings.telemetryServiceName, batch, this.opts.resource);
|
|
7707
|
-
this.inflight = this.inflight.then(
|
|
7708
|
-
() => this.opts.transport(url, body).then(
|
|
7709
|
-
() => {
|
|
7710
|
-
this.sent += batch.length;
|
|
7711
|
-
},
|
|
7712
|
-
() => {
|
|
7713
|
-
this.dropped += batch.length;
|
|
7714
|
-
}
|
|
7715
|
-
)
|
|
7716
|
-
);
|
|
7717
|
-
return this.inflight;
|
|
7718
|
-
}
|
|
7719
|
-
};
|
|
7720
7659
|
var current = NOOP_TELEMETRY;
|
|
7721
7660
|
function setTelemetry(t) {
|
|
7722
7661
|
current = t;
|
|
@@ -15250,6 +15189,246 @@ var SemanticIndex = class {
|
|
|
15250
15189
|
}
|
|
15251
15190
|
};
|
|
15252
15191
|
|
|
15192
|
+
// src/api/otlp.ts
|
|
15193
|
+
var SEVERITY_NUMBER = { DEBUG: 5, INFO: 9, WARN: 13, ERROR: 17 };
|
|
15194
|
+
function attr(key, v) {
|
|
15195
|
+
if (v === void 0 || v === null) return null;
|
|
15196
|
+
if (typeof v === "boolean") return { key, value: { boolValue: v } };
|
|
15197
|
+
if (typeof v === "number") return Number.isInteger(v) ? { key, value: { intValue: String(v) } } : { key, value: { doubleValue: v } };
|
|
15198
|
+
return { key, value: { stringValue: v } };
|
|
15199
|
+
}
|
|
15200
|
+
var attrs = (a) => Object.entries(a).map(([k, v]) => attr(k, v)).filter((x) => x !== null);
|
|
15201
|
+
var fetchSend = async (url, body, headers) => {
|
|
15202
|
+
const res = await fetch(url, { method: "POST", headers, body });
|
|
15203
|
+
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
15204
|
+
};
|
|
15205
|
+
var Pb = class {
|
|
15206
|
+
constructor() {
|
|
15207
|
+
this.parts = [];
|
|
15208
|
+
}
|
|
15209
|
+
varint(v) {
|
|
15210
|
+
let x = BigInt.asUintN(64, v);
|
|
15211
|
+
const b128 = BigInt(128);
|
|
15212
|
+
const low7 = BigInt(127);
|
|
15213
|
+
const seven = BigInt(7);
|
|
15214
|
+
while (x >= b128) {
|
|
15215
|
+
this.parts.push(Number(x & low7) | 128);
|
|
15216
|
+
x >>= seven;
|
|
15217
|
+
}
|
|
15218
|
+
this.parts.push(Number(x));
|
|
15219
|
+
}
|
|
15220
|
+
tag(field, wire) {
|
|
15221
|
+
this.varint(BigInt(field << 3 | wire));
|
|
15222
|
+
}
|
|
15223
|
+
uint(field, v) {
|
|
15224
|
+
this.tag(field, 0);
|
|
15225
|
+
this.varint(BigInt(v));
|
|
15226
|
+
return this;
|
|
15227
|
+
}
|
|
15228
|
+
fixed64(field, v) {
|
|
15229
|
+
this.tag(field, 1);
|
|
15230
|
+
for (let i = 0; i < 8; i++) this.parts.push(Number(v >> BigInt(8 * i) & BigInt(255)));
|
|
15231
|
+
return this;
|
|
15232
|
+
}
|
|
15233
|
+
double(field, v) {
|
|
15234
|
+
this.tag(field, 1);
|
|
15235
|
+
const b = new DataView(new ArrayBuffer(8));
|
|
15236
|
+
b.setFloat64(0, v, true);
|
|
15237
|
+
for (let i = 0; i < 8; i++) this.parts.push(b.getUint8(i));
|
|
15238
|
+
return this;
|
|
15239
|
+
}
|
|
15240
|
+
bytes(field, data) {
|
|
15241
|
+
this.tag(field, 2);
|
|
15242
|
+
this.varint(BigInt(data.length));
|
|
15243
|
+
for (const x of data) this.parts.push(x);
|
|
15244
|
+
return this;
|
|
15245
|
+
}
|
|
15246
|
+
string(field, s) {
|
|
15247
|
+
return this.bytes(field, new TextEncoder().encode(s));
|
|
15248
|
+
}
|
|
15249
|
+
message(field, m) {
|
|
15250
|
+
return this.bytes(field, m.finish());
|
|
15251
|
+
}
|
|
15252
|
+
finish() {
|
|
15253
|
+
return Uint8Array.from(this.parts);
|
|
15254
|
+
}
|
|
15255
|
+
};
|
|
15256
|
+
function pbValue(v) {
|
|
15257
|
+
if (typeof v === "string") return new Pb().string(1, v);
|
|
15258
|
+
if (typeof v === "boolean") return new Pb().uint(2, v ? 1 : 0);
|
|
15259
|
+
return Number.isInteger(v) ? new Pb().uint(3, BigInt(v)) : new Pb().double(4, v);
|
|
15260
|
+
}
|
|
15261
|
+
function pbAttrs(m, field, a) {
|
|
15262
|
+
for (const [k, v] of Object.entries(a)) {
|
|
15263
|
+
if (v === void 0 || v === null) continue;
|
|
15264
|
+
m.message(field, new Pb().string(1, k).message(2, pbValue(v)));
|
|
15265
|
+
}
|
|
15266
|
+
return m;
|
|
15267
|
+
}
|
|
15268
|
+
var OtlpLogExporter = class {
|
|
15269
|
+
constructor(o) {
|
|
15270
|
+
this.o = o;
|
|
15271
|
+
this.buffer = [];
|
|
15272
|
+
this.inflight = Promise.resolve();
|
|
15273
|
+
this.timer = null;
|
|
15274
|
+
/** Records sent, and records lost because the collector did not take them. */
|
|
15275
|
+
this.sent = 0;
|
|
15276
|
+
this.dropped = 0;
|
|
15277
|
+
const every = o.flushMs ?? 5e3;
|
|
15278
|
+
if (every > 0) {
|
|
15279
|
+
this.timer = setInterval(() => void this.flush(), every);
|
|
15280
|
+
this.timer.unref?.();
|
|
15281
|
+
}
|
|
15282
|
+
}
|
|
15283
|
+
log(severity, body, attributes = {}) {
|
|
15284
|
+
const ms = (this.o.now ?? Date.now)();
|
|
15285
|
+
this.buffer.push({ timeUnixNano: `${Math.round(ms)}000000`, severity, body, attributes });
|
|
15286
|
+
if (this.buffer.length >= (this.o.maxBatch ?? 50)) void this.flush();
|
|
15287
|
+
}
|
|
15288
|
+
/** Send what is buffered. Never rejects. */
|
|
15289
|
+
flush() {
|
|
15290
|
+
if (this.buffer.length === 0) return this.inflight;
|
|
15291
|
+
const batch = this.buffer;
|
|
15292
|
+
this.buffer = [];
|
|
15293
|
+
const resource = { "service.name": this.o.serviceName, "service.version": this.o.serviceVersion, ...this.o.resource };
|
|
15294
|
+
const protobuf = (this.o.protocol ?? "http/protobuf") === "http/protobuf";
|
|
15295
|
+
const body = protobuf ? this.protobuf(resource, batch) : JSON.stringify({
|
|
15296
|
+
resourceLogs: [
|
|
15297
|
+
{
|
|
15298
|
+
resource: { attributes: attrs(resource) },
|
|
15299
|
+
scopeLogs: [
|
|
15300
|
+
{
|
|
15301
|
+
scope: { name: this.o.serviceName },
|
|
15302
|
+
logRecords: batch.map((r) => ({
|
|
15303
|
+
timeUnixNano: r.timeUnixNano,
|
|
15304
|
+
observedTimeUnixNano: r.timeUnixNano,
|
|
15305
|
+
severityNumber: SEVERITY_NUMBER[r.severity],
|
|
15306
|
+
severityText: r.severity,
|
|
15307
|
+
body: { stringValue: r.body },
|
|
15308
|
+
attributes: attrs(r.attributes)
|
|
15309
|
+
}))
|
|
15310
|
+
}
|
|
15311
|
+
]
|
|
15312
|
+
}
|
|
15313
|
+
]
|
|
15314
|
+
});
|
|
15315
|
+
const url = `${this.o.endpoint.replace(/\/+$/, "")}/v1/logs`;
|
|
15316
|
+
const send2 = this.o.send ?? fetchSend;
|
|
15317
|
+
this.inflight = this.inflight.then(
|
|
15318
|
+
() => send2(url, body, { "Content-Type": protobuf ? "application/x-protobuf" : "application/json", ...this.o.headers ?? {} }).then(
|
|
15319
|
+
() => void (this.sent += batch.length),
|
|
15320
|
+
() => void (this.dropped += batch.length)
|
|
15321
|
+
)
|
|
15322
|
+
);
|
|
15323
|
+
return this.inflight;
|
|
15324
|
+
}
|
|
15325
|
+
protobuf(resource, batch) {
|
|
15326
|
+
const scope = new Pb().message(1, new Pb().string(1, this.o.serviceName));
|
|
15327
|
+
for (const r of batch) {
|
|
15328
|
+
const t = BigInt(r.timeUnixNano);
|
|
15329
|
+
const rec = new Pb().fixed64(1, t).uint(2, SEVERITY_NUMBER[r.severity]).string(3, r.severity).message(5, new Pb().string(1, r.body));
|
|
15330
|
+
pbAttrs(rec, 6, r.attributes).fixed64(11, t);
|
|
15331
|
+
scope.message(2, rec);
|
|
15332
|
+
}
|
|
15333
|
+
const resourceLogs = new Pb().message(1, pbAttrs(new Pb(), 1, resource)).message(2, scope);
|
|
15334
|
+
return new Pb().message(1, resourceLogs).finish();
|
|
15335
|
+
}
|
|
15336
|
+
/** Stop the timer and send what is left. */
|
|
15337
|
+
async close() {
|
|
15338
|
+
if (this.timer) clearInterval(this.timer);
|
|
15339
|
+
this.timer = null;
|
|
15340
|
+
await this.flush();
|
|
15341
|
+
}
|
|
15342
|
+
};
|
|
15343
|
+
function parseOtlpHeaders(text2) {
|
|
15344
|
+
const out = {};
|
|
15345
|
+
for (const part of (text2 ?? "").split(",")) {
|
|
15346
|
+
const i = part.indexOf("=");
|
|
15347
|
+
if (i <= 0) continue;
|
|
15348
|
+
const key = part.slice(0, i).trim();
|
|
15349
|
+
const value = part.slice(i + 1).trim();
|
|
15350
|
+
if (!key) continue;
|
|
15351
|
+
try {
|
|
15352
|
+
out[key] = decodeURIComponent(value);
|
|
15353
|
+
} catch {
|
|
15354
|
+
out[key] = value;
|
|
15355
|
+
}
|
|
15356
|
+
}
|
|
15357
|
+
return out;
|
|
15358
|
+
}
|
|
15359
|
+
var parseResourceAttributes = parseOtlpHeaders;
|
|
15360
|
+
var truthy = (v) => (v ?? "").trim().toLowerCase() === "true";
|
|
15361
|
+
function otlpFromEnv(env, defaults) {
|
|
15362
|
+
if (truthy(env.OTEL_SDK_DISABLED) || (env.OTEL_LOGS_EXPORTER ?? "").trim().toLowerCase() === "none") return null;
|
|
15363
|
+
const logs = env.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT?.trim();
|
|
15364
|
+
const base = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
15365
|
+
const endpoint = logs ? logs.replace(/\/v1\/logs\/?$/, "") : base;
|
|
15366
|
+
if (!endpoint) return null;
|
|
15367
|
+
const protocol = otlpProtocol(env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL);
|
|
15368
|
+
const resource = parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES);
|
|
15369
|
+
return {
|
|
15370
|
+
endpoint,
|
|
15371
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || resource["service.name"] || defaults.serviceName,
|
|
15372
|
+
...defaults.serviceVersion ? { serviceVersion: defaults.serviceVersion } : {},
|
|
15373
|
+
...protocol ? { protocol } : {},
|
|
15374
|
+
...Object.keys(resource).length ? { resource } : {},
|
|
15375
|
+
headers: { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_LOGS_HEADERS) }
|
|
15376
|
+
};
|
|
15377
|
+
}
|
|
15378
|
+
function otlpProtocol(v) {
|
|
15379
|
+
const p = (v ?? "").trim().toLowerCase();
|
|
15380
|
+
return p === "http/protobuf" || p === "http/json" ? p : void 0;
|
|
15381
|
+
}
|
|
15382
|
+
|
|
15383
|
+
// src/server/otlp-sink.ts
|
|
15384
|
+
function resolveOtlp(s, env, base) {
|
|
15385
|
+
const fromEnv = otlpFromEnv(env, { serviceName: s.telemetryServiceName, serviceVersion: base.serviceVersion });
|
|
15386
|
+
const disabled = (env.OTEL_SDK_DISABLED ?? "").trim().toLowerCase() === "true" || (env.OTEL_LOGS_EXPORTER ?? "").trim().toLowerCase() === "none";
|
|
15387
|
+
if (disabled || !fromEnv && !s.telemetryEnabled) return null;
|
|
15388
|
+
const resource = { ...base.resource, ...parseResourceAttributes(env.OTEL_RESOURCE_ATTRIBUTES) };
|
|
15389
|
+
const envProtocol = otlpProtocol(env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL);
|
|
15390
|
+
return {
|
|
15391
|
+
endpoint: fromEnv?.endpoint ?? s.telemetryEndpoint,
|
|
15392
|
+
serviceName: env.OTEL_SERVICE_NAME?.trim() || s.telemetryServiceName,
|
|
15393
|
+
serviceVersion: base.serviceVersion,
|
|
15394
|
+
protocol: envProtocol ?? s.telemetryProtocol ?? "http/protobuf",
|
|
15395
|
+
headers: { ...s.telemetryHeaders ?? {}, ...fromEnv?.headers ?? {} },
|
|
15396
|
+
resource
|
|
15397
|
+
};
|
|
15398
|
+
}
|
|
15399
|
+
var OtlpSink = class {
|
|
15400
|
+
constructor(options, send2) {
|
|
15401
|
+
this.options = options;
|
|
15402
|
+
this.send = send2;
|
|
15403
|
+
this.exporter = null;
|
|
15404
|
+
this.key = "";
|
|
15405
|
+
}
|
|
15406
|
+
/** The exporter for the current options (a new one when they changed), or null. */
|
|
15407
|
+
current() {
|
|
15408
|
+
const o = this.options();
|
|
15409
|
+
const key = o ? JSON.stringify(o) : "";
|
|
15410
|
+
if (key !== this.key) {
|
|
15411
|
+
const old = this.exporter;
|
|
15412
|
+
void old?.close();
|
|
15413
|
+
this.exporter = o ? new OtlpLogExporter({ ...o, flushMs: 0, ...this.send ? { send: this.send } : {} }) : null;
|
|
15414
|
+
this.key = key;
|
|
15415
|
+
}
|
|
15416
|
+
return this.exporter;
|
|
15417
|
+
}
|
|
15418
|
+
log(severity, body, attributes) {
|
|
15419
|
+
this.current()?.log(severity, body, attributes);
|
|
15420
|
+
}
|
|
15421
|
+
async flush() {
|
|
15422
|
+
await this.current()?.flush();
|
|
15423
|
+
}
|
|
15424
|
+
get sent() {
|
|
15425
|
+
return this.exporter?.sent ?? 0;
|
|
15426
|
+
}
|
|
15427
|
+
get dropped() {
|
|
15428
|
+
return this.exporter?.dropped ?? 0;
|
|
15429
|
+
}
|
|
15430
|
+
};
|
|
15431
|
+
|
|
15253
15432
|
// src/core/view-block.ts
|
|
15254
15433
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
15255
15434
|
var BLOCK_KEYS = /* @__PURE__ */ new Set(["view", "table", "columns", "where", "order_by", "limit", "join", "group_by", "aggregates", "expand", "labels", "row_link", "chart", "with_table"]);
|
|
@@ -20757,14 +20936,10 @@ var EngineRuntime = class {
|
|
|
20757
20936
|
session: call.session ?? ""
|
|
20758
20937
|
});
|
|
20759
20938
|
}
|
|
20760
|
-
/** Logs to
|
|
20939
|
+
/** Logs to an OpenTelemetry collector (when configured) and to `.datanotes/logs/`. */
|
|
20761
20940
|
startTelemetry() {
|
|
20762
20941
|
const s = this.o.getSettings;
|
|
20763
|
-
const logger = new
|
|
20764
|
-
getSettings: s,
|
|
20765
|
-
transport: async (url, body) => this.o.postLogs(url, body),
|
|
20766
|
-
resource: { "service.version": this.o.version, "host.name": this.o.hostName }
|
|
20767
|
-
});
|
|
20942
|
+
const logger = new OtlpSink(() => resolveOtlp(s(), this.o.env ?? {}, { serviceVersion: this.o.version, resource: { "service.instance.id": this.o.hostName } }), this.o.otlpSend);
|
|
20768
20943
|
const source = this.o.hostName;
|
|
20769
20944
|
const fileLog = new FileLogger({ enabled: () => s().fileLogEnabled, write: (day, lines) => appendLog(this.o.app, s(), day, lines, source) });
|
|
20770
20945
|
const tee = new TeeTelemetry([logger, fileLog]);
|
|
@@ -22069,7 +22244,7 @@ var NodeWatcher = class {
|
|
|
22069
22244
|
};
|
|
22070
22245
|
|
|
22071
22246
|
// src/node/service.ts
|
|
22072
|
-
var VERSION = true ? "1.
|
|
22247
|
+
var VERSION = true ? "1.4.0" : "dev";
|
|
22073
22248
|
function parseArgs(argv, env = process.env) {
|
|
22074
22249
|
const get = (name) => {
|
|
22075
22250
|
const i = argv.indexOf(`--${name}`);
|
|
@@ -22137,10 +22312,7 @@ async function startService(o, env = process.env) {
|
|
|
22137
22312
|
const res = await fetch(req.url, { method: req.method, headers: req.headers, body: req.body });
|
|
22138
22313
|
return { status: res.status, text: await res.text() };
|
|
22139
22314
|
},
|
|
22140
|
-
|
|
22141
|
-
const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body });
|
|
22142
|
-
if (!res.ok) throw new Error(`OTLP HTTP ${res.status}`);
|
|
22143
|
-
},
|
|
22315
|
+
env,
|
|
22144
22316
|
embeddingKey: () => env.DATANOTES_EMBEDDING_KEY || keyFromFile(),
|
|
22145
22317
|
version: VERSION,
|
|
22146
22318
|
hostName: "service",
|
|
@@ -22247,7 +22419,7 @@ Docs: https://datanotes.dev`;
|
|
|
22247
22419
|
}
|
|
22248
22420
|
|
|
22249
22421
|
// src/node/cli.ts
|
|
22250
|
-
var VERSION2 = true ? "1.
|
|
22422
|
+
var VERSION2 = true ? "1.4.0" : "dev";
|
|
22251
22423
|
async function main2(argv = process.argv.slice(2)) {
|
|
22252
22424
|
const [command, ...rest] = argv;
|
|
22253
22425
|
if (command === "--version" || command === "-v" || command === "version") return void process.stdout.write(`${VERSION2}
|