diff --git a/docs/env.md b/docs/env.md index 52bc67d5cad..834719a1333 100644 --- a/docs/env.md +++ b/docs/env.md @@ -115,6 +115,12 @@ Same as SLOGFILE, but for solo instead of chain. Lifetime: ? +## SOLO_SLOGSENDER + +Same as SLOGSENDER, but for solo instead of chain. + +Lifetime: ? + ## SOLO_MAX_DEBUG_LENGTH Affects: solo @@ -137,6 +143,21 @@ debug logs should be written. Lifetime: ? +## SLOGSENDER + +Affects: cosmic-swingset + +Purpose: intercept the SwingSet LOG file in realtime + +Description: when nonempty, use the value as a module specifier. The module +will be loaded by `@agoric/telemetry/src/make-slog-sender.js`, via +`import(moduleSpec)`, and then the exported `makeSlogSender` function creates a +`slogSender`. Then, every time a SLOG object is written by SwingSet, +`slogSender(slogObject)` will be called. + +The default is `'@agoric/telemetry/src/flight-recorder.js'`, which writes to an +mmap'ed circular buffer. + ## SWINGSET_WORKER_TYPE Affects: solo diff --git a/packages/cosmic-swingset/src/chain-main.js b/packages/cosmic-swingset/src/chain-main.js index ea4ecc96937..fbb6b37ffe2 100644 --- a/packages/cosmic-swingset/src/chain-main.js +++ b/packages/cosmic-swingset/src/chain-main.js @@ -7,6 +7,7 @@ import { } from '@agoric/swingset-vat/src/devices/mailbox.js'; import { assert, details as X } from '@agoric/assert'; +import { makeSlogSenderFromModule } from '@agoric/telemetry'; import { launch } from './launch-chain.js'; import makeBlockManager from './block-manager.js'; @@ -249,7 +250,12 @@ export default async function main(progname, args, { env, homedir, agcc }) { ), ).pathname; const { metricsProvider } = getTelemetryProviders({ console, env }); - const slogFile = env.SLOGFILE; + + const { SLOGFILE, SLOGSENDER } = env; + const slogSender = await makeSlogSenderFromModule(SLOGSENDER, { + stateDir: stateDBDir, + }); + const consensusMode = env.DEBUG === undefined; const s = await launch( stateDBDir, @@ -260,7 +266,8 @@ export default async function main(progname, args, { env, homedir, agcc }) { argv, undefined, metricsProvider, - slogFile, + SLOGFILE, + slogSender, consensusMode, ); return s; diff --git a/packages/cosmic-swingset/src/launch-chain.js b/packages/cosmic-swingset/src/launch-chain.js index e7f24ab7cfd..705c626397a 100644 --- a/packages/cosmic-swingset/src/launch-chain.js +++ b/packages/cosmic-swingset/src/launch-chain.js @@ -35,7 +35,7 @@ async function buildSwingset( hostStorage, vatconfig, argv, - { consensusMode, debugName = undefined, slogCallbacks, slogFile }, + { consensusMode, debugName = undefined, slogCallbacks, slogFile, slogSender }, ) { const debugPrefix = debugName === undefined ? '' : `${debugName}:`; let config = await loadSwingsetConfigFile(vatconfig); @@ -78,7 +78,12 @@ async function buildSwingset( const controller = await makeSwingsetController( hostStorage, deviceEndowments, - { overrideVatManagerOptions: { consensusMode }, slogCallbacks, slogFile }, + { + overrideVatManagerOptions: { consensusMode }, + slogCallbacks, + slogFile, + slogSender, + }, ); // We DON'T want to run the kernel yet, only when the application decides @@ -140,6 +145,7 @@ export async function launch( debugName = undefined, meterProvider = DEFAULT_METER_PROVIDER, slogFile = undefined, + slogSender, consensusMode = false, ) { console.info('Launching SwingSet kernel'); @@ -172,6 +178,7 @@ export async function launch( debugName, slogCallbacks, slogFile, + slogSender, consensusMode, }, ); diff --git a/packages/cosmic-swingset/src/sim-chain.js b/packages/cosmic-swingset/src/sim-chain.js index d5609299262..36736fccde4 100644 --- a/packages/cosmic-swingset/src/sim-chain.js +++ b/packages/cosmic-swingset/src/sim-chain.js @@ -10,6 +10,8 @@ import { import anylogger from 'anylogger'; +import { makeSlogSenderFromModule } from '@agoric/telemetry'; + import { resolve as importMetaResolve } from 'import-meta-resolve'; import { assert, details as X } from '@agoric/assert'; import { makeWithQueue } from '@agoric/vats/src/queue.js'; @@ -83,6 +85,12 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) { console, env: process.env, }); + + const { SLOGFILE, SLOGSENDER } = process.env; + const slogSender = await makeSlogSenderFromModule(SLOGSENDER, { + stateDir: stateDBdir, + }); + const s = await launch( stateDBdir, mailboxStorage, @@ -92,6 +100,8 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) { argv, GCI, // debugName metricsProvider, + SLOGFILE, + slogSender, ); const { savedHeight, savedActions, savedChainSends } = s; diff --git a/packages/solo/package.json b/packages/solo/package.json index d0dce7069b5..f8e728cd426 100644 --- a/packages/solo/package.json +++ b/packages/solo/package.json @@ -24,6 +24,7 @@ "dependencies": { "@agoric/access-token": "^0.4.16", "@agoric/assert": "^0.3.15", + "@agoric/telemetry": "^0.1.0", "@endo/captp": "^1.10.12", "@agoric/cosmic-swingset": "^0.34.4", "@agoric/eventual-send": "^0.14.0", diff --git a/packages/solo/src/start.js b/packages/solo/src/start.js index 5a7c730a85c..55499b44e87 100644 --- a/packages/solo/src/start.js +++ b/packages/solo/src/start.js @@ -15,6 +15,7 @@ import anylogger from 'anylogger'; // import djson from 'deterministic-json'; import { assert, details as X } from '@agoric/assert'; +import { makeSlogSenderFromModule } from '@agoric/telemetry'; import { loadSwingsetConfigFile, buildCommand, @@ -156,11 +157,14 @@ const buildSwingset = async ( } await initializeSwingset(config, argv, hostStorage); } - const slogFile = process.env.SOLO_SLOGFILE; + const { SOLO_SLOGFILE: slogFile, SOLO_SLOGSENDER } = process.env; + const slogSender = await makeSlogSenderFromModule(SOLO_SLOGSENDER, { + stateDir: kernelStateDBDir, + }); const controller = await makeSwingsetController( hostStorage, deviceEndowments, - { slogFile }, + { slogFile, slogSender }, ); async function saveState() { diff --git a/packages/telemetry/src/index.js b/packages/telemetry/src/index.js index e2eb2d1121e..ba797fb341b 100644 --- a/packages/telemetry/src/index.js +++ b/packages/telemetry/src/index.js @@ -3,6 +3,8 @@ import { MeterProvider } from '@opentelemetry/metrics'; import { PrometheusExporter } from '@opentelemetry/exporter-prometheus'; +export * from './make-slog-sender.js'; + /** * @typedef {Object} Powers * @property {{ warn: Console['warn'] }} console diff --git a/packages/telemetry/src/make-slog-sender.js b/packages/telemetry/src/make-slog-sender.js new file mode 100644 index 00000000000..57a52045426 --- /dev/null +++ b/packages/telemetry/src/make-slog-sender.js @@ -0,0 +1,26 @@ +// @ts-check +import path from 'path'; + +export const DEFAULT_SLOG_SENDER_MODULE = + '@agoric/telemetry/src/flight-recorder.js'; + +export const makeSlogSenderFromModule = async ( + slogSenderModule = DEFAULT_SLOG_SENDER_MODULE, + makerOpts = {}, +) => { + if (!slogSenderModule) { + return undefined; + } + + if (slogSenderModule.startsWith('.')) { + // Resolve relative to the current working directory. + slogSenderModule = path.resolve(slogSenderModule); + } + + console.warn(`Loading makeSlogSender from ${slogSenderModule}`); + const { makeSlogSender: maker } = await import(slogSenderModule); + if (typeof maker !== 'function') { + throw Error(`${slogSenderModule} did not export a makeSlogSender function`); + } + return maker(makerOpts); +};