feat: migrate to new backend logging convention - #4
Merged
Conversation
Replaces the previous custom Logger (six-level variadic LOG singleton)
with the convention defined in local-dev/logging.md:
- Five-method Logger interface: info, event, debug, error, scope.
Source ported from github.com/AquiGorka/go-logger, lives at
src/utils/logger/index.ts. Dual stdout-human / file-JSON output.
- src/config/logger.ts now exports createLogger() (no module-level
singleton). main.ts is the only place the root logger is created.
- NetworkEventBus becomes a DI'd class (takes { log } in constructor);
main.ts constructs it and passes deps into the soroban watcher,
scheduler, and HTTP routes.
- All sync free functions (refreshWasmRegistry, fetchCouncilTopology,
refreshTopology, coldStartScan, evaluateUnknownContract, etc.) take
{ log, bus } deps. Internal scoping via log.scope("funcName").
- WebSocket route exposes handleNetworkWs(deps) factory and
buildNetworkWsRouter(deps). v1.routes.ts now exports
buildApiRouter(deps).
- Removed local describeErr() helper — the new Logger stringifies
errors internally for both human and JSON sinks.
- Migrated all 42 LOG call sites + the one stray console.warn in
events/bus.ts.
No OTEL changes (this repo had no OTEL to begin with).
- NetworkEventBus: subscribe, publish entry breadcrumbs + debug for listener count and event kind. - soroban-watcher: publishMappedEvent + processRawEventBatch take log; callers updated. deno check src clean.
6 tasks
AquiGorka
added a commit
that referenced
this pull request
Jun 5, 2026
…roring provider-platform watcher pattern) (#5) ## Summary Refactors the topology / event-discovery surface to mirror `provider-platform`'s `event-watcher` pattern: **sync at boot, set listeners, update state as events come in**. The previous hourly periodic re-sync is gone; the new-council fallback that depended on a GitHub-sourced WASM hash registry is gone; what's left is a tight chain-event-driven loop that publishes within ~5 s of a new council going on-chain. Validated by a local-stack end-to-end smoke (`/Users/theahaco/repos/tmp/add-events-capture-framework-1/`) where two flow scripts deploy a fresh council mid-test, and the network-dashboard WS surfaces the resulting `council_formed → provider_added → channel_deposit → channel_bundle → channel_settlement` sequence in real time. ## The four architectural changes 1. **Remove the hourly topology resync** (`src/core/sync/scheduler.ts`). The scheduler now does only the 60-second rolling-window sweep (memory cap on the 24h counter window). Topology arrives at boot via `fetchCouncilTopology` and updates via the contract-init listener. 2. **Drop the WASM hash registry** (`src/core/sync/wasm-registry.ts` deleted; `src/main.ts`, `src/core/sync/topology-refresh.ts` updated). The registry was sourced from `Moonlight-Protocol/soroban-core`'s GitHub releases and returned `HTTP 403` for any unauthenticated fetch — which meant the new-council fallback was effectively a permanent no-op in local-dev. `isKnownChannelAuthHash` / `isWasmRegistryReady` / `knownHashCount` had no callers after change #3. 3. **Ungate the contract-init listener** (`src/core/sync/contract-init-listener.ts`, `src/core/sync/soroban-watcher.ts`). The watcher always polls `getEvents` for `contract_initialized` from unknown contracts (no more `isContractInitListenerEnabled()` gate). Each unknown is held in `pendingAdoption` with an `observedAtLedger` for back-fill (see #4) and a TTL of `PENDING_TTL_MS = 120 s` — long enough that a council registered AFTER its on-chain deploy still gets adopted on a subsequent tick. Past the TTL the contract is cached as `notMoonlight` so a chatty unrelated contract can't drag us into infinite topology refreshes. 4. **Back-fill on adoption** (`src/core/sync/soroban-watcher.ts:backfillFromLedger`, threaded via `drainPendingAdoptions`'s new `backfillFromLedger` dep). Forward polling resumes from `lastLedgerSeen + 1`, so any chain event between the deploy ledger and the adoption tick (most notably `provider_added` and the first SAC `transfer`) would otherwise be lost. The back-fill scan walks every currently-watched contract from the earliest adopted `observedAtLedger` to head, maps each event, and publishes via the bus. `networkState.recordEvent`'s dedup makes this safe to run concurrent with `pollTick`. The hot-path behaviour now matches `provider-platform/src/core/service/event-watcher/index.ts`: - **Boot**: `initFromDb` → `ensureWatcher` per ACTIVE membership. - **Listeners**: per-contract Soroban poll, narrowly-scoped. - **State updates as events come in**: handler emits via `emitForPp` / `emitForBundles`. - **Hot-path additions**: `addCouncilWatcher` / `addProviderAddress` exported for API handlers to call when new entities are created. For network-dashboard the analog is now: `fetchCouncilTopology` at boot, then chain-event-driven adoption via `contract_initialized` → `refreshTopology` → `backfillFromLedger`. ## Cross-PR dependency This PR must merge **before** Moonlight-Protocol/local-dev#112. That PR adds a strict-order, strict-value assertion harness whose `EXPECTED_EVENTS.network` begins with `council_formed` — which only fires deterministically after this refactor's `backfillFromLedger` is in place. Without this PR, the local-dev assertion would still fail on the first event index. ## Test plan - [x] `deno fmt --check` — clean (32 files) - [x] `deno lint` — clean (21 files) - [x] `deno task test` — **17 / 0** - [x] Local-stack end-to-end against a fresh-deploy flow script (testnet/main.ts + lifecycle/testnet-verify.ts): 5/5 network events captured in correct order per script, both with `council_formed` landing first via the back-fill path. - [ ] Reviewer pass on the `notMoonlight` TTL choice (`PENDING_TTL_MS = 120 s`) — that's the upper bound between a council's on-chain deploy and council-platform's `PUT /council/metadata` in the testnet scripts. - [ ] Reviewer pass on the back-fill scope — `backfillFromLedger` scans every watched contract from `minAdoptedLedger`, not only the newly-adopted ones, because cross-council SAC events (XLM `transfer` / `fee`) are mapped via the linkage table that `refreshTopology` just installed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts the convention defined in
local-dev/logging.md. First of four backend migrations (provider/council/pay are pending).src/utils/logger/index.ts— TS port of AquiGorka/go-logger. Five methods (info,event,debug,error,scope), four levels (Debug,Info,Event,Disabled), nested scopes, dual stdout-human / file-JSON output.src/config/logger.tsnow exportscreateLogger();main.tsis the only construction site and threads{ log, bus }through to every service and free function.NetworkEventBusbecomes a DI'd class — takes{ log }in its constructor, no longer exported as a singleton. The strayconsole.warnin itspublish()catch is nowlog.error.handle<X>(deps)factory pattern.handleNetworkWs(deps)returns the request handler;buildNetworkWsRouter(deps)andbuildApiRouter(deps)wire it up.refreshWasmRegistry,fetchCouncilTopology,refreshTopology,coldStartScan,evaluateUnknownContract,drainPendingAdoptions,startSorobanWatcher,startScheduler) all take{ log, bus }deps and scope internally vialog.scope("funcName").describeErr()helper — the new Logger handles error stringification for both human and JSON sinks viainstanceof Error/safeJsonValue.console.warnper the convention's mapping rules.No OTEL changes (this repo has no OTEL today).
Logging coverage
handleNetworkWs)main.ts)Verified locally (pre-push)
deno fmt --check: cleandeno lint: clean (22 files)deno task test: 17 passed, 0 faileddeno task check: cleangrep -rn 'LOG\.(...)' src/: 0 hitsgrep -rn 'console\.(...)' src/: 0 hitsgrep -rn 'describeErr' src/: 0 hitsTest plan
LOG_LEVEL=infovialocal-dev/up.shto confirm output format matches the specLast commit (
chore: bump version to 0.1.4) is the patch-bump-alone commit per the open-pr-checklist.