@agoric/telemetry 0.6.3-other-dev-8f8782b.0 → 0.6.3-other-dev-fbe72e7.0.fbe72e7
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 +0 -9
- package/package.json +36 -27
- package/scripts/ingest.sh +2 -2
- package/src/context-aware-slog-file.js +42 -0
- package/src/context-aware-slog.js +387 -0
- package/src/flight-recorder.js +173 -90
- package/src/frcat-entrypoint.js +15 -11
- package/src/index.js +29 -30
- package/src/ingest-slog-entrypoint.js +46 -15
- package/src/make-slog-sender.js +126 -109
- package/src/otel-and-flight-recorder.js +4 -1
- package/src/otel-context-aware-slog.js +131 -0
- package/src/otel-metrics.js +229 -0
- package/src/otel-trace.js +11 -1
- package/src/prometheus.js +18 -0
- package/src/serialize-slog-obj.js +32 -4
- package/src/slog-file.js +1 -1
- package/src/slog-sender-pipe-entrypoint.js +76 -74
- package/src/slog-sender-pipe.js +87 -112
- package/src/slog-to-otel.js +26 -11
- package/test/flight-recorder.test.js +114 -0
- package/test/prepare-test-env-ava.js +0 -2
- package/{jsconfig.json → tsconfig.json} +1 -0
- package/test/test-flight-recorder.js +0 -53
- /package/test/{test-import.js → import.test.js} +0 -0
package/src/otel-trace.js
CHANGED
|
@@ -22,16 +22,26 @@ export const SPAN_EXPORT_DELAY_MS = 1_000;
|
|
|
22
22
|
export const makeOtelTracingProvider = opts => {
|
|
23
23
|
const { env = process.env } = opts || {};
|
|
24
24
|
|
|
25
|
+
// https://opentelemetry.io/docs/concepts/signals/
|
|
26
|
+
// https://opentelemetry.io/docs/specs/otel/protocol/exporter/#endpoint-urls-for-otlphttp
|
|
27
|
+
// https://github.com/open-telemetry/opentelemetry-js/blob/experimental/v0.57.1/experimental/packages/exporter-trace-otlp-http/README.md#configuration-options-as-environment-variables
|
|
25
28
|
const { OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT } =
|
|
26
29
|
env;
|
|
27
30
|
if (!OTEL_EXPORTER_OTLP_ENDPOINT && !OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) {
|
|
31
|
+
console.debug(
|
|
32
|
+
'Not enabling OTLP Traces Exporter; enable with OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=<target URL> or OTEL_EXPORTER_OTLP_ENDPOINT=<target URL prefix>',
|
|
33
|
+
);
|
|
28
34
|
return undefined;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
const resource = new Resource(getResourceAttributes(opts));
|
|
32
38
|
|
|
33
39
|
const exporter = new OTLPTraceExporter();
|
|
34
|
-
console.info(
|
|
40
|
+
console.info(
|
|
41
|
+
'Enabling OTLP Traces Exporter to',
|
|
42
|
+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
|
|
43
|
+
`${OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces`,
|
|
44
|
+
);
|
|
35
45
|
|
|
36
46
|
const provider = new BasicTracerProvider({ resource });
|
|
37
47
|
provider.addSpanProcessor(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Fail } from '@endo/errors';
|
|
2
|
+
|
|
3
|
+
import { getPrometheusMeterProvider } from './index.js';
|
|
4
|
+
import { makeSlogSender as makeOtelMetricsSender } from './otel-metrics.js';
|
|
5
|
+
|
|
6
|
+
/** @param {import('./index.js').MakeSlogSenderOptions & {otelMeterName?: string}} opts */
|
|
7
|
+
export const makeSlogSender = async (opts = {}) => {
|
|
8
|
+
const { env, otelMeterName, serviceName } = opts;
|
|
9
|
+
if (!otelMeterName) throw Fail`OTel meter name is required`;
|
|
10
|
+
const otelMeterProvider = getPrometheusMeterProvider({
|
|
11
|
+
console,
|
|
12
|
+
env,
|
|
13
|
+
serviceName,
|
|
14
|
+
});
|
|
15
|
+
if (!otelMeterProvider) return;
|
|
16
|
+
|
|
17
|
+
return makeOtelMetricsSender({ ...opts, otelMeterName, otelMeterProvider });
|
|
18
|
+
};
|
|
@@ -1,4 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
)
|
|
1
|
+
const { hasOwn } = Object;
|
|
2
|
+
|
|
3
|
+
const replacer = (_key, value) => {
|
|
4
|
+
switch (typeof value) {
|
|
5
|
+
case 'object': {
|
|
6
|
+
if (value instanceof Error) {
|
|
7
|
+
// Represent each error as a serialization-friendly
|
|
8
|
+
// { errorType, message, cause?, errors?, stack? } object
|
|
9
|
+
// (itself subject to recursive replacement, particularly in `cause` and
|
|
10
|
+
// `errors`).
|
|
11
|
+
const obj = { errorType: value.name, message: value.message };
|
|
12
|
+
if (hasOwn(value, 'cause')) obj.cause = value.cause;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
14
|
+
// @ts-ignore TS2339 property "errors" is only on AggregateError
|
|
15
|
+
if (hasOwn(value, 'errors')) obj.errors = value.errors;
|
|
16
|
+
const stack = value.stack;
|
|
17
|
+
if (stack) obj.stack = stack;
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
case 'bigint':
|
|
23
|
+
// Represent each bigint as a JSON-serializable number, accepting the
|
|
24
|
+
// possible loss of precision.
|
|
25
|
+
return Number(value);
|
|
26
|
+
default:
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const serializeSlogObj = slogObj => JSON.stringify(slogObj, replacer);
|
package/src/slog-file.js
CHANGED
|
@@ -11,7 +11,7 @@ export const makeSlogSender = async ({ env: { SLOGFILE } = {} } = {}) => {
|
|
|
11
11
|
|
|
12
12
|
const slogSender = (slogObj, jsonObj = serializeSlogObj(slogObj)) => {
|
|
13
13
|
// eslint-disable-next-line prefer-template
|
|
14
|
-
|
|
14
|
+
stream.write(jsonObj + '\n').catch(() => {});
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
return Object.assign(slogSender, {
|
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
/**
|
|
3
|
+
* @file Run as a child process of {@link ./slog-sender-pipe.js} to isolate an
|
|
4
|
+
* aggregate slog sender (@see {@link ./make-slog-sender.js}). Communicates
|
|
5
|
+
* with its parent via Node.js IPC with advanced (structured clone)
|
|
6
|
+
* serialization.
|
|
7
|
+
* https://nodejs.org/docs/latest/api/child_process.html#advanced-serialization
|
|
8
|
+
*/
|
|
9
|
+
|
|
2
10
|
import '@endo/init';
|
|
3
11
|
|
|
4
|
-
import { makeAggregateError } from '@agoric/internal';
|
|
5
12
|
import anylogger from 'anylogger';
|
|
13
|
+
import { Fail } from '@endo/errors';
|
|
6
14
|
import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
|
|
7
15
|
|
|
8
16
|
import { makeSlogSender } from './make-slog-sender.js';
|
|
9
17
|
|
|
10
18
|
const logger = anylogger('slog-sender-pipe-entrypoint');
|
|
11
19
|
|
|
12
|
-
/** @type {(msg: import('./slog-sender-pipe.js').
|
|
20
|
+
/** @type {(msg: import('./slog-sender-pipe.js').PipeAPIReply) => void} */
|
|
13
21
|
const send = Function.prototype.bind.call(process.send, process);
|
|
14
22
|
|
|
15
23
|
/**
|
|
16
|
-
* @typedef {
|
|
17
|
-
* @
|
|
18
|
-
* @
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* @typedef {
|
|
22
|
-
* @
|
|
23
|
-
* @property {object} obj
|
|
24
|
+
* @typedef {{type: 'init', options: import('./index.js').MakeSlogSenderOptions }} InitMessage
|
|
25
|
+
* @typedef {{type: 'flush' }} FlushMessage
|
|
26
|
+
* @typedef {{type: 'send', obj: Record<string, unknown> }} SendMessage
|
|
27
|
+
*
|
|
28
|
+
* @typedef {InitMessage | FlushMessage} PipeAPIResponsefulMessage
|
|
29
|
+
* @typedef {SendMessage} PipeAPIResponselessMessage
|
|
30
|
+
* @typedef {PipeAPIResponsefulMessage | PipeAPIResponselessMessage} PipeAPIMessage
|
|
24
31
|
*/
|
|
25
|
-
/**
|
|
26
|
-
* @typedef {object} FlushMessage
|
|
27
|
-
* @property {'flush'} type
|
|
28
|
-
*/
|
|
29
|
-
/** @typedef {InitMessage | FlushMessage} SlogSenderPipeWaitMessages */
|
|
30
|
-
/** @typedef {SlogSenderPipeWaitMessages | SendMessage } SlogSenderPipeMessages */
|
|
31
32
|
|
|
32
33
|
const main = async () => {
|
|
33
34
|
/** @type {import('./index.js').SlogSender | undefined} */
|
|
@@ -45,9 +46,7 @@ const main = async () => {
|
|
|
45
46
|
|
|
46
47
|
/** @param {import('./index.js').MakeSlogSenderOptions} opts */
|
|
47
48
|
const init = async ({ env, ...otherOpts } = {}) => {
|
|
48
|
-
|
|
49
|
-
assert.fail('Already initialized');
|
|
50
|
-
}
|
|
49
|
+
!slogSender || Fail`Already initialized`;
|
|
51
50
|
|
|
52
51
|
slogSender = await makeSlogSender({
|
|
53
52
|
...otherOpts,
|
|
@@ -58,9 +57,7 @@ const main = async () => {
|
|
|
58
57
|
};
|
|
59
58
|
|
|
60
59
|
const flush = async () => {
|
|
61
|
-
if (!slogSender)
|
|
62
|
-
assert.fail('No sender available');
|
|
63
|
-
}
|
|
60
|
+
if (!slogSender) throw Fail`No sender available`;
|
|
64
61
|
|
|
65
62
|
await slogSender.forceFlush?.();
|
|
66
63
|
};
|
|
@@ -75,62 +72,67 @@ const main = async () => {
|
|
|
75
72
|
sendErrors.unshift(actualFlushError);
|
|
76
73
|
}
|
|
77
74
|
|
|
78
|
-
return
|
|
75
|
+
return AggregateError(sendErrors.splice(0));
|
|
79
76
|
};
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
78
|
+
/** @param {PipeAPIMessage} msg */
|
|
79
|
+
const onMessage = msg => {
|
|
80
|
+
if (!msg || typeof msg !== 'object') {
|
|
81
|
+
logger.warn('Received invalid message', msg);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
88
84
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
85
|
+
switch (msg.type) {
|
|
86
|
+
case 'init': {
|
|
87
|
+
void init(msg.options).then(
|
|
88
|
+
hasSender => {
|
|
89
|
+
send({ type: 'initReply', hasSender });
|
|
90
|
+
},
|
|
91
|
+
error => {
|
|
92
|
+
send({ type: 'initReply', hasSender: false, error });
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'flush': {
|
|
98
|
+
void flush().then(
|
|
99
|
+
() => {
|
|
100
|
+
send({ type: 'flushReply', error: generateFlushError() });
|
|
101
|
+
},
|
|
102
|
+
error => {
|
|
103
|
+
send({ type: 'flushReply', error: generateFlushError(error) });
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
case 'send': {
|
|
109
|
+
if (!slogSender) {
|
|
110
|
+
logger.warn('Received send with no sender available');
|
|
111
|
+
} else {
|
|
112
|
+
try {
|
|
113
|
+
slogSender(harden(msg.obj));
|
|
114
|
+
} catch (e) {
|
|
115
|
+
sendErrors.push(e);
|
|
121
116
|
}
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
default: {
|
|
125
|
-
// @ts-expect-error exhaustive type check
|
|
126
|
-
logger.warn('received unknown message type', msg.type);
|
|
127
117
|
}
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
default: {
|
|
121
|
+
// @ts-expect-error exhaustive type check
|
|
122
|
+
logger.warn('Received unknown message type', msg.type);
|
|
128
123
|
}
|
|
129
|
-
}
|
|
130
|
-
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
process.on('message', onMessage);
|
|
131
127
|
};
|
|
132
128
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
main().then(
|
|
131
|
+
() => {
|
|
132
|
+
process.exitCode = 0;
|
|
133
|
+
},
|
|
134
|
+
err => {
|
|
135
|
+
logger.error('Failed with', err);
|
|
136
|
+
process.exit(process.exitCode || 1);
|
|
137
|
+
},
|
|
138
|
+
);
|
package/src/slog-sender-pipe.js
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Export a `makeSlogSender` that spawns a
|
|
3
|
+
* {@link ./slog-sender-pipe-entrypoint.js} child process to which it forwards
|
|
4
|
+
* all slog entries via Node.js IPC with advanced (structured clone)
|
|
5
|
+
* serialization.
|
|
6
|
+
* https://nodejs.org/docs/latest/api/child_process.html#advanced-serialization
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
import { fork } from 'child_process';
|
|
2
10
|
import path from 'path';
|
|
11
|
+
import { promisify } from 'util';
|
|
3
12
|
import anylogger from 'anylogger';
|
|
4
13
|
|
|
14
|
+
import { q, Fail } from '@endo/errors';
|
|
15
|
+
import { makePromiseKit } from '@endo/promise-kit';
|
|
5
16
|
import { makeQueue } from '@endo/stream';
|
|
6
17
|
|
|
7
18
|
import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
|
|
8
19
|
|
|
9
|
-
const
|
|
10
|
-
const dirname = path.dirname(filename);
|
|
20
|
+
const dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
11
21
|
|
|
12
22
|
const logger = anylogger('slog-sender-pipe');
|
|
13
23
|
|
|
24
|
+
const sink = () => {};
|
|
25
|
+
|
|
14
26
|
/**
|
|
15
27
|
* @template {any[]} T
|
|
16
28
|
* @template R
|
|
@@ -24,168 +36,131 @@ const withMutex = operation => {
|
|
|
24
36
|
return async (...args) => {
|
|
25
37
|
await mutex.get();
|
|
26
38
|
const result = operation(...args);
|
|
27
|
-
mutex.put(
|
|
28
|
-
result.then(
|
|
29
|
-
() => {},
|
|
30
|
-
() => {},
|
|
31
|
-
),
|
|
32
|
-
);
|
|
39
|
+
mutex.put(result.then(sink, sink));
|
|
33
40
|
return result;
|
|
34
41
|
};
|
|
35
42
|
};
|
|
36
43
|
|
|
37
44
|
/**
|
|
38
|
-
* @
|
|
39
|
-
* @
|
|
40
|
-
* @property {boolean} hasSender
|
|
41
|
-
* @property {Error} [error]
|
|
45
|
+
* @template [P=unknown]
|
|
46
|
+
* @typedef {{ type: string, error?: Error } & P} PipeReply
|
|
42
47
|
*/
|
|
48
|
+
|
|
43
49
|
/**
|
|
44
|
-
* @typedef {
|
|
45
|
-
*
|
|
46
|
-
*
|
|
50
|
+
* @typedef {{
|
|
51
|
+
* init: {
|
|
52
|
+
* message: import('./slog-sender-pipe-entrypoint.js').InitMessage;
|
|
53
|
+
* reply: PipeReply<{ hasSender: boolean }>;
|
|
54
|
+
* };
|
|
55
|
+
* flush: {
|
|
56
|
+
* message: import('./slog-sender-pipe-entrypoint.js').FlushMessage;
|
|
57
|
+
* reply: PipeReply<{}>;
|
|
58
|
+
* };
|
|
59
|
+
* }} SlogSenderPipeAPI
|
|
60
|
+
*
|
|
61
|
+
* @typedef {keyof SlogSenderPipeAPI} PipeAPICommand
|
|
62
|
+
* @typedef {SlogSenderPipeAPI[PipeAPICommand]["reply"]} PipeAPIReply
|
|
47
63
|
*/
|
|
48
|
-
/** @typedef {SlogSenderInitReply | SlogSenderFlushReply} SlogSenderPipeWaitReplies */
|
|
49
64
|
|
|
50
|
-
/** @param {import('.').MakeSlogSenderOptions}
|
|
51
|
-
export const makeSlogSender = async
|
|
65
|
+
/** @param {import('.').MakeSlogSenderOptions} options */
|
|
66
|
+
export const makeSlogSender = async options => {
|
|
67
|
+
const { env = {} } = options;
|
|
52
68
|
const { registerShutdown } = makeShutdown();
|
|
69
|
+
|
|
53
70
|
const cp = fork(path.join(dirname, 'slog-sender-pipe-entrypoint.js'), [], {
|
|
54
|
-
stdio: ['
|
|
71
|
+
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
|
55
72
|
serialization: 'advanced',
|
|
73
|
+
env,
|
|
56
74
|
});
|
|
57
75
|
// logger.log('done fork');
|
|
58
76
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
resolve();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
})
|
|
75
|
-
),
|
|
76
|
-
);
|
|
77
|
+
const exitKit = makePromiseKit();
|
|
78
|
+
cp.on('error', error => {
|
|
79
|
+
// An exit event *might* be coming, so wait a tick.
|
|
80
|
+
setImmediate(() => exitKit.resolve({ error }));
|
|
81
|
+
});
|
|
82
|
+
cp.on('exit', (exitCode, signal) => {
|
|
83
|
+
exitKit.resolve({ exitCode, signal });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
/** @type {(msg: Record<string, unknown> & {type: string}) => Promise<void>} */
|
|
87
|
+
const rawSend = promisify(cp.send.bind(cp));
|
|
88
|
+
const pipeSend = withMutex(rawSend);
|
|
77
89
|
|
|
78
|
-
/**
|
|
79
|
-
* @typedef {{
|
|
80
|
-
* init: {
|
|
81
|
-
* message: import('./slog-sender-pipe-entrypoint').InitMessage;
|
|
82
|
-
* reply: SlogSenderInitReply;
|
|
83
|
-
* };
|
|
84
|
-
* flush: {
|
|
85
|
-
* message: import('./slog-sender-pipe-entrypoint').FlushMessage;
|
|
86
|
-
* reply: SlogSenderFlushReply;
|
|
87
|
-
* };
|
|
88
|
-
* }} SlogSenderWaitMessagesAndReplies
|
|
89
|
-
*/
|
|
90
|
-
|
|
91
|
-
/** @typedef {keyof SlogSenderWaitMessagesAndReplies} SendWaitCommands */
|
|
92
|
-
/**
|
|
93
|
-
* @template {SlogSenderPipeWaitReplies} T
|
|
94
|
-
* @typedef {Omit<T, 'type' | 'error'>} ReplyPayload
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
/** @type {import('@endo/stream').AsyncQueue<SlogSenderPipeWaitReplies>} */
|
|
90
|
+
/** @type {import('@endo/stream').AsyncQueue<PipeAPIReply>} */
|
|
98
91
|
const sendWaitQueue = makeQueue();
|
|
99
|
-
/** @type {
|
|
92
|
+
/** @type {PipeAPICommand | undefined} */
|
|
100
93
|
let sendWaitType;
|
|
101
94
|
|
|
102
95
|
const sendWaitReply = withMutex(
|
|
103
96
|
/**
|
|
104
|
-
* @template {
|
|
97
|
+
* @template {PipeAPICommand} T
|
|
105
98
|
* @param {T} type
|
|
106
|
-
* @param {Omit<
|
|
107
|
-
* @returns {Promise<
|
|
99
|
+
* @param {Omit<SlogSenderPipeAPI[T]["message"], 'type'>} payload
|
|
100
|
+
* @returns {Promise<Omit<SlogSenderPipeAPI[T]["reply"], keyof PipeReply>>}
|
|
108
101
|
*/
|
|
109
102
|
async (type, payload) => {
|
|
110
|
-
!sendWaitType ||
|
|
103
|
+
!sendWaitType || Fail`Invalid mutex state`;
|
|
111
104
|
|
|
112
105
|
const msg = { ...payload, type };
|
|
113
106
|
|
|
114
107
|
sendWaitType = type;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
return rest;
|
|
129
|
-
},
|
|
130
|
-
)
|
|
131
|
-
.finally(() => {
|
|
132
|
-
sendWaitType = undefined;
|
|
133
|
-
});
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
cp.on(
|
|
138
|
-
'message',
|
|
139
|
-
/** @param { SlogSenderPipeWaitReplies } msg */
|
|
140
|
-
msg => {
|
|
141
|
-
// logger.log('received', msg);
|
|
142
|
-
if (
|
|
143
|
-
!msg ||
|
|
144
|
-
typeof msg !== 'object' ||
|
|
145
|
-
msg.type !== `${sendWaitType}Reply`
|
|
146
|
-
) {
|
|
147
|
-
logger.warn('Received unexpected message', msg);
|
|
148
|
-
return;
|
|
108
|
+
await null;
|
|
109
|
+
try {
|
|
110
|
+
await pipeSend(msg);
|
|
111
|
+
/** @type {SlogSenderPipeAPI[T]["reply"]} */
|
|
112
|
+
const reply = await sendWaitQueue.get();
|
|
113
|
+
const { type: replyType, error, ...rest } = reply;
|
|
114
|
+
replyType === `${type}Reply` ||
|
|
115
|
+
Fail`Unexpected reply type ${q(replyType)}`;
|
|
116
|
+
if (error) throw error;
|
|
117
|
+
return rest;
|
|
118
|
+
} finally {
|
|
119
|
+
sendWaitType = undefined;
|
|
149
120
|
}
|
|
150
|
-
|
|
151
|
-
sendWaitQueue.put(msg);
|
|
152
121
|
},
|
|
153
122
|
);
|
|
154
123
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
124
|
+
/** @param {PipeReply} msg */
|
|
125
|
+
const onMessage = msg => {
|
|
126
|
+
// logger.log('received', msg);
|
|
127
|
+
if (!msg || msg.type !== `${sendWaitType}Reply`) {
|
|
128
|
+
logger.warn('Received unexpected message', msg);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
sendWaitQueue.put(msg);
|
|
133
|
+
};
|
|
134
|
+
cp.on('message', onMessage);
|
|
158
135
|
|
|
159
|
-
const
|
|
160
|
-
|
|
136
|
+
const flush = async () => {
|
|
137
|
+
await sendWaitReply('flush', {});
|
|
161
138
|
};
|
|
162
139
|
|
|
163
140
|
const shutdown = async () => {
|
|
164
141
|
// logger.log('shutdown');
|
|
165
|
-
if (!cp.connected)
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
142
|
+
if (!cp.connected) return;
|
|
168
143
|
|
|
169
144
|
await flush();
|
|
170
145
|
cp.disconnect();
|
|
146
|
+
await exitKit.promise;
|
|
171
147
|
};
|
|
172
148
|
registerShutdown(shutdown);
|
|
173
149
|
|
|
174
|
-
const { hasSender } = await init
|
|
150
|
+
const { hasSender } = await sendWaitReply('init', { options }).catch(err => {
|
|
175
151
|
cp.disconnect();
|
|
176
152
|
throw err;
|
|
177
153
|
});
|
|
178
|
-
|
|
179
154
|
if (!hasSender) {
|
|
180
155
|
cp.disconnect();
|
|
181
156
|
return undefined;
|
|
182
157
|
}
|
|
183
158
|
|
|
184
|
-
const slogSender =
|
|
159
|
+
const slogSender = obj => {
|
|
160
|
+
void pipeSend({ type: 'send', obj }).catch(sink);
|
|
161
|
+
};
|
|
185
162
|
return Object.assign(slogSender, {
|
|
186
|
-
forceFlush:
|
|
187
|
-
await flush();
|
|
188
|
-
},
|
|
163
|
+
forceFlush: flush,
|
|
189
164
|
shutdown,
|
|
190
165
|
usesJsonObject: false,
|
|
191
166
|
});
|
package/src/slog-to-otel.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import otel, { SpanStatusCode } from '@opentelemetry/api';
|
|
2
2
|
|
|
3
|
+
import { Fail, q } from '@endo/errors';
|
|
3
4
|
import { makeMarshal, Remotable } from '@endo/marshal';
|
|
4
|
-
import { Fail, q } from '@agoric/assert';
|
|
5
5
|
|
|
6
6
|
import { makeLegacyMap } from '@agoric/store';
|
|
7
7
|
import {
|
|
@@ -14,11 +14,8 @@ import {
|
|
|
14
14
|
|
|
15
15
|
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.VERBOSE);
|
|
16
16
|
|
|
17
|
-
/** @
|
|
18
|
-
/** @
|
|
19
|
-
/** @typedef {import('@opentelemetry/api').SpanContext} SpanContext */
|
|
20
|
-
/** @typedef {import('@opentelemetry/api').SpanOptions} SpanOptions */
|
|
21
|
-
/** @typedef {import('@opentelemetry/api').SpanAttributes} SpanAttributes */
|
|
17
|
+
/** @import {Span, Link as SpanLink} from '@opentelemetry/api' */
|
|
18
|
+
/** @import {SpanContext, SpanOptions} from '@opentelemetry/api' */
|
|
22
19
|
|
|
23
20
|
const { assign } = Object;
|
|
24
21
|
|
|
@@ -54,9 +51,9 @@ const serializeInto = (value, prefix, target = {}, depth = 3) => {
|
|
|
54
51
|
} else {
|
|
55
52
|
const proto = Object.getPrototypeOf(value);
|
|
56
53
|
if (proto == null || proto === Object.prototype) {
|
|
57
|
-
|
|
58
|
-
serializeInto(nested, `${prefix}.${key}`, target, depth)
|
|
59
|
-
|
|
54
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
55
|
+
serializeInto(nested, `${prefix}.${key}`, target, depth);
|
|
56
|
+
}
|
|
60
57
|
return target;
|
|
61
58
|
}
|
|
62
59
|
}
|
|
@@ -142,7 +139,10 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
142
139
|
serializeBodyFormat: 'smallcaps',
|
|
143
140
|
});
|
|
144
141
|
|
|
145
|
-
/**
|
|
142
|
+
/**
|
|
143
|
+
* @param {import('@agoric/swingset-vat').SwingSetCapData} data
|
|
144
|
+
* @returns {any}
|
|
145
|
+
*/
|
|
146
146
|
const unserialize = data => {
|
|
147
147
|
try {
|
|
148
148
|
const body = rawUnserialize(data);
|
|
@@ -915,7 +915,7 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
915
915
|
break;
|
|
916
916
|
}
|
|
917
917
|
case 'cosmic-swingset-upgrade-finish': {
|
|
918
|
-
spans.pop(['
|
|
918
|
+
spans.pop(['upgrade', slogAttrs.blockHeight]);
|
|
919
919
|
dbTransactionManager.end();
|
|
920
920
|
break;
|
|
921
921
|
}
|
|
@@ -971,6 +971,21 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
971
971
|
spans.pop('bridge-inbound');
|
|
972
972
|
break;
|
|
973
973
|
}
|
|
974
|
+
case 'cosmic-swingset-timer-poll': {
|
|
975
|
+
spans.push(['timer-poll', slogAttrs.blockTime]);
|
|
976
|
+
spans.pop('timer-poll');
|
|
977
|
+
break;
|
|
978
|
+
}
|
|
979
|
+
case 'cosmic-swingset-inject-kernel-upgrade-events': {
|
|
980
|
+
spans.push('kernel-upgrade-events');
|
|
981
|
+
spans.pop('kernel-upgrade-events');
|
|
982
|
+
break;
|
|
983
|
+
}
|
|
984
|
+
case 'cosmic-swingset-install-bundle': {
|
|
985
|
+
spans.push(['install-bundle', slogAttrs.endoZipBase64Sha512]);
|
|
986
|
+
spans.pop('install-bundle');
|
|
987
|
+
break;
|
|
988
|
+
}
|
|
974
989
|
case 'cosmic-swingset-end-block-start': {
|
|
975
990
|
// Add `end-block` as an event onto the encompassing `block` span
|
|
976
991
|
spans.top()?.addEvent('end-block-action', cleanAttrs(slogAttrs), now);
|