@rootcat/node 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +213 -2
- package/dist/callsite.d.ts +109 -0
- package/dist/callsite.js +565 -0
- package/dist/callsite.js.map +1 -0
- package/dist/config.d.ts +75 -0
- package/dist/config.js +182 -0
- package/dist/config.js.map +1 -0
- package/dist/drizzle.d.ts +21 -0
- package/dist/drizzle.js +124 -0
- package/dist/drizzle.js.map +1 -0
- package/dist/esm.mjs +41 -0
- package/dist/esmhook.d.ts +12 -0
- package/dist/esmhook.js +84 -0
- package/dist/esmhook.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/intake.d.ts +40 -0
- package/dist/intake.js +164 -0
- package/dist/intake.js.map +1 -0
- package/dist/keys.d.ts +9 -0
- package/dist/keys.js +15 -0
- package/dist/keys.js.map +1 -0
- package/dist/notices.d.ts +2 -0
- package/dist/notices.js +21 -0
- package/dist/notices.js.map +1 -0
- package/dist/otlp.d.ts +75 -0
- package/dist/otlp.js +449 -0
- package/dist/otlp.js.map +1 -0
- package/dist/pg.d.ts +16 -0
- package/dist/pg.js +165 -0
- package/dist/pg.js.map +1 -0
- package/dist/pgpool.d.ts +13 -0
- package/dist/pgpool.js +85 -0
- package/dist/pgpool.js.map +1 -0
- package/dist/postgresjs.d.ts +33 -0
- package/dist/postgresjs.js +272 -0
- package/dist/postgresjs.js.map +1 -0
- package/dist/privacy.d.ts +38 -0
- package/dist/privacy.js +147 -0
- package/dist/privacy.js.map +1 -0
- package/dist/processor.d.ts +94 -0
- package/dist/processor.js +252 -0
- package/dist/processor.js.map +1 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +12 -0
- package/dist/register.js.map +1 -0
- package/dist/sampler.d.ts +12 -0
- package/dist/sampler.js +52 -0
- package/dist/sampler.js.map +1 -0
- package/dist/sdk.d.ts +88 -0
- package/dist/sdk.js +305 -0
- package/dist/sdk.js.map +1 -0
- package/package.json +65 -4
package/dist/pgpool.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InstrumentationBase, type InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
|
|
2
|
+
import { type CallSiteSampling } from './processor.js';
|
|
3
|
+
/** PgPoolEntryOptions bounds and gates the capture. */
|
|
4
|
+
export interface PgPoolEntryOptions extends InstrumentationConfig {
|
|
5
|
+
depth: number;
|
|
6
|
+
sampling: CallSiteSampling;
|
|
7
|
+
isApp?: (file: string) => boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class PgPoolEntryInstrumentation extends InstrumentationBase<PgPoolEntryOptions> {
|
|
10
|
+
constructor(options: PgPoolEntryOptions);
|
|
11
|
+
protected init(): InstrumentationNodeModuleDefinition;
|
|
12
|
+
private readonly wrapQuery;
|
|
13
|
+
}
|
package/dist/pgpool.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PgPoolEntryInstrumentation = void 0;
|
|
4
|
+
// pg-pool entry. Pool.prototype.query hands the query to a client on a
|
|
5
|
+
// later tick, so the pg query span's stack never shows the caller: this
|
|
6
|
+
// wrapper captures the call site where the application calls pool.query
|
|
7
|
+
// and carries it in the active context to the span that starts later.
|
|
8
|
+
//
|
|
9
|
+
// Pool.prototype.connect queues its callback when every client is busy
|
|
10
|
+
// and runs it from the release of another client, in that client's
|
|
11
|
+
// query's context. Its callback is bound to the context of the connect
|
|
12
|
+
// call, so a queued query keeps its own call site and its own parent
|
|
13
|
+
// instead of borrowing the query that freed the client.
|
|
14
|
+
const api_1 = require("@opentelemetry/api");
|
|
15
|
+
const instrumentation_1 = require("@opentelemetry/instrumentation");
|
|
16
|
+
const callsite_js_1 = require("./callsite.js");
|
|
17
|
+
const processor_js_1 = require("./processor.js");
|
|
18
|
+
function poolPrototype(moduleExports) {
|
|
19
|
+
const proto = moduleExports?.prototype;
|
|
20
|
+
return proto && typeof proto.query === 'function' && typeof proto.connect === 'function' ? proto : undefined;
|
|
21
|
+
}
|
|
22
|
+
class PgPoolEntryInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
23
|
+
constructor(options) {
|
|
24
|
+
super('@rootcat/node/pg-pool-entry', '0.1.0', options);
|
|
25
|
+
}
|
|
26
|
+
init() {
|
|
27
|
+
return new instrumentation_1.InstrumentationNodeModuleDefinition('pg-pool', ['>=2.0.0 <4'], (moduleExports) => {
|
|
28
|
+
const proto = poolPrototype(moduleExports);
|
|
29
|
+
if (proto) {
|
|
30
|
+
for (const name of ['query', 'connect']) {
|
|
31
|
+
if ((0, instrumentation_1.isWrapped)(proto[name])) {
|
|
32
|
+
this._unwrap(proto, name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
this._wrap(proto, 'query', this.wrapQuery);
|
|
36
|
+
this._wrap(proto, 'connect', wrapConnect);
|
|
37
|
+
}
|
|
38
|
+
return moduleExports;
|
|
39
|
+
}, (moduleExports) => {
|
|
40
|
+
const proto = poolPrototype(moduleExports);
|
|
41
|
+
if (proto) {
|
|
42
|
+
this._unwrap(proto, 'query');
|
|
43
|
+
this._unwrap(proto, 'connect');
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
wrapQuery = (original) => {
|
|
48
|
+
const instrumentation = this;
|
|
49
|
+
return function query(...args) {
|
|
50
|
+
let carried;
|
|
51
|
+
try {
|
|
52
|
+
// A callback continues in the caller's context, never in the
|
|
53
|
+
// query's: a query issued from it captures its own site.
|
|
54
|
+
const last = args.length - 1;
|
|
55
|
+
if (last >= 0 && typeof args[last] === 'function') {
|
|
56
|
+
args[last] = api_1.context.bind(api_1.context.active(), args[last]);
|
|
57
|
+
}
|
|
58
|
+
const config = instrumentation.getConfig();
|
|
59
|
+
carried = config.sampling.carry(() => (0, callsite_js_1.captureCallSite)(config.depth, config.isApp, query));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
carried = undefined;
|
|
63
|
+
}
|
|
64
|
+
return (0, processor_js_1.runCarried)(carried, () => original.apply(this, args));
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
exports.PgPoolEntryInstrumentation = PgPoolEntryInstrumentation;
|
|
69
|
+
/**
|
|
70
|
+
* wrapConnect binds a connect callback to the context of the call, and
|
|
71
|
+
* runs the pool's own work (opening a client and its socket) in the root
|
|
72
|
+
* context: a connection outlives the query that opened it, and the events
|
|
73
|
+
* it emits later (a LISTEN notification, an error) must not run in that
|
|
74
|
+
* query's context, with its span and call site.
|
|
75
|
+
*/
|
|
76
|
+
function wrapConnect(original) {
|
|
77
|
+
return function connect(...args) {
|
|
78
|
+
const callback = args[0];
|
|
79
|
+
if (typeof callback === 'function') {
|
|
80
|
+
args[0] = api_1.context.bind(api_1.context.active(), callback);
|
|
81
|
+
}
|
|
82
|
+
return api_1.context.with(api_1.ROOT_CONTEXT, () => original.apply(this, args));
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=pgpool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pgpool.js","sourceRoot":"","sources":["../src/pgpool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { InstrumentationBase, type InstrumentationConfig, InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
|
|
2
|
+
import { type CallSiteSampling } from './processor.js';
|
|
3
|
+
/** PostgresJsOptions configures the instrumentation. */
|
|
4
|
+
export interface PostgresJsOptions extends InstrumentationConfig {
|
|
5
|
+
/** sampling gates the origin parsing, like any capture. */
|
|
6
|
+
sampling: CallSiteSampling;
|
|
7
|
+
}
|
|
8
|
+
export declare class PostgresJsInstrumentation extends InstrumentationBase<PostgresJsOptions> {
|
|
9
|
+
private readonly patched;
|
|
10
|
+
/** origins remembers the parsed call site per template, null for none. */
|
|
11
|
+
private readonly origins;
|
|
12
|
+
constructor(options: PostgresJsOptions);
|
|
13
|
+
protected init(): InstrumentationNodeModuleDefinition;
|
|
14
|
+
/**
|
|
15
|
+
* patchModule wraps the factory. CommonJS exports the function itself
|
|
16
|
+
* (the wrapper is returned in its place); an ES module namespace has it
|
|
17
|
+
* as default (replaced in place).
|
|
18
|
+
*/
|
|
19
|
+
private patchModule;
|
|
20
|
+
private unpatchModule;
|
|
21
|
+
private wrapFactory;
|
|
22
|
+
/** patchQueryClass finds Query.prototype through an unexecuted query. */
|
|
23
|
+
private patchQueryClass;
|
|
24
|
+
private readonly wrapHandle;
|
|
25
|
+
/**
|
|
26
|
+
* originSite is the application frame of the driver's own origin stack
|
|
27
|
+
* for a tagged query, parsed once per template; undefined for unsafe()
|
|
28
|
+
* and file() queries, which hold no origin.
|
|
29
|
+
*/
|
|
30
|
+
private originSite;
|
|
31
|
+
private startQuerySpan;
|
|
32
|
+
private endQuerySpan;
|
|
33
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PostgresJsInstrumentation = void 0;
|
|
4
|
+
// postgres.js (the `postgres` package) has no upstream instrumentation.
|
|
5
|
+
// Its Query is a lazy promise: nothing runs until .then, .execute or
|
|
6
|
+
// .forEach calls handle(). An awaited Query is a Promise subclass, so the
|
|
7
|
+
// await calls then() from a job of its own, where no application frame
|
|
8
|
+
// is on the stack. The driver itself keeps a short stack per tagged
|
|
9
|
+
// template (query.origin, four frames, cached per template), which is
|
|
10
|
+
// where the span's call site comes from: parsed once per template.
|
|
11
|
+
// The span starts in handle() on the first execution and ends when the
|
|
12
|
+
// query resolves or rejects. The statement text is what the driver built
|
|
13
|
+
// (query.statement.string, with $1 placeholders); the span never touches
|
|
14
|
+
// query.args.
|
|
15
|
+
//
|
|
16
|
+
// The Query class is internal to the module, so the module's default
|
|
17
|
+
// export (the postgres() factory) is wrapped: the first sql function it
|
|
18
|
+
// returns yields the class through an unexecuted query, and handle() is
|
|
19
|
+
// patched on that prototype once. The same wrapper serves the CommonJS
|
|
20
|
+
// and the ES module builds.
|
|
21
|
+
const api_1 = require("@opentelemetry/api");
|
|
22
|
+
const instrumentation_1 = require("@opentelemetry/instrumentation");
|
|
23
|
+
const callsite_js_1 = require("./callsite.js");
|
|
24
|
+
const privacy_js_1 = require("./privacy.js");
|
|
25
|
+
const processor_js_1 = require("./processor.js");
|
|
26
|
+
/** Bounds. */
|
|
27
|
+
const maxStatementBytes = 4096;
|
|
28
|
+
const supportedVersions = ['>=3.0.0 <4'];
|
|
29
|
+
const rootcatSpan = Symbol('rootcat.span');
|
|
30
|
+
const rootcatWrapped = Symbol('rootcat.wrapped');
|
|
31
|
+
const rootcatOptions = Symbol('rootcat.options');
|
|
32
|
+
/** probe is a template that is never executed; it only yields the class. */
|
|
33
|
+
const probe = Object.freeze(Object.assign(['select 1'], { raw: Object.freeze(['select 1']) }));
|
|
34
|
+
class PostgresJsInstrumentation extends instrumentation_1.InstrumentationBase {
|
|
35
|
+
patched = new WeakSet();
|
|
36
|
+
/** origins remembers the parsed call site per template, null for none. */
|
|
37
|
+
origins = new WeakMap();
|
|
38
|
+
constructor(options) {
|
|
39
|
+
super('@rootcat/node/postgres-js', '0.1.0', options);
|
|
40
|
+
}
|
|
41
|
+
init() {
|
|
42
|
+
return new instrumentation_1.InstrumentationNodeModuleDefinition('postgres', supportedVersions, (moduleExports) => this.patchModule(moduleExports), (moduleExports) => this.unpatchModule(moduleExports));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* patchModule wraps the factory. CommonJS exports the function itself
|
|
46
|
+
* (the wrapper is returned in its place); an ES module namespace has it
|
|
47
|
+
* as default (replaced in place).
|
|
48
|
+
*/
|
|
49
|
+
patchModule(moduleExports) {
|
|
50
|
+
const ns = moduleExports;
|
|
51
|
+
if (typeof ns === 'function') {
|
|
52
|
+
return this.wrapFactory(ns);
|
|
53
|
+
}
|
|
54
|
+
if (ns && typeof ns.default === 'function') {
|
|
55
|
+
try {
|
|
56
|
+
ns.default = this.wrapFactory(ns.default);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// a frozen namespace: no postgres.js spans
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return moduleExports;
|
|
63
|
+
}
|
|
64
|
+
unpatchModule(moduleExports) {
|
|
65
|
+
const ns = moduleExports;
|
|
66
|
+
const wrapped = typeof ns === 'function' ? ns : ns?.default;
|
|
67
|
+
const original = wrapped?.[rootcatWrapped];
|
|
68
|
+
if (original && typeof ns !== 'function' && ns) {
|
|
69
|
+
ns.default = original;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
wrapFactory(original) {
|
|
73
|
+
const marked = original;
|
|
74
|
+
if (marked[rootcatWrapped]) {
|
|
75
|
+
return original;
|
|
76
|
+
}
|
|
77
|
+
const instrumentation = this;
|
|
78
|
+
const wrapper = function postgres(...args) {
|
|
79
|
+
const sql = original.apply(this, args);
|
|
80
|
+
try {
|
|
81
|
+
instrumentation.patchQueryClass(sql);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// the driver works untraced
|
|
85
|
+
}
|
|
86
|
+
return sql;
|
|
87
|
+
};
|
|
88
|
+
Object.assign(wrapper, original);
|
|
89
|
+
Object.setPrototypeOf(wrapper, Object.getPrototypeOf(original));
|
|
90
|
+
wrapper[rootcatWrapped] = original;
|
|
91
|
+
return wrapper;
|
|
92
|
+
}
|
|
93
|
+
/** patchQueryClass finds Query.prototype through an unexecuted query. */
|
|
94
|
+
patchQueryClass(sql) {
|
|
95
|
+
if (typeof sql !== 'function') {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const query = sql(probe);
|
|
99
|
+
if (!query || typeof query !== 'object') {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const proto = Object.getPrototypeOf(query);
|
|
103
|
+
if (!proto || typeof proto.handle !== 'function') {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// The connection options live on the sql function; the queries of
|
|
107
|
+
// this instance reach them through their handler.
|
|
108
|
+
if (typeof query.handler === 'function' && sql.options && typeof sql.options === 'object') {
|
|
109
|
+
query.handler[rootcatOptions] = sql.options;
|
|
110
|
+
}
|
|
111
|
+
if (this.patched.has(proto)) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.patched.add(proto);
|
|
115
|
+
if ((0, instrumentation_1.isWrapped)(proto.handle)) {
|
|
116
|
+
this._unwrap(proto, 'handle');
|
|
117
|
+
}
|
|
118
|
+
this._wrap(proto, 'handle', this.wrapHandle);
|
|
119
|
+
}
|
|
120
|
+
wrapHandle = (original) => {
|
|
121
|
+
const instrumentation = this;
|
|
122
|
+
return function handle(...args) {
|
|
123
|
+
if (this.executed || this[rootcatSpan]) {
|
|
124
|
+
return original.apply(this, args);
|
|
125
|
+
}
|
|
126
|
+
let span;
|
|
127
|
+
try {
|
|
128
|
+
span = instrumentation.startQuerySpan(this);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
span = undefined;
|
|
132
|
+
}
|
|
133
|
+
if (!span) {
|
|
134
|
+
return original.apply(this, args);
|
|
135
|
+
}
|
|
136
|
+
this[rootcatSpan] = span;
|
|
137
|
+
const resolve = this.resolve;
|
|
138
|
+
const reject = this.reject;
|
|
139
|
+
const query = this;
|
|
140
|
+
if (typeof resolve === 'function') {
|
|
141
|
+
this.resolve = function (value) {
|
|
142
|
+
instrumentation.endQuerySpan(query, span, undefined);
|
|
143
|
+
return resolve.call(this, value);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (typeof reject === 'function') {
|
|
147
|
+
this.reject = function (reason) {
|
|
148
|
+
instrumentation.endQuerySpan(query, span, reason);
|
|
149
|
+
return reject.call(this, reason);
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return original.apply(this, args);
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* originSite is the application frame of the driver's own origin stack
|
|
157
|
+
* for a tagged query, parsed once per template; undefined for unsafe()
|
|
158
|
+
* and file() queries, which hold no origin.
|
|
159
|
+
*/
|
|
160
|
+
originSite(query) {
|
|
161
|
+
const strings = query.strings;
|
|
162
|
+
if (!query.tagged || !strings || typeof strings !== 'object') {
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
const known = this.origins.get(strings);
|
|
166
|
+
if (known !== undefined) {
|
|
167
|
+
return known ?? undefined;
|
|
168
|
+
}
|
|
169
|
+
let site;
|
|
170
|
+
try {
|
|
171
|
+
const origin = query.origin;
|
|
172
|
+
site = typeof origin === 'string' ? (0, callsite_js_1.parseStack)(origin) : undefined;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
site = undefined;
|
|
176
|
+
}
|
|
177
|
+
this.origins.set(strings, site ?? null);
|
|
178
|
+
return site;
|
|
179
|
+
}
|
|
180
|
+
startQuerySpan(query) {
|
|
181
|
+
// The template's own origin (free: parsed once per template) wins over
|
|
182
|
+
// a site carried from outside, as a query written in the continuation
|
|
183
|
+
// of another is its own; unsafe() queries keep what an ORM carried.
|
|
184
|
+
const ctx = this.getConfig().sampling.carry(() => this.originSite(query), true) ?? api_1.context.active();
|
|
185
|
+
if ((0, processor_js_1.unsampled)(ctx)) {
|
|
186
|
+
return undefined; // not recorded: the query runs untouched
|
|
187
|
+
}
|
|
188
|
+
const options = query.handler?.[rootcatOptions];
|
|
189
|
+
const keyword = (0, privacy_js_1.statementKeyword)(leadingText(query));
|
|
190
|
+
const attributes = { 'db.system.name': 'postgresql' };
|
|
191
|
+
if (keyword !== '') {
|
|
192
|
+
attributes['db.operation.name'] = keyword;
|
|
193
|
+
}
|
|
194
|
+
if (options) {
|
|
195
|
+
if (typeof options.database === 'string') {
|
|
196
|
+
attributes['db.namespace'] = options.database;
|
|
197
|
+
}
|
|
198
|
+
const host = Array.isArray(options.host) ? options.host[0] : options.host;
|
|
199
|
+
if (typeof host === 'string') {
|
|
200
|
+
attributes['server.address'] = host;
|
|
201
|
+
}
|
|
202
|
+
const port = Array.isArray(options.port) ? options.port[0] : options.port;
|
|
203
|
+
if (typeof port === 'number') {
|
|
204
|
+
attributes['server.port'] = port;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const name = keyword === '' ? 'postgres.query' : `postgres.query:${keyword}`;
|
|
208
|
+
return this.tracer.startSpan(name, { kind: api_1.SpanKind.CLIENT, attributes }, ctx);
|
|
209
|
+
}
|
|
210
|
+
endQuerySpan(query, span, reason) {
|
|
211
|
+
try {
|
|
212
|
+
// Literals are masked (or the text reduced to its keyword) by the
|
|
213
|
+
// processor's statement rule as the span ends.
|
|
214
|
+
const statement = finalStatement(query);
|
|
215
|
+
if (statement !== '') {
|
|
216
|
+
span.setAttribute('db.query.text', statement);
|
|
217
|
+
}
|
|
218
|
+
if (reason !== undefined) {
|
|
219
|
+
// The server's message can quote the input that failed ("invalid
|
|
220
|
+
// input syntax for type integer: ..."), so, as the pg
|
|
221
|
+
// instrumentation does, only the error's class and SQLSTATE travel.
|
|
222
|
+
const error = reason;
|
|
223
|
+
const name = typeof error?.name === 'string' ? error.name.slice(0, 64) : 'Error';
|
|
224
|
+
const code = typeof error?.code === 'string' ? error.code.slice(0, 16) : '';
|
|
225
|
+
const message = code === '' ? `${name} from postgres.js` : `${name} from postgres.js (code: ${code})`;
|
|
226
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message });
|
|
227
|
+
span.addEvent('exception', { 'exception.type': name, 'exception.message': message });
|
|
228
|
+
span.setAttribute('error.type', name);
|
|
229
|
+
if (code !== '') {
|
|
230
|
+
span.setAttribute('db.response.status_code', code);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// the span still ends
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
span.end();
|
|
239
|
+
delete query[rootcatSpan];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
exports.PostgresJsInstrumentation = PostgresJsInstrumentation;
|
|
244
|
+
/** leadingText is the first template part, before any parameter. */
|
|
245
|
+
function leadingText(query) {
|
|
246
|
+
const strings = query.strings;
|
|
247
|
+
if (Array.isArray(strings) && typeof strings[0] === 'string') {
|
|
248
|
+
return strings[0].slice(0, 256);
|
|
249
|
+
}
|
|
250
|
+
return '';
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* finalStatement is the statement the driver sent: query.statement.string
|
|
254
|
+
* once built, else the template joined with placeholders for a query the
|
|
255
|
+
* driver answered without building (an error before the build).
|
|
256
|
+
*/
|
|
257
|
+
function finalStatement(query) {
|
|
258
|
+
const built = query.statement?.string;
|
|
259
|
+
if (typeof built === 'string') {
|
|
260
|
+
return built.slice(0, maxStatementBytes);
|
|
261
|
+
}
|
|
262
|
+
const strings = query.strings;
|
|
263
|
+
if (Array.isArray(strings) && strings.every((part) => typeof part === 'string')) {
|
|
264
|
+
let text = strings[0] ?? '';
|
|
265
|
+
for (let i = 1; i < strings.length; i++) {
|
|
266
|
+
text += '$' + i + strings[i];
|
|
267
|
+
}
|
|
268
|
+
return text.slice(0, maxStatementBytes);
|
|
269
|
+
}
|
|
270
|
+
return '';
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=postgresjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgresjs.js","sourceRoot":"","sources":["../src/postgresjs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Attributes } from '@opentelemetry/api';
|
|
2
|
+
/** StatementMode is what query text keeps: its shape with literals masked, or its first keyword. */
|
|
3
|
+
export type StatementMode = 'masked' | 'off';
|
|
4
|
+
/** urlKeys hold a URL or path whose query, fragment and opaque segments are cut. */
|
|
5
|
+
export declare const urlKeys: readonly ["http.url", "url.full", "http.target", "url.path"];
|
|
6
|
+
/** clearedKeys hold a query string, a client's address or a user agent; they are emptied. */
|
|
7
|
+
export declare const clearedKeys: readonly ["url.query", "http.client_ip", "client.address", "net.peer.ip", "net.sock.peer.addr", "network.peer.address", "user_agent.original", "http.user_agent"];
|
|
8
|
+
/** maxStatementLength bounds the text the masker reads. */
|
|
9
|
+
export declare const maxStatementLength = 4096;
|
|
10
|
+
/** statementKeys hold query text. */
|
|
11
|
+
export declare const statementKeys: readonly ["db.statement", "db.query.text"];
|
|
12
|
+
/**
|
|
13
|
+
* framesOnly keeps the frame lines of a stack trace (" at ...") and
|
|
14
|
+
* drops the message line(s) before them, which can quote input.
|
|
15
|
+
*/
|
|
16
|
+
export declare function framesOnly(stack: string): string;
|
|
17
|
+
/** stripQuery cuts a URL at its query or fragment. */
|
|
18
|
+
export declare function stripQuery(url: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* maskPath cuts the query and fragment and replaces every segment that
|
|
21
|
+
* could hold an identifier or a token (numbers, UUIDs, JWTs, reset
|
|
22
|
+
* tokens, emails, file names) with {id}; the scheme and host of a full
|
|
23
|
+
* URL are kept.
|
|
24
|
+
*/
|
|
25
|
+
export declare function maskPath(url: string): string;
|
|
26
|
+
/** statementKeyword keeps the first word of a statement, upper-cased. */
|
|
27
|
+
export declare function statementKeyword(statement: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* maskLiterals replaces every literal in a SQL statement with ?, keeps
|
|
30
|
+
* identifiers, keywords and placeholders, and drops comments (which
|
|
31
|
+
* sqlcommenter fills with values).
|
|
32
|
+
*/
|
|
33
|
+
export declare function maskLiterals(statement: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* scrubbed returns the attribute changes privacy requires for the given
|
|
36
|
+
* attributes, or undefined when nothing needs changing.
|
|
37
|
+
*/
|
|
38
|
+
export declare function scrubbed(attributes: Attributes, statement: StatementMode): Attributes | undefined;
|
package/dist/privacy.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.statementKeys = exports.maxStatementLength = exports.clearedKeys = exports.urlKeys = void 0;
|
|
4
|
+
exports.framesOnly = framesOnly;
|
|
5
|
+
exports.stripQuery = stripQuery;
|
|
6
|
+
exports.maskPath = maskPath;
|
|
7
|
+
exports.statementKeyword = statementKeyword;
|
|
8
|
+
exports.maskLiterals = maskLiterals;
|
|
9
|
+
exports.scrubbed = scrubbed;
|
|
10
|
+
/** urlKeys hold a URL or path whose query, fragment and opaque segments are cut. */
|
|
11
|
+
exports.urlKeys = ['http.url', 'url.full', 'http.target', 'url.path'];
|
|
12
|
+
/** clearedKeys hold a query string, a client's address or a user agent; they are emptied. */
|
|
13
|
+
exports.clearedKeys = [
|
|
14
|
+
'url.query',
|
|
15
|
+
'http.client_ip',
|
|
16
|
+
'client.address',
|
|
17
|
+
'net.peer.ip',
|
|
18
|
+
'net.sock.peer.addr',
|
|
19
|
+
'network.peer.address',
|
|
20
|
+
'user_agent.original',
|
|
21
|
+
'http.user_agent',
|
|
22
|
+
];
|
|
23
|
+
/** maxStatementLength bounds the text the masker reads. */
|
|
24
|
+
exports.maxStatementLength = 4096;
|
|
25
|
+
/** statementKeys hold query text. */
|
|
26
|
+
exports.statementKeys = ['db.statement', 'db.query.text'];
|
|
27
|
+
/**
|
|
28
|
+
* framesOnly keeps the frame lines of a stack trace (" at ...") and
|
|
29
|
+
* drops the message line(s) before them, which can quote input.
|
|
30
|
+
*/
|
|
31
|
+
function framesOnly(stack) {
|
|
32
|
+
const lines = stack.split('\n');
|
|
33
|
+
const first = lines.findIndex((line) => /^\s+at\s/.test(line));
|
|
34
|
+
if (first <= 0) {
|
|
35
|
+
return first === 0 ? stack : '';
|
|
36
|
+
}
|
|
37
|
+
return lines.slice(first).join('\n');
|
|
38
|
+
}
|
|
39
|
+
/** stripQuery cuts a URL at its query or fragment. */
|
|
40
|
+
function stripQuery(url) {
|
|
41
|
+
const cut = url.search(/[?#]/);
|
|
42
|
+
return cut < 0 ? url : url.slice(0, cut);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* keptSegment reports whether a path segment can hold no secret: a name
|
|
46
|
+
* of letters, hyphens and underscores up to 32 bytes that is not a
|
|
47
|
+
* mixed-case run of twelve or more (a base62 token without digits), or a
|
|
48
|
+
* version (v1, api2). The agent's request capture keeps the same.
|
|
49
|
+
*/
|
|
50
|
+
function keptSegment(segment) {
|
|
51
|
+
if (/^[A-Za-z]{1,4}\d{1,3}$/.test(segment)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (!/^[A-Za-z_-]{1,32}$/.test(segment)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
return !(segment.length >= 12 && /[a-z]/.test(segment) && /[A-Z]/.test(segment));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* maskPath cuts the query and fragment and replaces every segment that
|
|
61
|
+
* could hold an identifier or a token (numbers, UUIDs, JWTs, reset
|
|
62
|
+
* tokens, emails, file names) with {id}; the scheme and host of a full
|
|
63
|
+
* URL are kept.
|
|
64
|
+
*/
|
|
65
|
+
function maskPath(url) {
|
|
66
|
+
const bare = stripQuery(url);
|
|
67
|
+
const origin = /^[a-z][a-z0-9+.-]*:\/\/[^/]*/i.exec(bare);
|
|
68
|
+
const prefix = origin ? origin[0] : '';
|
|
69
|
+
const rest = bare.slice(prefix.length);
|
|
70
|
+
const masked = rest
|
|
71
|
+
.split('/')
|
|
72
|
+
.map((segment) => (segment === '' || keptSegment(segment) ? segment : '{id}'))
|
|
73
|
+
.join('/');
|
|
74
|
+
return prefix + masked;
|
|
75
|
+
}
|
|
76
|
+
/** statementKeyword keeps the first word of a statement, upper-cased. */
|
|
77
|
+
function statementKeyword(statement) {
|
|
78
|
+
const match = /^\s*(?:\/\*.*?\*\/\s*)*([A-Za-z]+)/.exec(statement);
|
|
79
|
+
return match ? match[1].toUpperCase() : '';
|
|
80
|
+
}
|
|
81
|
+
// One pass over the text: quoted identifiers are kept, comments dropped,
|
|
82
|
+
// string, dollar-quoted and numeric literals masked. $1-style
|
|
83
|
+
// placeholders are kept (a dollar quote's tag cannot start with a digit,
|
|
84
|
+
// and a number right after $ is not a literal).
|
|
85
|
+
// A string or dollar quote left open (a statement cut at its bound) is
|
|
86
|
+
// masked to the end.
|
|
87
|
+
// An E'' string's E must start a word (in LIKE'%x%' the E ends a keyword).
|
|
88
|
+
// Numbers: decimal with or without digits on either side of the point
|
|
89
|
+
// (1, 1.5, 1., .5), with an exponent, or hexadecimal (0xDEAD).
|
|
90
|
+
const sqlTokens = /("(?:[^"]|"")*"|`(?:[^`]|``)*`)|(--[^\n]*|\/\*[\s\S]*?\*\/)|((?<!\w)[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'|\$([A-Za-z_][A-Za-z0-9_]*)?\$[\s\S]*?\$\4\$|(?<!\w)[Ee]'[\s\S]*$|'[\s\S]*$|\$(?:[A-Za-z_][A-Za-z0-9_]*)?\$[\s\S]*$)|((?<![\w$.])(?:0[xX][0-9a-fA-F]+|(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)(?![\w.]))/g;
|
|
91
|
+
/**
|
|
92
|
+
* maskLiterals replaces every literal in a SQL statement with ?, keeps
|
|
93
|
+
* identifiers, keywords and placeholders, and drops comments (which
|
|
94
|
+
* sqlcommenter fills with values).
|
|
95
|
+
*/
|
|
96
|
+
function maskLiterals(statement) {
|
|
97
|
+
return statement
|
|
98
|
+
.replace(sqlTokens, (_match, identifier, comment) => {
|
|
99
|
+
if (identifier !== undefined) {
|
|
100
|
+
return identifier;
|
|
101
|
+
}
|
|
102
|
+
if (comment !== undefined) {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
return '?';
|
|
106
|
+
})
|
|
107
|
+
.trim();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* scrubbed returns the attribute changes privacy requires for the given
|
|
111
|
+
* attributes, or undefined when nothing needs changing.
|
|
112
|
+
*/
|
|
113
|
+
function scrubbed(attributes, statement) {
|
|
114
|
+
let changes;
|
|
115
|
+
const set = (key, value) => {
|
|
116
|
+
(changes ??= {})[key] = value;
|
|
117
|
+
};
|
|
118
|
+
for (const key of exports.urlKeys) {
|
|
119
|
+
const value = attributes[key];
|
|
120
|
+
if (typeof value === 'string' && value !== '') {
|
|
121
|
+
const masked = maskPath(value);
|
|
122
|
+
if (masked !== value) {
|
|
123
|
+
set(key, masked);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const key of exports.clearedKeys) {
|
|
128
|
+
const value = attributes[key];
|
|
129
|
+
if (typeof value === 'string' && value !== '') {
|
|
130
|
+
set(key, '');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
for (const key of exports.statementKeys) {
|
|
134
|
+
const value = attributes[key];
|
|
135
|
+
if (typeof value === 'string' && value !== '') {
|
|
136
|
+
// The span limits bound attribute values; the masker never runs over
|
|
137
|
+
// more than that, whatever set the attribute.
|
|
138
|
+
const bounded = value.length > exports.maxStatementLength ? value.slice(0, exports.maxStatementLength) : value;
|
|
139
|
+
const kept = statement === 'off' ? statementKeyword(bounded) : maskLiterals(bounded);
|
|
140
|
+
if (kept !== value) {
|
|
141
|
+
set(key, kept);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return changes;
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=privacy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../src/privacy.ts"],"names":[],"mappings":"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"}
|