@agoric/telemetry 0.6.3-other-dev-3eb1a1d.0 → 0.6.3-other-dev-d15096d.0.d15096d
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 +29 -28
- package/src/context-aware-slog-file.js +7 -2
- package/src/context-aware-slog.js +11 -7
- package/src/flight-recorder.js +39 -19
- package/src/frcat-entrypoint.js +2 -2
- package/src/index.js +29 -26
- package/src/make-slog-sender.js +129 -109
- package/src/otel-and-flight-recorder.js +5 -1
- package/src/otel-context-aware-slog.js +12 -6
- package/src/otel-metrics.js +230 -0
- package/src/otel-trace.js +11 -1
- package/src/prometheus.js +22 -0
- package/src/serialize-slog-obj.js +32 -4
- package/src/slog-file.js +5 -1
- package/src/slog-sender-pipe-entrypoint.js +74 -69
- package/src/slog-sender-pipe.js +96 -111
- package/src/slog-to-otel.js +12 -6
- package/test/flight-recorder.test.js +42 -11
|
@@ -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
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { makeFsStreamWriter } from '@agoric/internal/src/node/fs-stream.js';
|
|
2
2
|
import { serializeSlogObj } from './serialize-slog-obj.js';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* @import {MakeSlogSenderOptions} from './index.js';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** @param {MakeSlogSenderOptions} opts */
|
|
5
9
|
export const makeSlogSender = async ({ env: { SLOGFILE } = {} } = {}) => {
|
|
6
10
|
const stream = await makeFsStreamWriter(SLOGFILE);
|
|
7
11
|
|
|
@@ -1,35 +1,45 @@
|
|
|
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
12
|
import anylogger from 'anylogger';
|
|
13
|
+
import { Fail } from '@endo/errors';
|
|
5
14
|
import { makeShutdown } from '@agoric/internal/src/node/shutdown.js';
|
|
6
15
|
|
|
7
16
|
import { makeSlogSender } from './make-slog-sender.js';
|
|
8
17
|
|
|
18
|
+
/**
|
|
19
|
+
* @import {PipeAPIReply} from './slog-sender-pipe.js';
|
|
20
|
+
* @import {MakeSlogSenderOptions} from './index.js';
|
|
21
|
+
* @import {SlogSender} from './index.js';
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
25
|
+
// @ts-ignore TODO remove when anylogger has types
|
|
9
26
|
const logger = anylogger('slog-sender-pipe-entrypoint');
|
|
10
27
|
|
|
11
|
-
/** @type {(msg:
|
|
28
|
+
/** @type {(msg: PipeAPIReply) => void} */
|
|
12
29
|
const send = Function.prototype.bind.call(process.send, process);
|
|
13
30
|
|
|
14
31
|
/**
|
|
15
|
-
* @typedef {
|
|
16
|
-
* @
|
|
17
|
-
* @
|
|
32
|
+
* @typedef {{type: 'init', options: MakeSlogSenderOptions }} InitMessage
|
|
33
|
+
* @typedef {{type: 'flush' }} FlushMessage
|
|
34
|
+
* @typedef {{type: 'send', obj: Record<string, unknown> }} SendMessage
|
|
35
|
+
*
|
|
36
|
+
* @typedef {InitMessage | FlushMessage} PipeAPIResponsefulMessage
|
|
37
|
+
* @typedef {SendMessage} PipeAPIResponselessMessage
|
|
38
|
+
* @typedef {PipeAPIResponsefulMessage | PipeAPIResponselessMessage} PipeAPIMessage
|
|
18
39
|
*/
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {object} SendMessage
|
|
21
|
-
* @property {'send'} type
|
|
22
|
-
* @property {object} obj
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {object} FlushMessage
|
|
26
|
-
* @property {'flush'} type
|
|
27
|
-
*/
|
|
28
|
-
/** @typedef {InitMessage | FlushMessage} SlogSenderPipeWaitMessages */
|
|
29
|
-
/** @typedef {SlogSenderPipeWaitMessages | SendMessage } SlogSenderPipeMessages */
|
|
30
40
|
|
|
31
41
|
const main = async () => {
|
|
32
|
-
/** @type {
|
|
42
|
+
/** @type {SlogSender | undefined} */
|
|
33
43
|
let slogSender;
|
|
34
44
|
|
|
35
45
|
const sendErrors = [];
|
|
@@ -42,11 +52,9 @@ const main = async () => {
|
|
|
42
52
|
process.disconnect?.();
|
|
43
53
|
});
|
|
44
54
|
|
|
45
|
-
/** @param {
|
|
55
|
+
/** @param {MakeSlogSenderOptions} opts */
|
|
46
56
|
const init = async ({ env, ...otherOpts } = {}) => {
|
|
47
|
-
|
|
48
|
-
assert.fail('Already initialized');
|
|
49
|
-
}
|
|
57
|
+
!slogSender || Fail`Already initialized`;
|
|
50
58
|
|
|
51
59
|
slogSender = await makeSlogSender({
|
|
52
60
|
...otherOpts,
|
|
@@ -57,9 +65,7 @@ const main = async () => {
|
|
|
57
65
|
};
|
|
58
66
|
|
|
59
67
|
const flush = async () => {
|
|
60
|
-
if (!slogSender)
|
|
61
|
-
assert.fail('No sender available');
|
|
62
|
-
}
|
|
68
|
+
if (!slogSender) throw Fail`No sender available`;
|
|
63
69
|
|
|
64
70
|
await slogSender.forceFlush?.();
|
|
65
71
|
};
|
|
@@ -77,56 +83,55 @@ const main = async () => {
|
|
|
77
83
|
return AggregateError(sendErrors.splice(0));
|
|
78
84
|
};
|
|
79
85
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
86
|
+
/** @param {PipeAPIMessage} msg */
|
|
87
|
+
const onMessage = msg => {
|
|
88
|
+
if (!msg || typeof msg !== 'object') {
|
|
89
|
+
logger.warn('Received invalid message', msg);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
87
92
|
|
|
88
|
-
|
|
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
|
-
}
|
|
93
|
+
switch (msg.type) {
|
|
94
|
+
case 'init': {
|
|
95
|
+
void init(msg.options).then(
|
|
96
|
+
hasSender => {
|
|
97
|
+
send({ type: 'initReply', hasSender });
|
|
98
|
+
},
|
|
99
|
+
error => {
|
|
100
|
+
send({ type: 'initReply', hasSender: false, error });
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'flush': {
|
|
106
|
+
void flush().then(
|
|
107
|
+
() => {
|
|
108
|
+
send({ type: 'flushReply', error: generateFlushError() });
|
|
109
|
+
},
|
|
110
|
+
error => {
|
|
111
|
+
send({ type: 'flushReply', error: generateFlushError(error) });
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case 'send': {
|
|
117
|
+
if (!slogSender) {
|
|
118
|
+
logger.warn('Received send with no sender available');
|
|
119
|
+
} else {
|
|
120
|
+
try {
|
|
121
|
+
slogSender(harden(msg.obj));
|
|
122
|
+
} catch (e) {
|
|
123
|
+
sendErrors.push(e);
|
|
120
124
|
}
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
default: {
|
|
124
|
-
// @ts-expect-error exhaustive type check
|
|
125
|
-
logger.warn('received unknown message type', msg.type);
|
|
126
125
|
}
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
default: {
|
|
129
|
+
// @ts-expect-error exhaustive type check
|
|
130
|
+
logger.warn('Received unknown message type', msg.type);
|
|
127
131
|
}
|
|
128
|
-
}
|
|
129
|
-
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
process.on('message', onMessage);
|
|
130
135
|
};
|
|
131
136
|
|
|
132
137
|
process.exitCode = 1;
|
package/src/slog-sender-pipe.js
CHANGED
|
@@ -1,190 +1,175 @@
|
|
|
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
|
|
|
20
|
+
/**
|
|
21
|
+
* @import {AsyncQueue} from '@endo/stream';
|
|
22
|
+
* @import {InitMessage} from './slog-sender-pipe-entrypoint.js';
|
|
23
|
+
* @import {FlushMessage} from './slog-sender-pipe-entrypoint.js';
|
|
24
|
+
* @import {MakeSlogSenderOptions} from './index.js';
|
|
25
|
+
*/
|
|
26
|
+
|
|
9
27
|
const dirname = path.dirname(new URL(import.meta.url).pathname);
|
|
10
28
|
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
+
// @ts-ignore TODO remove when anylogger has types
|
|
11
31
|
const logger = anylogger('slog-sender-pipe');
|
|
12
32
|
|
|
33
|
+
const sink = () => {};
|
|
34
|
+
|
|
13
35
|
/**
|
|
14
36
|
* @template {any[]} T
|
|
15
37
|
* @template R
|
|
16
38
|
* @param {(...args: T) => Promise<R>} operation
|
|
17
39
|
*/
|
|
18
40
|
const withMutex = operation => {
|
|
19
|
-
/** @type {
|
|
41
|
+
/** @type {AsyncQueue<void>} */
|
|
20
42
|
const mutex = makeQueue();
|
|
21
43
|
mutex.put(Promise.resolve());
|
|
22
44
|
/** @param {T} args */
|
|
23
45
|
return async (...args) => {
|
|
24
46
|
await mutex.get();
|
|
25
47
|
const result = operation(...args);
|
|
26
|
-
mutex.put(
|
|
27
|
-
result.then(
|
|
28
|
-
() => {},
|
|
29
|
-
() => {},
|
|
30
|
-
),
|
|
31
|
-
);
|
|
48
|
+
mutex.put(result.then(sink, sink));
|
|
32
49
|
return result;
|
|
33
50
|
};
|
|
34
51
|
};
|
|
35
52
|
|
|
36
53
|
/**
|
|
37
|
-
* @
|
|
38
|
-
* @
|
|
39
|
-
* @property {boolean} hasSender
|
|
40
|
-
* @property {Error} [error]
|
|
54
|
+
* @template [P=unknown]
|
|
55
|
+
* @typedef {{ type: string, error?: Error } & P} PipeReply
|
|
41
56
|
*/
|
|
57
|
+
|
|
42
58
|
/**
|
|
43
|
-
* @typedef {
|
|
44
|
-
*
|
|
45
|
-
*
|
|
59
|
+
* @typedef {{
|
|
60
|
+
* init: {
|
|
61
|
+
* message: InitMessage;
|
|
62
|
+
* reply: PipeReply<{ hasSender: boolean }>;
|
|
63
|
+
* };
|
|
64
|
+
* flush: {
|
|
65
|
+
* message: FlushMessage;
|
|
66
|
+
* reply: PipeReply<{}>;
|
|
67
|
+
* };
|
|
68
|
+
* }} SlogSenderPipeAPI
|
|
69
|
+
*
|
|
70
|
+
* @typedef {keyof SlogSenderPipeAPI} PipeAPICommand
|
|
71
|
+
* @typedef {SlogSenderPipeAPI[PipeAPICommand]["reply"]} PipeAPIReply
|
|
46
72
|
*/
|
|
47
|
-
/** @typedef {SlogSenderInitReply | SlogSenderFlushReply} SlogSenderPipeWaitReplies */
|
|
48
73
|
|
|
49
|
-
/** @param {
|
|
50
|
-
export const makeSlogSender = async
|
|
74
|
+
/** @param {MakeSlogSenderOptions} options */
|
|
75
|
+
export const makeSlogSender = async options => {
|
|
76
|
+
const { env = {} } = options;
|
|
51
77
|
const { registerShutdown } = makeShutdown();
|
|
78
|
+
|
|
52
79
|
const cp = fork(path.join(dirname, 'slog-sender-pipe-entrypoint.js'), [], {
|
|
53
|
-
stdio: ['
|
|
80
|
+
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
|
54
81
|
serialization: 'advanced',
|
|
82
|
+
env,
|
|
55
83
|
});
|
|
56
84
|
// logger.log('done fork');
|
|
57
85
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
cp.send(msg, err => {
|
|
67
|
-
if (err) {
|
|
68
|
-
reject(err);
|
|
69
|
-
} else {
|
|
70
|
-
resolve();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
})
|
|
74
|
-
),
|
|
75
|
-
);
|
|
86
|
+
const exitKit = makePromiseKit();
|
|
87
|
+
cp.on('error', error => {
|
|
88
|
+
// An exit event *might* be coming, so wait a tick.
|
|
89
|
+
setImmediate(() => exitKit.resolve({ error }));
|
|
90
|
+
});
|
|
91
|
+
cp.on('exit', (exitCode, signal) => {
|
|
92
|
+
exitKit.resolve({ exitCode, signal });
|
|
93
|
+
});
|
|
76
94
|
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
* };
|
|
83
|
-
* flush: {
|
|
84
|
-
* message: import('./slog-sender-pipe-entrypoint.js').FlushMessage;
|
|
85
|
-
* reply: SlogSenderFlushReply;
|
|
86
|
-
* };
|
|
87
|
-
* }} SlogSenderWaitMessagesAndReplies
|
|
88
|
-
*/
|
|
89
|
-
|
|
90
|
-
/** @typedef {keyof SlogSenderWaitMessagesAndReplies} SendWaitCommands */
|
|
91
|
-
/**
|
|
92
|
-
* @template {SlogSenderPipeWaitReplies} T
|
|
93
|
-
* @typedef {Omit<T, 'type' | 'error'>} ReplyPayload
|
|
94
|
-
*/
|
|
95
|
-
|
|
96
|
-
/** @type {import('@endo/stream').AsyncQueue<SlogSenderPipeWaitReplies>} */
|
|
95
|
+
/** @type {(msg: Record<string, unknown> & {type: string}) => Promise<void>} */
|
|
96
|
+
const rawSend = promisify(cp.send.bind(cp));
|
|
97
|
+
const pipeSend = withMutex(rawSend);
|
|
98
|
+
|
|
99
|
+
/** @type {AsyncQueue<PipeAPIReply>} */
|
|
97
100
|
const sendWaitQueue = makeQueue();
|
|
98
|
-
/** @type {
|
|
101
|
+
/** @type {PipeAPICommand | undefined} */
|
|
99
102
|
let sendWaitType;
|
|
100
103
|
|
|
101
104
|
const sendWaitReply = withMutex(
|
|
102
105
|
/**
|
|
103
|
-
* @template {
|
|
106
|
+
* @template {PipeAPICommand} T
|
|
104
107
|
* @param {T} type
|
|
105
|
-
* @param {Omit<
|
|
106
|
-
* @returns {Promise<
|
|
108
|
+
* @param {Omit<SlogSenderPipeAPI[T]["message"], 'type'>} payload
|
|
109
|
+
* @returns {Promise<Omit<SlogSenderPipeAPI[T]["reply"], keyof PipeReply>>}
|
|
107
110
|
*/
|
|
108
111
|
async (type, payload) => {
|
|
109
|
-
!sendWaitType ||
|
|
112
|
+
!sendWaitType || Fail`Invalid mutex state`;
|
|
110
113
|
|
|
111
114
|
const msg = { ...payload, type };
|
|
112
115
|
|
|
113
116
|
sendWaitType = type;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
return rest;
|
|
128
|
-
},
|
|
129
|
-
)
|
|
130
|
-
.finally(() => {
|
|
131
|
-
sendWaitType = undefined;
|
|
132
|
-
});
|
|
133
|
-
},
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
cp.on(
|
|
137
|
-
'message',
|
|
138
|
-
/** @param { SlogSenderPipeWaitReplies } msg */
|
|
139
|
-
msg => {
|
|
140
|
-
// logger.log('received', msg);
|
|
141
|
-
if (
|
|
142
|
-
!msg ||
|
|
143
|
-
typeof msg !== 'object' ||
|
|
144
|
-
msg.type !== `${sendWaitType}Reply`
|
|
145
|
-
) {
|
|
146
|
-
logger.warn('Received unexpected message', msg);
|
|
147
|
-
return;
|
|
117
|
+
await null;
|
|
118
|
+
try {
|
|
119
|
+
await pipeSend(msg);
|
|
120
|
+
/** @type {SlogSenderPipeAPI[T]["reply"]} */
|
|
121
|
+
const reply = await sendWaitQueue.get();
|
|
122
|
+
const { type: replyType, error, ...rest } = reply;
|
|
123
|
+
replyType === `${type}Reply` ||
|
|
124
|
+
Fail`Unexpected reply type ${q(replyType)}`;
|
|
125
|
+
if (error) throw error;
|
|
126
|
+
return rest;
|
|
127
|
+
} finally {
|
|
128
|
+
sendWaitType = undefined;
|
|
148
129
|
}
|
|
149
|
-
|
|
150
|
-
sendWaitQueue.put(msg);
|
|
151
130
|
},
|
|
152
131
|
);
|
|
153
132
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
133
|
+
/** @param {PipeReply} msg */
|
|
134
|
+
const onMessage = msg => {
|
|
135
|
+
// logger.log('received', msg);
|
|
136
|
+
if (!msg || msg.type !== `${sendWaitType}Reply`) {
|
|
137
|
+
logger.warn('Received unexpected message', msg);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
sendWaitQueue.put(msg);
|
|
142
|
+
};
|
|
143
|
+
cp.on('message', onMessage);
|
|
157
144
|
|
|
158
|
-
const
|
|
159
|
-
|
|
145
|
+
const flush = async () => {
|
|
146
|
+
await sendWaitReply('flush', {});
|
|
160
147
|
};
|
|
161
148
|
|
|
162
149
|
const shutdown = async () => {
|
|
163
150
|
// logger.log('shutdown');
|
|
164
|
-
if (!cp.connected)
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
151
|
+
if (!cp.connected) return;
|
|
167
152
|
|
|
168
153
|
await flush();
|
|
169
154
|
cp.disconnect();
|
|
155
|
+
await exitKit.promise;
|
|
170
156
|
};
|
|
171
157
|
registerShutdown(shutdown);
|
|
172
158
|
|
|
173
|
-
const { hasSender } = await init
|
|
159
|
+
const { hasSender } = await sendWaitReply('init', { options }).catch(err => {
|
|
174
160
|
cp.disconnect();
|
|
175
161
|
throw err;
|
|
176
162
|
});
|
|
177
|
-
|
|
178
163
|
if (!hasSender) {
|
|
179
164
|
cp.disconnect();
|
|
180
165
|
return undefined;
|
|
181
166
|
}
|
|
182
167
|
|
|
183
|
-
const slogSender =
|
|
168
|
+
const slogSender = obj => {
|
|
169
|
+
void pipeSend({ type: 'send', obj }).catch(sink);
|
|
170
|
+
};
|
|
184
171
|
return Object.assign(slogSender, {
|
|
185
|
-
forceFlush:
|
|
186
|
-
await flush();
|
|
187
|
-
},
|
|
172
|
+
forceFlush: flush,
|
|
188
173
|
shutdown,
|
|
189
174
|
usesJsonObject: false,
|
|
190
175
|
});
|
package/src/slog-to-otel.js
CHANGED
|
@@ -14,7 +14,13 @@ import {
|
|
|
14
14
|
|
|
15
15
|
// diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.VERBOSE);
|
|
16
16
|
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* @import {Span, Link as SpanLink} from '@opentelemetry/api'
|
|
19
|
+
* @import {Tracer} from '@opentelemetry/api';
|
|
20
|
+
* @import {SwingSetCapData} from '@agoric/swingset-vat';
|
|
21
|
+
* @import {Message} from '@agoric/swingset-vat';
|
|
22
|
+
* @import {KernelSyscallObject} from '@agoric/swingset-vat';
|
|
23
|
+
*/
|
|
18
24
|
/** @import {SpanContext, SpanOptions} from '@opentelemetry/api' */
|
|
19
25
|
|
|
20
26
|
const { assign } = Object;
|
|
@@ -76,7 +82,7 @@ export const floatSecondsToHiRes = sFloat => {
|
|
|
76
82
|
};
|
|
77
83
|
|
|
78
84
|
/**
|
|
79
|
-
* @param {
|
|
85
|
+
* @param {Tracer} tracer
|
|
80
86
|
* @param {Record<string, any>} [overrideAttrs]
|
|
81
87
|
*/
|
|
82
88
|
export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
@@ -140,7 +146,7 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
140
146
|
});
|
|
141
147
|
|
|
142
148
|
/**
|
|
143
|
-
* @param {
|
|
149
|
+
* @param {SwingSetCapData} data
|
|
144
150
|
* @returns {any}
|
|
145
151
|
*/
|
|
146
152
|
const unserialize = data => {
|
|
@@ -156,12 +162,12 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
156
162
|
/**
|
|
157
163
|
* @typedef {{
|
|
158
164
|
* method: string;
|
|
159
|
-
* args:
|
|
165
|
+
* args: SwingSetCapData;
|
|
160
166
|
* result?: string | undefined | null,
|
|
161
167
|
* }} OldMessage
|
|
162
168
|
*/
|
|
163
169
|
/** @typedef {ReturnType<typeof parseMsg>} ParsedMessage */
|
|
164
|
-
/** @param {
|
|
170
|
+
/** @param {Message | OldMessage} msg */
|
|
165
171
|
const parseMsg = msg => {
|
|
166
172
|
/** @type {string | symbol | null} */
|
|
167
173
|
let method = null;
|
|
@@ -699,7 +705,7 @@ export const makeSlogToOtelKit = (tracer, overrideAttrs = {}) => {
|
|
|
699
705
|
if (isReplaying) {
|
|
700
706
|
break;
|
|
701
707
|
}
|
|
702
|
-
/** @type {{ksc:
|
|
708
|
+
/** @type {{ksc: KernelSyscallObject } & Record<string, unknown>} */
|
|
703
709
|
const { ksc, vsc: _1, ...attrs } = slogAttrs;
|
|
704
710
|
if (!ksc) {
|
|
705
711
|
break;
|
|
@@ -12,27 +12,48 @@ const bufferTests = test.macro(
|
|
|
12
12
|
/**
|
|
13
13
|
*
|
|
14
14
|
* @param {*} t
|
|
15
|
-
* @param {{makeBuffer:
|
|
15
|
+
* @param {{makeBuffer: typeof makeSimpleCircularBuffer}} input
|
|
16
16
|
*/
|
|
17
17
|
async (t, input) => {
|
|
18
18
|
const BUFFER_SIZE = 512;
|
|
19
19
|
|
|
20
|
-
const { name: tmpFile, removeCallback } = tmp.fileSync(
|
|
21
|
-
|
|
20
|
+
const { name: tmpFile, removeCallback } = tmp.fileSync({
|
|
21
|
+
discardDescriptor: true,
|
|
22
|
+
});
|
|
23
|
+
t.teardown(removeCallback);
|
|
24
|
+
const { fileHandle, readCircBuf, writeCircBuf } = await input.makeBuffer({
|
|
22
25
|
circularBufferSize: BUFFER_SIZE,
|
|
23
26
|
circularBufferFilename: tmpFile,
|
|
24
27
|
});
|
|
25
|
-
const
|
|
28
|
+
const realSlogSender = makeSlogSenderFromBuffer({
|
|
29
|
+
fileHandle,
|
|
30
|
+
writeCircBuf,
|
|
31
|
+
});
|
|
32
|
+
let wasShutdown = false;
|
|
33
|
+
const shutdown = () => {
|
|
34
|
+
if (wasShutdown) return;
|
|
35
|
+
wasShutdown = true;
|
|
36
|
+
|
|
37
|
+
return realSlogSender.shutdown();
|
|
38
|
+
};
|
|
39
|
+
t.teardown(shutdown);
|
|
40
|
+
// To verify lack of attempted mutation by the consumer, send only hardened
|
|
41
|
+
// entries.
|
|
42
|
+
/** @type {typeof realSlogSender} */
|
|
43
|
+
const slogSender = Object.assign(
|
|
44
|
+
(obj, serialized) => realSlogSender(harden(obj), serialized),
|
|
45
|
+
realSlogSender,
|
|
46
|
+
);
|
|
26
47
|
slogSender({ type: 'start' });
|
|
27
48
|
await slogSender.forceFlush();
|
|
28
49
|
t.is(fs.readFileSync(tmpFile, { encoding: 'utf8' }).length, BUFFER_SIZE);
|
|
29
50
|
|
|
30
51
|
const len0 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT);
|
|
31
|
-
const { done: done0 } = readCircBuf(len0);
|
|
52
|
+
const { done: done0 } = await readCircBuf(len0);
|
|
32
53
|
t.false(done0, 'readCircBuf should not be done');
|
|
33
54
|
const dv0 = new DataView(len0.buffer);
|
|
34
55
|
const buf0 = new Uint8Array(Number(dv0.getBigUint64(0)));
|
|
35
|
-
const { done: done0b } = readCircBuf(buf0, len0.byteLength);
|
|
56
|
+
const { done: done0b } = await readCircBuf(buf0, len0.byteLength);
|
|
36
57
|
t.false(done0b, 'readCircBuf should not be done');
|
|
37
58
|
const buf0Str = new TextDecoder().decode(buf0);
|
|
38
59
|
t.is(buf0Str, `\n{"type":"start"}`, `start compare failed`);
|
|
@@ -51,12 +72,12 @@ const bufferTests = test.macro(
|
|
|
51
72
|
let offset = 0;
|
|
52
73
|
const len1 = new Uint8Array(BigUint64Array.BYTES_PER_ELEMENT);
|
|
53
74
|
for (let i = 490; i < last; i += 1) {
|
|
54
|
-
const { done: done1 } = readCircBuf(len1, offset);
|
|
75
|
+
const { done: done1 } = await readCircBuf(len1, offset);
|
|
55
76
|
offset += len1.byteLength;
|
|
56
77
|
t.false(done1, `readCircBuf ${i} should not be done`);
|
|
57
78
|
const dv1 = new DataView(len1.buffer);
|
|
58
79
|
const buf1 = new Uint8Array(Number(dv1.getBigUint64(0)));
|
|
59
|
-
const { done: done1b } = readCircBuf(buf1, offset);
|
|
80
|
+
const { done: done1b } = await readCircBuf(buf1, offset);
|
|
60
81
|
offset += buf1.byteLength;
|
|
61
82
|
t.false(done1b, `readCircBuf ${i} should not be done`);
|
|
62
83
|
const buf1Str = new TextDecoder().decode(buf1);
|
|
@@ -67,14 +88,24 @@ const bufferTests = test.macro(
|
|
|
67
88
|
);
|
|
68
89
|
}
|
|
69
90
|
|
|
70
|
-
const { done: done2 } = readCircBuf(len1, offset);
|
|
91
|
+
const { done: done2 } = await readCircBuf(len1, offset);
|
|
71
92
|
t.assert(done2, `readCircBuf ${last} should be done`);
|
|
72
93
|
|
|
73
94
|
slogSender(null, 'PRE-SERIALIZED');
|
|
74
95
|
await slogSender.forceFlush();
|
|
75
96
|
t.truthy(fs.readFileSync(tmpFile).includes('PRE-SERIALIZED'));
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
|
|
98
|
+
slogSender(null, 'PRE_SHUTDOWN');
|
|
99
|
+
const shutdownP = shutdown();
|
|
100
|
+
slogSender(null, 'POST_SHUTDOWN');
|
|
101
|
+
await shutdownP;
|
|
102
|
+
slogSender(null, 'SHUTDOWN_COMPLETED');
|
|
103
|
+
|
|
104
|
+
const finalContent = fs.readFileSync(tmpFile);
|
|
105
|
+
|
|
106
|
+
t.truthy(finalContent.includes('PRE_SHUTDOWN'));
|
|
107
|
+
t.falsy(finalContent.includes('POST_SHUTDOWN'));
|
|
108
|
+
t.falsy(finalContent.includes('SHUTDOWN_COMPLETED'));
|
|
78
109
|
},
|
|
79
110
|
);
|
|
80
111
|
|