@open-mercato/telemetry 0.10.1-develop.7900.1.69c0120c72 → 0.10.1-develop.7903.1.d8ab1c8b0d
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/telemetry",
|
|
3
|
-
"version": "0.10.1-develop.
|
|
3
|
+
"version": "0.10.1-develop.7903.1.d8ab1c8b0d",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Vendor-neutral spans, metrics, and error reporting backed by a pluggable OTLP provider. Off by default; the telemetry runtime is loaded only when explicitly enabled.",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@open-mercato/shared": "0.10.1-develop.
|
|
41
|
+
"@open-mercato/shared": "0.10.1-develop.7903.1.d8ab1c8b0d"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": ">=18.0.0"
|
|
@@ -16,10 +16,13 @@ import path from 'node:path'
|
|
|
16
16
|
* a dead port still drives the query path, and the instrumentation records the
|
|
17
17
|
* span before the underlying call fails.
|
|
18
18
|
*/
|
|
19
|
-
const distProvider = path.resolve(__dirname, '../../dist/provider/otlp-provider.js')
|
|
20
19
|
const pkgRoot = path.resolve(__dirname, '../..')
|
|
21
20
|
|
|
22
21
|
const CHILD_SCRIPT = `
|
|
22
|
+
import assert from 'node:assert/strict'
|
|
23
|
+
import { writeSync } from 'node:fs'
|
|
24
|
+
const stage = (name) => writeSync(2, 'STAGE=' + name + '\\n')
|
|
25
|
+
stage('import-provider')
|
|
23
26
|
const { OtlpProvider } = await import('@open-mercato/telemetry/provider/otlp-provider')
|
|
24
27
|
const { setActiveProvider } = await import('@open-mercato/telemetry/provider/registry')
|
|
25
28
|
const { InMemorySpanExporter, SimpleSpanProcessor } = await import('@opentelemetry/sdk-trace-base')
|
|
@@ -27,34 +30,59 @@ const { InMemorySpanExporter, SimpleSpanProcessor } = await import('@opentelemet
|
|
|
27
30
|
// No 'instrumentations' override: the provider's DEFAULT list must include the
|
|
28
31
|
// pg instrumentation (with PII-safe options), or this probe records no span.
|
|
29
32
|
const exporter = new InMemorySpanExporter()
|
|
33
|
+
const processor = new SimpleSpanProcessor(exporter)
|
|
30
34
|
const provider = new OtlpProvider({
|
|
31
|
-
spanProcessors: [
|
|
35
|
+
spanProcessors: [processor],
|
|
36
|
+
logRecordProcessors: [],
|
|
37
|
+
metricReaders: [],
|
|
32
38
|
})
|
|
33
39
|
// start() BEFORE pg is imported — the ordering the CLI bootstrap guarantees.
|
|
40
|
+
stage('start-provider')
|
|
34
41
|
await provider.start()
|
|
35
42
|
setActiveProvider(provider)
|
|
36
43
|
|
|
44
|
+
stage('import-pg')
|
|
37
45
|
const pgModule = await import('pg')
|
|
38
46
|
const Client = (pgModule.default ?? pgModule).Client
|
|
39
|
-
const client = new Client({
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
const client = new Client({
|
|
48
|
+
host: '127.0.0.1', port: 1, connectionTimeoutMillis: 200,
|
|
49
|
+
user: 'telemetry-probe', database: 'telemetry-probe',
|
|
50
|
+
})
|
|
51
|
+
const ended = new Promise((resolve) => client.once('end', resolve))
|
|
52
|
+
try {
|
|
53
|
+
stage('connect')
|
|
54
|
+
await assert.rejects(client.connect())
|
|
55
|
+
if (process.argv[2] === 'socket-ended') {
|
|
56
|
+
stage('wait-socket-end')
|
|
57
|
+
await ended
|
|
58
|
+
}
|
|
59
|
+
stage('close-client')
|
|
60
|
+
await client.end()
|
|
61
|
+
stage('query')
|
|
62
|
+
await assert.rejects(client.query('SELECT $1::text', ['pii-sentinel-value']))
|
|
63
|
+
stage('flush-spans')
|
|
64
|
+
await processor.forceFlush()
|
|
65
|
+
const spans = exporter.getFinishedSpans().map((span) => ({ name: span.name, attributes: span.attributes }))
|
|
66
|
+
stage('shutdown-provider')
|
|
67
|
+
await provider.shutdown()
|
|
68
|
+
stage('shutdown-finished')
|
|
69
|
+
console.log('SPANS=' + JSON.stringify(spans))
|
|
70
|
+
} finally {
|
|
71
|
+
await client.end()
|
|
72
|
+
await provider.shutdown()
|
|
73
|
+
}
|
|
46
74
|
`
|
|
47
75
|
|
|
48
76
|
type ProbeSpan = { name: string; attributes: Record<string, unknown> }
|
|
49
77
|
|
|
50
|
-
function runChild(): ProbeSpan[] {
|
|
78
|
+
function runChild(order: string): ProbeSpan[] {
|
|
51
79
|
// The script must live inside the package tree: ESM resolves bare specifiers
|
|
52
80
|
// (`@open-mercato/telemetry`, `@opentelemetry/*`, `pg`) relative to the file's
|
|
53
81
|
// location, not the process cwd.
|
|
54
82
|
const scriptFile = path.join(pkgRoot, `.pg-instrumentation-probe.${process.pid}.mjs`)
|
|
55
83
|
fs.writeFileSync(scriptFile, CHILD_SCRIPT)
|
|
56
84
|
try {
|
|
57
|
-
const result = spawnSync(process.execPath, [scriptFile], {
|
|
85
|
+
const result = spawnSync(process.execPath, [scriptFile, order], {
|
|
58
86
|
cwd: pkgRoot,
|
|
59
87
|
encoding: 'utf8',
|
|
60
88
|
env: {
|
|
@@ -65,24 +93,26 @@ function runChild(): ProbeSpan[] {
|
|
|
65
93
|
},
|
|
66
94
|
timeout: 30_000,
|
|
67
95
|
})
|
|
68
|
-
const line = (result.stdout || '').split('\n').find((
|
|
69
|
-
if (
|
|
96
|
+
const line = (result.stdout || '').split('\n').find((candidate) => candidate.startsWith('SPANS='))
|
|
97
|
+
if (result.error || result.status !== 0 || result.signal || !line) {
|
|
98
|
+
throw new Error(`[internal] child probe failed.\nerror: ${result.error?.message ?? 'none'}\nstatus: ${result.status}\nsignal: ${result.signal}\nstdout: ${result.stdout}\nstderr: ${result.stderr}`)
|
|
99
|
+
}
|
|
70
100
|
return JSON.parse(line.slice('SPANS='.length)) as ProbeSpan[]
|
|
71
101
|
} finally {
|
|
72
102
|
fs.rmSync(scriptFile, { force: true })
|
|
73
103
|
}
|
|
74
104
|
}
|
|
75
105
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
106
|
+
describe('OTLP provider pg instrumentation', () => {
|
|
107
|
+
it.each(['connect-rejected', 'socket-ended'])(
|
|
108
|
+
'emits a pg query span without leaking bound parameter values after %s',
|
|
109
|
+
(order) => {
|
|
110
|
+
const spans = runChild(order)
|
|
111
|
+
expect(spans.some((span) => span.name.startsWith('pg.query'))).toBe(true)
|
|
112
|
+
// enhancedDatabaseReporting must stay off in the provider's default wiring:
|
|
113
|
+
// the statement text may appear, bound values must not.
|
|
114
|
+
expect(JSON.stringify(spans)).not.toContain('pii-sentinel-value')
|
|
115
|
+
},
|
|
116
|
+
35_000,
|
|
117
|
+
)
|
|
88
118
|
})
|