Skip to content

Monitoring and logging

This page shows what you can observe in a running libspiffy node and how to wire it into your own logging and metrics. libspiffy 5.0.0 does not collect or export metrics itself. It gives you three things to build on: log records, coordinator events, and a few status getters.

libspiffy logs through package:logging. Nothing is printed until you listen to the root logger:

lib/logging.dart
import 'package:logging/logging.dart';
void setUpLogging() {
Logger.root.level = Level.INFO;
Logger.root.onRecord.listen((r) {
print('${r.time.toIso8601String()} ${r.level.name} ${r.loggerName}: ${r.message}'
'${r.error != null ? ' ${r.error}' : ''}');
});
}

Each component logs under its own name. The names are flat, not dotted, so filter on record.loggerName. Useful ones:

Logger name What it reports
LibSpiffyActorSystem Start-up, the P2P network chosen, peer redials, shutdown, a missing secureStorage (SEVERE)
ARCActor, SubmissionWatcherActor Broadcasts, status scans, the retry queue
HeaderSyncActor, BlockHeaderChain, SpiffyNodeBridge, LibSpiffyPeerHandler, LibSpiffy-SpiffyNode P2P header sync and peers
CdnHeaderSyncService, LibSpiffy-CDNSync CDN header sync
SPVActor Proof and BEEF validation
WalletCoordinatorActor, WalletManagerActor, BitcoinWalletAggregate Commands and the wallet aggregate
PaymentCoordinatorActor, InvoiceCoordinatorActor, PaymentChannelManagerActor Payments, invoices, channels
ImportActor, TransactionImportService, WhatsOnChainDataSource Wallet import and the data source
PostgresEventStore, PostgresWalletStorage PostgreSQL storage

A SEVERE record from LibSpiffyActorSystem saying no secureStorage was supplied means your keys are in memory only. Alert on it.

Subscribe to libspiffy.coordinatorEvents, or to one kind with libspiffy.coordinator.on<E>(), and count or alert on these. Fields are listed in Events.

Event When What to do with it
ErrorEvent Something failed that has no reply of its own to report it in. Carries walletId, source, message, stackTrace, and requestId when a request caused it. Count by source; alert on spikes.
BroadcastFailureEvent ARC refused or could not take a broadcast. Carries txid, error, willRetry. Count; a rising rate means ARC is refusing or unreachable.
TransactionConfirmedEvent A merkle proof put a wallet’s transaction in a block on the active chain (txid, blockHeight). Time from send to confirmation.
TransactionConfirmationRevertedEvent A confirmation was taken back by a reorganization or a contradicting header (reason). Alert: anything you did on the confirmation lost its evidence.
BlockHeadersStoredEvent Each batch of headers stored (headersStored, startHeight, endHeight, success, error, source). Header sync throughput; alert on success == false.
HeaderSyncStatusEvent Header sync caught up with its peers, or fell behind them. Carries a HeaderSyncStatus. Gauge of sync state.
WalletStatusEvent The coordinator shut down (status 'shutdown', message). Lifecycle log.
// Sketch: count failures by source with your own metrics client.
libspiffy.coordinatorEvents?.listen((event) {
switch (event) {
case ErrorEvent(:final source, :final message):
metrics.increment('libspiffy.error', tags: {'source': source});
log.warning('$source: $message');
case BroadcastFailureEvent(:final willRetry):
metrics.increment('libspiffy.broadcast_failure', tags: {'retry': '$willRetry'});
case HeaderSyncStatusEvent(:final status):
metrics.gauge('libspiffy.header_height', status.height);
metrics.gauge('libspiffy.peers', status.peerCount);
default:
break;
}
});

metrics and log stand for your own clients; they are not part of libspiffy.

HeaderSyncStatus has four fields:

Field Meaning
height Height of the active chain’s tip.
networkHeight The higher of height and what the connected peers reported in their handshake; 0 while no peer reported one. For showing progress.
synced Whether the last answer a peer gave held fewer than a full batch (2,000) of headers. The verdict on “caught up”.
peerCount Peers connected now. 0 means nothing is syncing.

Ask for it at any time with GetHeaderSyncStatusQuery. ask() completes with its HeaderSyncStatusResponse:

final response = await libspiffy.coordinator.ask(GetHeaderSyncStatusQuery());
final status = response.status;
print('${status.height}/${status.networkHeight}, synced: ${status.synced}');

A request that fails makes ask() throw CoordinatorFailure, and one with no reply within its timeout (one minute for this query) throws TimeoutException. Count both if you poll the status from a health check.

On first start, initialize() can import headers from a CDN. Its two callbacks are the only way to watch that phase:

  • onHeaderSyncProgress: (int current, int total, CdnSyncPhase phase) { ... }
  • onHeaderSyncResult: (CdnSyncResult result) { ... } with success, headersImported, finalHeight, elapsed and error.

See CDN header sync.

On LibSpiffyActorSystem:

Getter Meaning
isReady / ready The actors take commands. Completes before header sync is done.
isInitialized The system is up and not shut down.
networkHeight The network’s tip as best known: the higher of peers’ reports and the local chain; 0 while no peer is connected.
headerChain.bestHeight The height of the local header chain.

A readiness probe for a server can combine isReady with HeaderSyncStatus.synced and peerCount > 0.

There are no built-in counters, timers or exporters for message rates, actor memory, ARC response times, invoice rates or proof validation rates. If you need them, derive them from the events and log records above, or time your own calls.