diff --git a/packages/interface/src/index.ts b/packages/interface/src/index.ts index 5b1463e4c2..975fa3d2c0 100644 --- a/packages/interface/src/index.ts +++ b/packages/interface/src/index.ts @@ -106,6 +106,23 @@ export interface IdentifyResult { connection: Connection } +/** + * Logger component for libp2p + */ +export interface Logger { + (formatter: any, ...args: any[]): void + error(formatter: any, ...args: any[]): void + trace(formatter: any, ...args: any[]): void + enabled: boolean +} + +/** + * Peer logger component for libp2p + */ +export interface ComponentLogger { + forComponent(name: string): Logger +} + /** * Once you have a libp2p instance, you can listen to several events it emits, * so that you can be notified of relevant network events. @@ -408,6 +425,8 @@ export interface Libp2p extends Startable, Ty */ metrics?: Metrics + logger: ComponentLogger + /** * Get a deduplicated list of peer advertising multiaddrs by concatenating * the listen addresses used by transports with any configured @@ -626,6 +645,13 @@ export interface AbortOptions { signal?: AbortSignal } +/** + * An object that contains a Logger as the `log` property. + */ +export interface LoggerOptions { + log: Logger +} + /** * Returns a new type with all fields marked optional. * diff --git a/packages/libp2p/src/components.ts b/packages/libp2p/src/components.ts index 2d65d956dc..6a254e12df 100644 --- a/packages/libp2p/src/components.ts +++ b/packages/libp2p/src/components.ts @@ -1,6 +1,7 @@ import { CodeError } from '@libp2p/interface/errors' import { isStartable, type Startable } from '@libp2p/interface/startable' -import type { Libp2pEvents } from '@libp2p/interface' +import { defaultLogger } from '@libp2p/logger' +import type { Libp2pEvents, ComponentLogger } from '@libp2p/interface' import type { ConnectionProtector } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { ContentRouting } from '@libp2p/interface/content-routing' @@ -18,6 +19,7 @@ import type { Datastore } from 'interface-datastore' export interface Components extends Record, Startable { peerId: PeerId + logger: ComponentLogger events: TypedEventTarget addressManager: AddressManager peerStore: PeerStore @@ -35,6 +37,7 @@ export interface Components extends Record, Startable { export interface ComponentsInit { peerId?: PeerId + logger?: ComponentLogger events?: TypedEventTarget addressManager?: AddressManager peerStore?: PeerStore @@ -60,6 +63,10 @@ class DefaultComponents implements Startable { for (const [key, value] of Object.entries(init)) { this.components[key] = value } + + if (this.components.logger == null) { + this.components.logger = defaultLogger() + } } isStarted (): boolean { diff --git a/packages/libp2p/src/connection-manager/auto-dial.ts b/packages/libp2p/src/connection-manager/auto-dial.ts index a110adfd46..515c23d90f 100644 --- a/packages/libp2p/src/connection-manager/auto-dial.ts +++ b/packages/libp2p/src/connection-manager/auto-dial.ts @@ -1,16 +1,13 @@ -import { logger } from '@libp2p/logger' import { PeerMap, PeerSet } from '@libp2p/peer-collections' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' import { PeerJobQueue } from '../utils/peer-job-queue.js' import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_DISCOVERED_PEERS_DEBOUNCE, AUTO_DIAL_INTERVAL, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PEER_RETRY_THRESHOLD, AUTO_DIAL_PRIORITY, LAST_DIAL_FAILURE_KEY, MIN_CONNECTIONS } from './constants.js' -import type { Libp2pEvents } from '@libp2p/interface' +import type { Libp2pEvents, Logger, ComponentLogger } from '@libp2p/interface' import type { TypedEventTarget } from '@libp2p/interface/events' import type { PeerStore } from '@libp2p/interface/peer-store' import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' -const log = logger('libp2p:connection-manager:auto-dial') - interface AutoDialInit { minConnections?: number maxQueueLength?: number @@ -25,6 +22,7 @@ interface AutoDialComponents { connectionManager: ConnectionManager peerStore: PeerStore events: TypedEventTarget + logger: ComponentLogger } const defaultOptions = { @@ -50,6 +48,7 @@ export class AutoDial implements Startable { private autoDialInterval?: ReturnType private started: boolean private running: boolean + readonly #log: Logger /** * Proactively tries to connect to known peers stored in the PeerStore. @@ -65,20 +64,21 @@ export class AutoDial implements Startable { this.autoDialMaxQueueLength = init.maxQueueLength ?? defaultOptions.maxQueueLength this.autoDialPeerRetryThresholdMs = init.autoDialPeerRetryThreshold ?? defaultOptions.autoDialPeerRetryThreshold this.autoDialDiscoveredPeersDebounce = init.autoDialDiscoveredPeersDebounce ?? defaultOptions.autoDialDiscoveredPeersDebounce + this.#log = components.logger.forComponent('libp2p:connection-manager:auto-dial') this.started = false this.running = false this.queue = new PeerJobQueue({ concurrency: init.autoDialConcurrency ?? defaultOptions.autoDialConcurrency }) this.queue.addListener('error', (err) => { - log.error('error during auto-dial', err) + this.#log.error('error during auto-dial', err) }) // check the min connection limit whenever a peer disconnects components.events.addEventListener('connection:close', () => { this.autoDial() .catch(err => { - log.error(err) + this.#log.error(err) }) }) @@ -93,7 +93,7 @@ export class AutoDial implements Startable { debounce = setTimeout(() => { this.autoDial() .catch(err => { - log.error(err) + this.#log.error(err) }) }, this.autoDialDiscoveredPeersDebounce) }) @@ -107,7 +107,7 @@ export class AutoDial implements Startable { this.autoDialInterval = setTimeout(() => { this.autoDial() .catch(err => { - log.error('error while autodialing', err) + this.#log.error('error while autodialing', err) }) }, this.autoDialIntervalMs) this.started = true @@ -116,7 +116,7 @@ export class AutoDial implements Startable { afterStart (): void { this.autoDial() .catch(err => { - log.error('error while autodialing', err) + this.#log.error('error while autodialing', err) }) } @@ -139,24 +139,24 @@ export class AutoDial implements Startable { // Already has enough connections if (numConnections >= this.minConnections) { if (this.minConnections > 0) { - log.trace('have enough connections %d/%d', numConnections, this.minConnections) + this.#log.trace('have enough connections %d/%d', numConnections, this.minConnections) } return } if (this.queue.size > this.autoDialMaxQueueLength) { - log('not enough connections %d/%d but auto dial queue is full', numConnections, this.minConnections) + this.#log('not enough connections %d/%d but auto dial queue is full', numConnections, this.minConnections) return } if (this.running) { - log('not enough connections %d/%d - but skipping autodial as it is already running', numConnections, this.minConnections) + this.#log('not enough connections %d/%d - but skipping autodial as it is already running', numConnections, this.minConnections) return } this.running = true - log('not enough connections %d/%d - will dial peers to increase the number of connections', numConnections, this.minConnections) + this.#log('not enough connections %d/%d - will dial peers to increase the number of connections', numConnections, this.minConnections) const dialQueue = new PeerSet( // @ts-expect-error boolean filter removes falsy peer IDs @@ -172,25 +172,25 @@ export class AutoDial implements Startable { (peer) => { // Remove peers without addresses if (peer.addresses.length === 0) { - log.trace('not autodialing %p because they have no addresses', peer.id) + this.#log.trace('not autodialing %p because they have no addresses', peer.id) return false } // remove peers we are already connected to if (connections.has(peer.id)) { - log.trace('not autodialing %p because they are already connected', peer.id) + this.#log.trace('not autodialing %p because they are already connected', peer.id) return false } // remove peers we are already dialling if (dialQueue.has(peer.id)) { - log.trace('not autodialing %p because they are already being dialed', peer.id) + this.#log.trace('not autodialing %p because they are already being dialed', peer.id) return false } // remove peers already in the autodial queue if (this.queue.hasJob(peer.id)) { - log.trace('not autodialing %p because they are already being autodialed', peer.id) + this.#log.trace('not autodialing %p because they are already being autodialed', peer.id) return false } @@ -249,7 +249,7 @@ export class AutoDial implements Startable { return Date.now() - lastDialFailureTimestamp > this.autoDialPeerRetryThresholdMs }) - log('selected %d/%d peers to dial', peersThatHaveNotFailed.length, peers.length) + this.#log('selected %d/%d peers to dial', peersThatHaveNotFailed.length, peers.length) for (const peer of peersThatHaveNotFailed) { this.queue.add(async () => { @@ -257,19 +257,19 @@ export class AutoDial implements Startable { // Check to see if we still need to auto dial if (numConnections >= this.minConnections) { - log('got enough connections now %d/%d', numConnections, this.minConnections) + this.#log('got enough connections now %d/%d', numConnections, this.minConnections) this.queue.clear() return } - log('connecting to a peerStore stored peer %p', peer.id) + this.#log('connecting to a peerStore stored peer %p', peer.id) await this.connectionManager.openConnection(peer.id, { priority: this.autoDialPriority }) }, { peerId: peer.id }).catch(err => { - log.error('could not connect to peerStore stored peer', err) + this.#log.error('could not connect to peerStore stored peer', err) }) } @@ -279,7 +279,7 @@ export class AutoDial implements Startable { this.autoDialInterval = setTimeout(() => { this.autoDial() .catch(err => { - log.error('error while autodialing', err) + this.#log.error('error while autodialing', err) }) }, this.autoDialIntervalMs) } diff --git a/packages/libp2p/src/connection-manager/connection-pruner.ts b/packages/libp2p/src/connection-manager/connection-pruner.ts index adbed0a56c..8771a25b42 100644 --- a/packages/libp2p/src/connection-manager/connection-pruner.ts +++ b/packages/libp2p/src/connection-manager/connection-pruner.ts @@ -1,14 +1,11 @@ -import { logger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' import { MAX_CONNECTIONS } from './constants.js' -import type { Libp2pEvents } from '@libp2p/interface' +import type { Libp2pEvents, Logger, ComponentLogger } from '@libp2p/interface' import type { TypedEventTarget } from '@libp2p/interface/events' import type { PeerStore } from '@libp2p/interface/peer-store' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { Multiaddr } from '@multiformats/multiaddr' -const log = logger('libp2p:connection-manager:connection-pruner') - interface ConnectionPrunerInit { maxConnections?: number allow?: Multiaddr[] @@ -18,6 +15,7 @@ interface ConnectionPrunerComponents { connectionManager: ConnectionManager peerStore: PeerStore events: TypedEventTarget + logger: ComponentLogger } const defaultOptions = { @@ -34,6 +32,7 @@ export class ConnectionPruner { private readonly peerStore: PeerStore private readonly allow: Multiaddr[] private readonly events: TypedEventTarget + readonly #log: Logger constructor (components: ConnectionPrunerComponents, init: ConnectionPrunerInit = {}) { this.maxConnections = init.maxConnections ?? defaultOptions.maxConnections @@ -41,12 +40,13 @@ export class ConnectionPruner { this.connectionManager = components.connectionManager this.peerStore = components.peerStore this.events = components.events + this.#log = components.logger.forComponent('libp2p:connection-manager:connection-pruner') // check the max connection limit whenever a peer connects components.events.addEventListener('connection:open', () => { this.maybePruneConnections() .catch(err => { - log.error(err) + this.#log.error(err) }) }) } @@ -60,12 +60,12 @@ export class ConnectionPruner { const numConnections = connections.length const toPrune = Math.max(numConnections - this.maxConnections, 0) - log('checking max connections limit %d/%d', numConnections, this.maxConnections) + this.#log('checking max connections limit %d/%d', numConnections, this.maxConnections) if (numConnections <= this.maxConnections) { return } - log('max connections limit exceeded %d/%d, pruning %d connection(s)', numConnections, this.maxConnections, toPrune) + this.#log('max connections limit exceeded %d/%d, pruning %d connection(s)', numConnections, this.maxConnections, toPrune) const peerValues = new PeerMap() // work out peer values @@ -87,7 +87,7 @@ export class ConnectionPruner { }, 0)) } catch (err: any) { if (err.code !== 'ERR_NOT_FOUND') { - log.error('error loading peer tags', err) + this.#log.error('error loading peer tags', err) } } } @@ -124,7 +124,7 @@ export class ConnectionPruner { const toClose = [] for (const connection of sortedConnections) { - log('too many connections open - closing a connection to %p', connection.remotePeer) + this.#log('too many connections open - closing a connection to %p', connection.remotePeer) // check allow list const connectionInAllowList = this.allow.some((ma) => { return connection.remoteAddr.toString().startsWith(ma.toString()) @@ -146,7 +146,7 @@ export class ConnectionPruner { try { await connection.close() } catch (err) { - log.error(err) + this.#log.error(err) } }) ) diff --git a/packages/libp2p/src/connection-manager/dial-queue.ts b/packages/libp2p/src/connection-manager/dial-queue.ts index c366417757..d45e989590 100644 --- a/packages/libp2p/src/connection-manager/dial-queue.ts +++ b/packages/libp2p/src/connection-manager/dial-queue.ts @@ -1,6 +1,5 @@ import { AbortError, CodeError } from '@libp2p/interface/errors' import { setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' import { defaultAddressSort } from '@libp2p/utils/address-sort' import { type Multiaddr, type Resolver, resolvers } from '@multiformats/multiaddr' @@ -19,7 +18,7 @@ import { LAST_DIAL_FAILURE_KEY } from './constants.js' import { combineSignals, resolveMultiaddrs } from './utils.js' -import type { AddressSorter, AbortOptions, PendingDial } from '@libp2p/interface' +import type { AddressSorter, AbortOptions, PendingDial, ComponentLogger, Logger } from '@libp2p/interface' import type { Connection } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { Metric, Metrics } from '@libp2p/interface/metrics' @@ -27,8 +26,6 @@ import type { PeerId } from '@libp2p/interface/peer-id' import type { Address, PeerStore } from '@libp2p/interface/peer-store' import type { TransportManager } from '@libp2p/interface-internal/transport-manager' -const log = logger('libp2p:connection-manager:dial-queue') - export interface PendingDialTarget { resolve(value: any): void reject(err: Error): void @@ -70,6 +67,7 @@ interface DialQueueComponents { peerStore: PeerStore transportManager: TransportManager connectionGater: ConnectionGater + logger: ComponentLogger } export class DialQueue { @@ -87,6 +85,7 @@ export class DialQueue { private readonly pendingDialCount?: Metric private readonly shutDownController: AbortController private readonly connections: PeerMap + readonly #log: Logger constructor (components: DialQueueComponents, init: DialerInit = {}) { this.addressSorter = init.addressSorter ?? defaultOptions.addressSorter @@ -94,6 +93,7 @@ export class DialQueue { this.maxParallelDialsPerPeer = init.maxParallelDialsPerPeer ?? defaultOptions.maxParallelDialsPerPeer this.dialTimeout = init.dialTimeout ?? defaultOptions.dialTimeout this.connections = init.connections ?? new PeerMap() + this.#log = components.logger.forComponent('libp2p:connection-manager:dial-queue') this.peerId = components.peerId this.peerStore = components.peerStore @@ -133,7 +133,7 @@ export class DialQueue { }) // a started job errored this.queue.on('error', (err) => { - log.error('error in dial queue', err) + this.#log.error('error in dial queue', err) this.pendingDialCount?.update(this.queue.size) this.inProgressDialCount?.update(this.queue.pending) }) @@ -205,7 +205,7 @@ export class DialQueue { }) if (existingConnection != null) { - log('already connected to %a', existingConnection.remoteAddr) + this.#log('already connected to %a', existingConnection.remoteAddr) return existingConnection } @@ -226,12 +226,12 @@ export class DialQueue { }) if (existingDial != null) { - log('joining existing dial target for %p', peerId) + this.#log('joining existing dial target for %p', peerId) signal.clear() return existingDial.promise } - log('creating dial target for', addrsToDial.map(({ multiaddr }) => multiaddr.toString())) + this.#log('creating dial target for', addrsToDial.map(({ multiaddr }) => multiaddr.toString())) // @ts-expect-error .promise property is set below const pendingDial: PendingDialInternal = { id: randomId(), @@ -252,7 +252,7 @@ export class DialQueue { signal.clear() }) .catch(async err => { - log.error('dial failed to %s', pendingDial.multiaddrs.map(ma => ma.toString()).join(', '), err) + this.#log.error('dial failed to %s', pendingDial.multiaddrs.map(ma => ma.toString()).join(', '), err) if (peerId != null) { // record the last failed dial @@ -263,7 +263,7 @@ export class DialQueue { } }) } catch (err: any) { - log.error('could not update last dial failure key for %p', peerId, err) + this.#log.error('could not update last dial failure key for %p', peerId, err) } } @@ -294,12 +294,12 @@ export class DialQueue { }) if (existingConnection != null) { - log('already connected to %a', existingConnection.remoteAddr) + this.#log('already connected to %a', existingConnection.remoteAddr) await connection.close() return existingConnection } - log('connection opened to %a', connection.remoteAddr) + this.#log('connection opened to %a', connection.remoteAddr) return connection } @@ -334,11 +334,11 @@ export class DialQueue { // if just a peer id was passed, load available multiaddrs for this peer from the address book if (addrs.length === 0) { - log('loading multiaddrs for %p', peerId) + this.#log('loading multiaddrs for %p', peerId) try { const peer = await this.peerStore.get(peerId) addrs.push(...peer.addresses) - log('loaded multiaddrs for %p', peerId, addrs.map(({ multiaddr }) => multiaddr.toString())) + this.#log('loaded multiaddrs for %p', peerId, addrs.map(({ multiaddr }) => multiaddr.toString())) } catch (err: any) { if (err.code !== codes.ERR_NOT_FOUND) { throw err @@ -350,7 +350,10 @@ export class DialQueue { // resolve addresses - this can result in a one-to-many translation when dnsaddrs are resolved let resolvedAddresses = (await Promise.all( addrs.map(async addr => { - const result = await resolveMultiaddrs(addr.multiaddr, options) + const result = await resolveMultiaddrs(addr.multiaddr, { + ...options, + log: this.#log + }) if (result.length === 1 && result[0].equals(addr.multiaddr)) { return addr @@ -422,8 +425,8 @@ export class DialQueue { const dedupedMultiaddrs = [...dedupedAddrs.values()] if (dedupedMultiaddrs.length === 0 || dedupedMultiaddrs.length > this.maxPeerAddrsToDial) { - log('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString())) - log('addresses for %p after filtering', peerId ?? 'unknown peer', dedupedMultiaddrs.map(({ multiaddr }) => multiaddr.toString())) + this.#log('addresses for %p before filtering', peerId ?? 'unknown peer', resolvedAddresses.map(({ multiaddr }) => multiaddr.toString())) + this.#log('addresses for %p after filtering', peerId ?? 'unknown peer', dedupedMultiaddrs.map(({ multiaddr }) => multiaddr.toString())) } // make sure we actually have some addresses to dial @@ -467,7 +470,7 @@ export class DialQueue { concurrency: this.maxParallelDialsPerPeer }) peerDialQueue.on('error', (err) => { - log.error('error dialling', err) + this.#log.error('error dialling', err) }) const conn = await Promise.any(pendingDial.multiaddrs.map(async (addr, i) => { @@ -480,13 +483,13 @@ export class DialQueue { // let any signal abort the dial const signal = combineSignals(controller.signal, options.signal) signal.addEventListener('abort', () => { - log('dial to %a aborted', addr) + this.#log('dial to %a aborted', addr) }) const deferred = pDefer() await peerDialQueue.add(async () => { if (signal.aborted) { - log('dial to %a was aborted before reaching the head of the peer dial queue', addr) + this.#log('dial to %a was aborted before reaching the head of the peer dial queue', addr) deferred.reject(new AbortError()) return } @@ -495,7 +498,7 @@ export class DialQueue { await this.queue.add(async () => { try { if (signal.aborted) { - log('dial to %a was aborted before reaching the head of the dial queue', addr) + this.#log('dial to %a was aborted before reaching the head of the dial queue', addr) deferred.reject(new AbortError()) return } @@ -510,10 +513,10 @@ export class DialQueue { if (controller.signal.aborted) { // another dial succeeded faster than this one - log('multiple dials succeeded, closing superfluous connection') + this.#log('multiple dials succeeded, closing superfluous connection') conn.close().catch(err => { - log.error('error closing superfluous connection', err) + this.#log.error('error closing superfluous connection', err) }) deferred.reject(new AbortError()) @@ -530,13 +533,13 @@ export class DialQueue { } }) - log('dial to %a succeeded', addr) + this.#log('dial to %a succeeded', addr) // resolve the connection promise deferred.resolve(conn) } catch (err: any) { // something only went wrong if our signal was not aborted - log.error('error during dial of %a', addr, err) + this.#log.error('error during dial of %a', addr, err) deferred.reject(err) } }, { diff --git a/packages/libp2p/src/connection-manager/index.ts b/packages/libp2p/src/connection-manager/index.ts index 0afceac860..137f331c4a 100644 --- a/packages/libp2p/src/connection-manager/index.ts +++ b/packages/libp2p/src/connection-manager/index.ts @@ -1,6 +1,5 @@ import { CodeError } from '@libp2p/interface/errors' import { KEEP_ALIVE } from '@libp2p/interface/peer-store/tags' -import { logger } from '@libp2p/logger' import { PeerMap } from '@libp2p/peer-collections' import { defaultAddressSort } from '@libp2p/utils/address-sort' import { type Multiaddr, type Resolver, multiaddr } from '@multiformats/multiaddr' @@ -12,7 +11,7 @@ import { AutoDial } from './auto-dial.js' import { ConnectionPruner } from './connection-pruner.js' import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PRIORITY, DIAL_TIMEOUT, INBOUND_CONNECTION_THRESHOLD, MAX_CONNECTIONS, MAX_INCOMING_PENDING_CONNECTIONS, MAX_PARALLEL_DIALS, MAX_PARALLEL_DIALS_PER_PEER, MAX_PEER_ADDRS_TO_DIAL, MIN_CONNECTIONS } from './constants.js' import { DialQueue } from './dial-queue.js' -import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions } from '@libp2p/interface' +import type { PendingDial, AddressSorter, Libp2pEvents, AbortOptions, ComponentLogger, Logger } from '@libp2p/interface' import type { Connection, MultiaddrConnection } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { TypedEventTarget } from '@libp2p/interface/events' @@ -23,8 +22,6 @@ import type { Startable } from '@libp2p/interface/startable' import type { ConnectionManager, OpenConnectionOptions } from '@libp2p/interface-internal/connection-manager' import type { TransportManager } from '@libp2p/interface-internal/transport-manager' -const log = logger('libp2p:connection-manager') - const DEFAULT_DIAL_PRIORITY = 50 export interface ConnectionManagerInit { @@ -166,6 +163,7 @@ export interface DefaultConnectionManagerComponents { transportManager: TransportManager connectionGater: ConnectionGater events: TypedEventTarget + logger: ComponentLogger } /** @@ -188,6 +186,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { private readonly peerStore: PeerStore private readonly metrics?: Metrics private readonly events: TypedEventTarget + readonly #log: Logger constructor (components: DefaultConnectionManagerComponents, init: ConnectionManagerInit = {}) { this.maxConnections = init.maxConnections ?? defaultOptions.maxConnections @@ -206,6 +205,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { this.peerStore = components.peerStore this.metrics = components.metrics this.events = components.events + this.#log = components.logger.forComponent('libp2p:connection-manager') this.onConnect = this.onConnect.bind(this) this.onDisconnect = this.onDisconnect.bind(this) @@ -229,7 +229,8 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { this.autoDial = new AutoDial({ connectionManager: this, peerStore: components.peerStore, - events: components.events + events: components.events, + logger: components.logger }, { minConnections, autoDialConcurrency: init.autoDialConcurrency ?? defaultOptions.autoDialConcurrency, @@ -241,7 +242,8 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { this.connectionPruner = new ConnectionPruner({ connectionManager: this, peerStore: components.peerStore, - events: components.events + events: components.events, + logger: components.logger }, { maxConnections: this.maxConnections, allow: this.allow @@ -252,7 +254,8 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { metrics: components.metrics, peerStore: components.peerStore, transportManager: components.transportManager, - connectionGater: components.connectionGater + connectionGater: components.connectionGater, + logger: components.logger }, { addressSorter: init.addressSorter ?? defaultAddressSort, maxParallelDials: init.maxParallelDials ?? MAX_PARALLEL_DIALS, @@ -356,7 +359,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { this.autoDial.start() this.started = true - log('started') + this.#log('started') } async afterStart (): Promise { @@ -373,13 +376,13 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { keepAlivePeers.map(async peer => { await this.openConnection(peer.id) .catch(err => { - log.error(err) + this.#log.error(err) }) }) ) }) .catch(err => { - log.error(err) + this.#log.error(err) }) this.autoDial.afterStart() @@ -400,22 +403,22 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { try { await connection.close() } catch (err) { - log.error(err) + this.#log.error(err) } })()) } } - log('closing %d connections', tasks.length) + this.#log('closing %d connections', tasks.length) await Promise.all(tasks) this.connections.clear() - log('stopped') + this.#log('stopped') } onConnect (evt: CustomEvent): void { void this._onConnect(evt).catch(err => { - log.error(err) + this.#log.error(err) }) } @@ -505,12 +508,12 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { const { peerId } = getPeerAddress(peerIdOrMultiaddr) if (peerId != null && options.force !== true) { - log('dial %p', peerId) + this.#log('dial %p', peerId) const existingConnection = this.getConnections(peerId) .find(conn => !conn.transient) if (existingConnection != null) { - log('had an existing non-transient connection to %p', peerId) + this.#log('had an existing non-transient connection to %p', peerId) return existingConnection } @@ -566,7 +569,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { }) if (denyConnection) { - log('connection from %a refused - connection remote address was in deny list', maConn.remoteAddr) + this.#log('connection from %a refused - connection remote address was in deny list', maConn.remoteAddr) return false } @@ -583,7 +586,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { // check pending connections if (this.incomingPendingConnections === this.maxIncomingPendingConnections) { - log('connection from %a refused - incomingPendingConnections exceeded by host', maConn.remoteAddr) + this.#log('connection from %a refused - incomingPendingConnections exceeded by host', maConn.remoteAddr) return false } @@ -593,7 +596,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { try { await this.inboundConnectionRateLimiter.consume(host, 1) } catch { - log('connection from %a refused - inboundConnectionThreshold exceeded by host %s', maConn.remoteAddr, host) + this.#log('connection from %a refused - inboundConnectionThreshold exceeded by host %s', maConn.remoteAddr, host) return false } } @@ -604,7 +607,7 @@ export class DefaultConnectionManager implements ConnectionManager, Startable { return true } - log('connection from %a refused - maxConnections exceeded', maConn.remoteAddr) + this.#log('connection from %a refused - maxConnections exceeded', maConn.remoteAddr) return false } diff --git a/packages/libp2p/src/connection-manager/utils.ts b/packages/libp2p/src/connection-manager/utils.ts index 0bec4506ab..d32108e26f 100644 --- a/packages/libp2p/src/connection-manager/utils.ts +++ b/packages/libp2p/src/connection-manager/utils.ts @@ -1,14 +1,12 @@ import { setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { type AbortOptions, multiaddr, type Multiaddr } from '@multiformats/multiaddr' import { type ClearableSignal, anySignal } from 'any-signal' - -const log = logger('libp2p:connection-manager:utils') +import type { LoggerOptions } from '@libp2p/interface' /** * Resolve multiaddr recursively */ -export async function resolveMultiaddrs (ma: Multiaddr, options: AbortOptions): Promise { +export async function resolveMultiaddrs (ma: Multiaddr, options: AbortOptions & LoggerOptions): Promise { // TODO: recursive logic should live in multiaddr once dns4/dns6 support is in place // Now only supporting resolve for dnsaddr const resolvableProto = ma.protoNames().includes('dnsaddr') @@ -31,7 +29,7 @@ export async function resolveMultiaddrs (ma: Multiaddr, options: AbortOptions): return array }, ([])) - log('resolved %s to', ma, output.map(ma => ma.toString())) + options.log('resolved %s to', ma, output.map(ma => ma.toString())) return output } @@ -39,13 +37,13 @@ export async function resolveMultiaddrs (ma: Multiaddr, options: AbortOptions): /** * Resolve a given multiaddr. If this fails, an empty array will be returned */ -async function resolveRecord (ma: Multiaddr, options: AbortOptions): Promise { +async function resolveRecord (ma: Multiaddr, options: AbortOptions & LoggerOptions): Promise { try { ma = multiaddr(ma.toString()) // Use current multiaddr module const multiaddrs = await ma.resolve(options) return multiaddrs } catch (err) { - log.error(`multiaddr ${ma.toString()} could not be resolved`, err) + options.log.error(`multiaddr ${ma.toString()} could not be resolved`, err) return [] } } diff --git a/packages/libp2p/src/connection/index.ts b/packages/libp2p/src/connection/index.ts index 3aca11759b..fbc62c72f1 100644 --- a/packages/libp2p/src/connection/index.ts +++ b/packages/libp2p/src/connection/index.ts @@ -1,14 +1,11 @@ import { symbol } from '@libp2p/interface/connection' import { CodeError } from '@libp2p/interface/errors' import { setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' -import type { AbortOptions } from '@libp2p/interface' +import type { AbortOptions, Logger, ComponentLogger } from '@libp2p/interface' import type { Direction, Connection, Stream, ConnectionTimeline, ConnectionStatus, NewStreamOptions } from '@libp2p/interface/connection' import type { PeerId } from '@libp2p/interface/peer-id' import type { Multiaddr } from '@multiformats/multiaddr' -const log = logger('libp2p:connection') - const CLOSE_TIMEOUT = 500 interface ConnectionInit { @@ -24,6 +21,7 @@ interface ConnectionInit { multiplexer?: string encryption?: string transient?: boolean + logger: ComponentLogger } /** @@ -76,6 +74,8 @@ export class ConnectionImpl implements Connection { */ private readonly _getStreams: () => Stream[] + readonly #log: Logger + /** * An implementation of the js-libp2p connection. * Any libp2p transport should use an upgrader to return this connection. @@ -92,6 +92,7 @@ export class ConnectionImpl implements Connection { this.multiplexer = init.multiplexer this.encryption = init.encryption this.transient = init.transient ?? false + this.#log = init.logger.forComponent('libp2p:connection') if (this.remoteAddr.getPeerId() == null) { this.remoteAddr = this.remoteAddr.encapsulate(`/p2p/${this.remotePeer}`) @@ -150,7 +151,7 @@ export class ConnectionImpl implements Connection { return } - log('closing connection to %a', this.remoteAddr) + this.#log('closing connection to %a', this.remoteAddr) this.status = 'closing' @@ -159,35 +160,35 @@ export class ConnectionImpl implements Connection { setMaxListeners(Infinity, options.signal) try { - log.trace('closing all streams') + this.#log.trace('closing all streams') // close all streams gracefully - this can throw if we're not multiplexed await Promise.all( this.streams.map(async s => s.close(options)) ) - log.trace('closing underlying transport') + this.#log.trace('closing underlying transport') // close raw connection await this._close(options) - log.trace('updating timeline with close time') + this.#log.trace('updating timeline with close time') this.status = 'closed' this.timeline.close = Date.now() } catch (err: any) { - log.error('error encountered during graceful close of connection to %a', this.remoteAddr, err) + this.#log.error('error encountered during graceful close of connection to %a', this.remoteAddr, err) this.abort(err) } } abort (err: Error): void { - log.error('aborting connection to %a due to error', this.remoteAddr, err) + this.#log.error('aborting connection to %a due to error', this.remoteAddr, err) this.status = 'closing' this.streams.forEach(s => { s.abort(err) }) - log.error('all streams aborted', this.streams.length) + this.#log.error('all streams aborted', this.streams.length) // Abort raw connection this._abort(err) diff --git a/packages/libp2p/src/identify/identify.ts b/packages/libp2p/src/identify/identify.ts index 57c2be4281..8fef5c9949 100644 --- a/packages/libp2p/src/identify/identify.ts +++ b/packages/libp2p/src/identify/identify.ts @@ -1,6 +1,5 @@ import { CodeError } from '@libp2p/interface/errors' import { setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import { peerIdFromKeys } from '@libp2p/peer-id' import { RecordEnvelope, PeerRecord } from '@libp2p/peer-record' import { type Multiaddr, multiaddr, protocols } from '@multiformats/multiaddr' @@ -20,7 +19,7 @@ import { } from './consts.js' import { Identify } from './pb/message.js' import type { IdentifyService, IdentifyServiceComponents, IdentifyServiceInit } from './index.js' -import type { Libp2pEvents, IdentifyResult, SignedPeerRecord, AbortOptions } from '@libp2p/interface' +import type { Libp2pEvents, IdentifyResult, SignedPeerRecord, AbortOptions, Logger } from '@libp2p/interface' import type { Connection, Stream } from '@libp2p/interface/connection' import type { TypedEventTarget } from '@libp2p/interface/events' import type { PeerId } from '@libp2p/interface/peer-id' @@ -30,8 +29,6 @@ import type { AddressManager } from '@libp2p/interface-internal/address-manager' import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager' import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar' -const log = logger('libp2p:identify') - // https://github.com/libp2p/go-libp2p/blob/8d2e54e1637041d5cf4fac1e531287560bd1f4ac/p2p/protocol/identify/id.go#L52 const MAX_IDENTIFY_MESSAGE_SIZE = 1024 * 8 @@ -73,6 +70,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { private readonly maxObservedAddresses: number private readonly events: TypedEventTarget private readonly runOnTransientConnection: boolean + readonly #log: Logger constructor (components: IdentifyServiceComponents, init: IdentifyServiceInit) { this.started = false @@ -82,6 +80,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { this.addressManager = components.addressManager this.connectionManager = components.connectionManager this.events = components.events + this.#log = components.logger.forComponent('libp2p:identify') this.identifyProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PROTOCOL_VERSION}` this.identifyPushProtocolStr = `/${init.protocolPrefix ?? defaultValues.protocolPrefix}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_NAME}/${MULTICODEC_IDENTIFY_PUSH_PROTOCOL_VERSION}` @@ -104,13 +103,13 @@ export class DefaultIdentifyService implements Startable, IdentifyService { // When a new connection happens, trigger identify components.events.addEventListener('connection:open', (evt) => { const connection = evt.detail - this.identify(connection).catch(err => { log.error('error during identify trigged by connection:open', err) }) + this.identify(connection).catch(err => { this.#log.error('error during identify trigged by connection:open', err) }) }) } // When self peer record changes, trigger identify-push components.events.addEventListener('self:peer:update', (evt) => { - void this.push().catch(err => { log.error(err) }) + void this.push().catch(err => { this.#log.error(err) }) }) // Append user agent version to default AGENT_VERSION depending on the environment @@ -141,7 +140,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { await this.registrar.handle(this.identifyProtocolStr, (data) => { void this._handleIdentify(data).catch(err => { - log.error(err) + this.#log.error(err) }) }, { maxInboundStreams: this.maxInboundStreams, @@ -150,7 +149,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { }) await this.registrar.handle(this.identifyPushProtocolStr, (data) => { void this._handlePush(data).catch(err => { - log.error(err) + this.#log.error(err) }) }, { maxInboundStreams: this.maxPushIncomingStreams, @@ -215,7 +214,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { }) } catch (err: any) { // Just log errors - log.error('could not push identify update to peer', err) + this.#log.error('could not push identify update to peer', err) stream?.abort(err) } }) @@ -276,7 +275,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { return message } catch (err: any) { - log.error('error while reading identify message', err) + this.#log.error('error while reading identify message', err) stream?.abort(err) throw err } @@ -307,12 +306,12 @@ export class DefaultIdentifyService implements Startable, IdentifyService { // Get the observedAddr if there is one const cleanObservedAddr = getCleanMultiaddr(observedAddr) - log('identify completed for peer %p and protocols %o', id, protocols) - log('our observed address is %a', cleanObservedAddr) + this.#log('identify completed for peer %p and protocols %o', id, protocols) + this.#log('our observed address is %a', cleanObservedAddr) if (cleanObservedAddr != null && this.addressManager.getObservedAddrs().length < (this.maxObservedAddresses ?? Infinity)) { - log('storing our observed address %a', cleanObservedAddr) + this.#log('storing our observed address %a', cleanObservedAddr) this.addressManager.addObservedAddr(cleanObservedAddr) } @@ -370,7 +369,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { signal }) } catch (err: any) { - log.error('could not respond to identify request', err) + this.#log.error('could not respond to identify request', err) stream.abort(err) } } @@ -399,16 +398,16 @@ export class DefaultIdentifyService implements Startable, IdentifyService { await this.#consumeIdentifyMessage(connection, message) } catch (err: any) { - log.error('received invalid message', err) + this.#log.error('received invalid message', err) stream.abort(err) return } - log('handled push from %p', connection.remotePeer) + this.#log('handled push from %p', connection.remotePeer) } async #consumeIdentifyMessage (connection: Connection, message: Identify): Promise { - log('received identify from %p', connection.remotePeer) + this.#log('received identify from %p', connection.remotePeer) if (message == null) { throw new CodeError('message was null or undefined', 'ERR_INVALID_MESSAGE') @@ -441,7 +440,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { // if the peer record has been sent, prefer the addresses in the record as they are signed by the remote peer if (message.signedPeerRecord != null) { - log('received signedPeerRecord in push from %p', connection.remotePeer) + this.#log('received signedPeerRecord in push from %p', connection.remotePeer) let peerRecordEnvelope = message.signedPeerRecord const envelope = await RecordEnvelope.openAndCertify(peerRecordEnvelope, PeerRecord.DOMAIN) @@ -478,7 +477,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { // ensure seq is greater than, or equal to, the last received if (storedRecord.seqNumber >= peerRecord.seqNumber) { - log('sequence number was lower or equal to existing sequence number - stored: %d received: %d', storedRecord.seqNumber, peerRecord.seqNumber) + this.#log('sequence number was lower or equal to existing sequence number - stored: %d received: %d', storedRecord.seqNumber, peerRecord.seqNumber) peerRecord = storedRecord peerRecordEnvelope = existingPeer.peerRecordEnvelope } @@ -499,10 +498,10 @@ export class DefaultIdentifyService implements Startable, IdentifyService { addresses: peerRecord.multiaddrs } } else { - log('%p did not send a signed peer record', connection.remotePeer) + this.#log('%p did not send a signed peer record', connection.remotePeer) } - log('patching %p with', connection.remotePeer, peer) + this.#log('patching %p with', connection.remotePeer, peer) await this.peerStore.patch(connection.remotePeer, peer) if (message.agentVersion != null || message.protocolVersion != null) { @@ -516,7 +515,7 @@ export class DefaultIdentifyService implements Startable, IdentifyService { metadata.ProtocolVersion = uint8ArrayFromString(message.protocolVersion) } - log('merging %p metadata', connection.remotePeer, metadata) + this.#log('merging %p metadata', connection.remotePeer, metadata) await this.peerStore.merge(connection.remotePeer, { metadata }) diff --git a/packages/libp2p/src/identify/index.ts b/packages/libp2p/src/identify/index.ts index 61dc2e3523..fc0fbf6320 100644 --- a/packages/libp2p/src/identify/index.ts +++ b/packages/libp2p/src/identify/index.ts @@ -4,7 +4,7 @@ import { } from './consts.js' import { DefaultIdentifyService } from './identify.js' import { Identify } from './pb/message.js' -import type { AbortOptions, IdentifyResult, Libp2pEvents } from '@libp2p/interface' +import type { AbortOptions, IdentifyResult, Libp2pEvents, ComponentLogger } from '@libp2p/interface' import type { TypedEventTarget } from '@libp2p/interface/events' import type { PeerId } from '@libp2p/interface/peer-id' import type { PeerStore } from '@libp2p/interface/peer-store' @@ -59,6 +59,7 @@ export interface IdentifyServiceComponents { registrar: Registrar addressManager: AddressManager events: TypedEventTarget + logger: ComponentLogger } /** diff --git a/packages/libp2p/src/index.ts b/packages/libp2p/src/index.ts index 8fa5882135..d9a0e61095 100644 --- a/packages/libp2p/src/index.ts +++ b/packages/libp2p/src/index.ts @@ -19,7 +19,7 @@ import type { AddressManagerInit } from './address-manager/index.js' import type { Components } from './components.js' import type { ConnectionManagerInit } from './connection-manager/index.js' import type { TransportManagerInit } from './transport-manager.js' -import type { Libp2p, ServiceMap, RecursivePartial } from '@libp2p/interface' +import type { Libp2p, ServiceMap, RecursivePartial, ComponentLogger } from '@libp2p/interface' import type { ConnectionProtector } from '@libp2p/interface/connection' import type { ConnectionEncrypter } from '@libp2p/interface/connection-encrypter' import type { ConnectionGater } from '@libp2p/interface/connection-gater' @@ -108,6 +108,27 @@ export interface Libp2pInit * Arbitrary libp2p modules */ services: ServiceFactoryMap + + /** + * An optional logging implementation that can be used to write runtime logs. + * + * Set the `DEBUG` env var or the `debug` key on LocalStorage to see logs. + * + * @example + * + * Node.js: + * + * ```console + * $ DEBUG="*libp2p:*" node myscript.js + * ``` + * + * Browsers: + * + * ```javascript + * localStorage.setItem('debug', '*libp2p:*') + * ``` + */ + logger?: ComponentLogger } export type { Libp2p } diff --git a/packages/libp2p/src/libp2p.ts b/packages/libp2p/src/libp2p.ts index 5708aa59e3..e39511b1c9 100644 --- a/packages/libp2p/src/libp2p.ts +++ b/packages/libp2p/src/libp2p.ts @@ -5,7 +5,7 @@ import { TypedEventEmitter, CustomEvent, setMaxListeners } from '@libp2p/interfa import { peerDiscovery } from '@libp2p/interface/peer-discovery' import { type PeerRouting, peerRouting } from '@libp2p/interface/peer-routing' import { DefaultKeyChain } from '@libp2p/keychain' -import { logger } from '@libp2p/logger' +import { defaultLogger } from '@libp2p/logger' import { PeerSet } from '@libp2p/peer-collections' import { peerIdFromString } from '@libp2p/peer-id' import { createEd25519PeerId } from '@libp2p/peer-id-factory' @@ -28,7 +28,7 @@ import { DefaultTransportManager } from './transport-manager.js' import { DefaultUpgrader } from './upgrader.js' import type { Components } from './components.js' import type { Libp2p, Libp2pInit, Libp2pOptions } from './index.js' -import type { Libp2pEvents, PendingDial, ServiceMap, AbortOptions } from '@libp2p/interface' +import type { Libp2pEvents, PendingDial, ServiceMap, AbortOptions, ComponentLogger, Logger } from '@libp2p/interface' import type { Connection, NewStreamOptions, Stream } from '@libp2p/interface/connection' import type { KeyChain } from '@libp2p/interface/keychain' import type { Metrics } from '@libp2p/interface/metrics' @@ -39,8 +39,6 @@ import type { Topology } from '@libp2p/interface/topology' import type { StreamHandler, StreamHandlerOptions } from '@libp2p/interface-internal/registrar' import type { Datastore } from 'interface-datastore' -const log = logger('libp2p') - export class Libp2pNode> extends TypedEventEmitter implements Libp2p { public peerId: PeerId public peerStore: PeerStore @@ -49,9 +47,11 @@ export class Libp2pNode> extends public keychain: KeyChain public metrics?: Metrics public services: T + public logger: ComponentLogger public components: Components #started: boolean + readonly #log: Logger constructor (init: Libp2pInit) { super() @@ -74,10 +74,13 @@ export class Libp2pNode> extends this.#started = false this.peerId = init.peerId + this.logger = init.logger ?? defaultLogger() + this.#log = this.logger.forComponent('libp2p') // @ts-expect-error {} may not be of type T this.services = {} const components = this.components = defaultComponents({ peerId: init.peerId, + logger: this.logger, events, datastore: init.datastore ?? new MemoryDatastore(), connectionGater: connectionGater(init.connectionGater) @@ -170,7 +173,7 @@ export class Libp2pNode> extends const service: any = createService(this.components) if (service == null) { - log.error('service factory %s returned null or undefined instance', name) + this.#log.error('service factory %s returned null or undefined instance', name) continue } @@ -178,17 +181,17 @@ export class Libp2pNode> extends this.configureComponent(name, service) if (service[contentRouting] != null) { - log('registering service %s for content routing', name) + this.#log('registering service %s for content routing', name) contentRouters.push(service[contentRouting]) } if (service[peerRouting] != null) { - log('registering service %s for peer routing', name) + this.#log('registering service %s for peer routing', name) peerRouters.push(service[peerRouting]) } if (service[peerDiscovery] != null) { - log('registering service %s for peer discovery', name) + this.#log('registering service %s for peer discovery', name) service[peerDiscovery].addEventListener('peer', (evt: CustomEvent) => { this.#onDiscoveryPeer(evt) }) @@ -199,7 +202,7 @@ export class Libp2pNode> extends private configureComponent (name: string, component: T): T { if (component == null) { - log.error('component %s was null or undefined', name) + this.#log.error('component %s was null or undefined', name) } this.components[name] = component @@ -217,12 +220,12 @@ export class Libp2pNode> extends this.#started = true - log('libp2p is starting') + this.#log('libp2p is starting') const keys = await this.keychain.listKeys() if (keys.find(key => key.name === 'self') == null) { - log('importing self key into keychain') + this.#log('importing self key into keychain') await this.keychain.importPeer('self', this.components.peerId) } @@ -232,9 +235,9 @@ export class Libp2pNode> extends await this.components.afterStart?.() this.safeDispatchEvent('start', { detail: this }) - log('libp2p has started') + this.#log('libp2p has started') } catch (err: any) { - log.error('An error occurred starting libp2p', err) + this.#log.error('An error occurred starting libp2p', err) await this.stop() throw err } @@ -248,7 +251,7 @@ export class Libp2pNode> extends return } - log('libp2p is stopping') + this.#log('libp2p is stopping') this.#started = false @@ -257,7 +260,7 @@ export class Libp2pNode> extends await this.components.afterStop?.() this.safeDispatchEvent('stop', { detail: this }) - log('libp2p has stopped') + this.#log('libp2p has stopped') } isStarted (): boolean { @@ -322,7 +325,7 @@ export class Libp2pNode> extends * Get the public key for the given peer id */ async getPublicKey (peer: PeerId, options: AbortOptions = {}): Promise { - log('getPublicKey %p', peer) + this.#log('getPublicKey %p', peer) if (peer.publicKey != null) { return peer.publicKey @@ -391,7 +394,7 @@ export class Libp2pNode> extends const { detail: peer } = evt if (peer.id.toString() === this.peerId.toString()) { - log.error(new Error(codes.ERR_DISCOVERED_SELF)) + this.#log.error(new Error(codes.ERR_DISCOVERED_SELF)) return } @@ -399,7 +402,7 @@ export class Libp2pNode> extends multiaddrs: peer.multiaddrs, protocols: peer.protocols }) - .catch(err => { log.error(err) }) + .catch(err => { this.#log.error(err) }) } } diff --git a/packages/libp2p/src/upgrader.ts b/packages/libp2p/src/upgrader.ts index 0789e55900..f6b0bc574a 100644 --- a/packages/libp2p/src/upgrader.ts +++ b/packages/libp2p/src/upgrader.ts @@ -1,13 +1,12 @@ import { CodeError } from '@libp2p/interface/errors' import { setMaxListeners } from '@libp2p/interface/events' -import { logger } from '@libp2p/logger' import * as mss from '@libp2p/multistream-select' import { peerIdFromString } from '@libp2p/peer-id' import { createConnection } from './connection/index.js' import { INBOUND_UPGRADE_TIMEOUT } from './connection-manager/constants.js' import { codes } from './errors.js' import { DEFAULT_MAX_INBOUND_STREAMS, DEFAULT_MAX_OUTBOUND_STREAMS } from './registrar.js' -import type { Libp2pEvents, AbortOptions } from '@libp2p/interface' +import type { Libp2pEvents, AbortOptions, ComponentLogger, Logger } from '@libp2p/interface' import type { MultiaddrConnection, Connection, Stream, ConnectionProtector, NewStreamOptions } from '@libp2p/interface/connection' import type { ConnectionEncrypter, SecuredConnection } from '@libp2p/interface/connection-encrypter' import type { ConnectionGater } from '@libp2p/interface/connection-gater' @@ -21,8 +20,6 @@ import type { ConnectionManager } from '@libp2p/interface-internal/connection-ma import type { Registrar } from '@libp2p/interface-internal/registrar' import type { Duplex, Source } from 'it-stream-types' -const log = logger('libp2p:upgrader') - interface CreateConnectionOptions { cryptoProtocol: string direction: 'inbound' | 'outbound' @@ -105,6 +102,7 @@ export interface DefaultUpgraderComponents { registrar: Registrar peerStore: PeerStore events: TypedEventTarget + logger: ComponentLogger } type EncryptedConn = Duplex, Source, Promise> @@ -117,10 +115,12 @@ export class DefaultUpgrader implements Upgrader { private readonly muxers: Map private readonly inboundUpgradeTimeout: number private readonly events: TypedEventTarget + readonly #log: Logger constructor (components: DefaultUpgraderComponents, init: UpgraderInit) { this.components = components this.connectionEncryption = new Map() + this.#log = components.logger.forComponent('libp2p:upgrader') init.connectionEncryption.forEach(encrypter => { this.connectionEncryption.set(encrypter.protocol, encrypter) @@ -179,7 +179,7 @@ export class DefaultUpgrader implements Upgrader { this.components.metrics?.trackMultiaddrConnection(maConn) - log('starting the inbound connection upgrade') + this.#log('starting the inbound connection upgrade') // Protect let protectedConn = maConn @@ -188,7 +188,7 @@ export class DefaultUpgrader implements Upgrader { const protector = this.components.connectionProtector if (protector != null) { - log('protecting the inbound connection') + this.#log('protecting the inbound connection') protectedConn = await protector.protect(maConn) } } @@ -235,13 +235,13 @@ export class DefaultUpgrader implements Upgrader { upgradedConn = multiplexed.stream } } catch (err: any) { - log.error('Failed to upgrade inbound connection', err) + this.#log.error('Failed to upgrade inbound connection', err) throw err } await this.shouldBlockConnection(remotePeer, maConn, 'denyInboundUpgradedConnection') - log('Successfully upgraded inbound connection') + this.#log('Successfully upgraded inbound connection') return this._createConnection({ cryptoProtocol, @@ -280,7 +280,7 @@ export class DefaultUpgrader implements Upgrader { this.components.metrics?.trackMultiaddrConnection(maConn) - log('Starting the outbound connection upgrade') + this.#log('Starting the outbound connection upgrade') // If the transport natively supports encryption, skip connection // protector and encryption @@ -333,14 +333,14 @@ export class DefaultUpgrader implements Upgrader { upgradedConn = multiplexed.stream } } catch (err: any) { - log.error('Failed to upgrade outbound connection', err) + this.#log.error('Failed to upgrade outbound connection', err) await maConn.close(err) throw err } await this.shouldBlockConnection(remotePeer, maConn, 'denyOutboundUpgradedConnection') - log('Successfully upgraded outbound connection') + this.#log('Successfully upgraded outbound connection') return this._createConnection({ cryptoProtocol, @@ -385,7 +385,7 @@ export class DefaultUpgrader implements Upgrader { .then(async () => { const protocols = this.components.registrar.getProtocols() const { stream, protocol } = await mss.handle(muxedStream, protocols) - log('%s: incoming stream opened on %s', direction, protocol) + this.#log('%s: incoming stream opened on %s', direction, protocol) if (connection == null) { return @@ -418,7 +418,7 @@ export class DefaultUpgrader implements Upgrader { this._onStream({ connection, stream: muxedStream, protocol }) }) .catch(async err => { - log.error(err) + this.#log.error(err) if (muxedStream.timeline.close == null) { await muxedStream.close() @@ -432,12 +432,12 @@ export class DefaultUpgrader implements Upgrader { throw new CodeError('Stream is not multiplexed', codes.ERR_MUXER_UNAVAILABLE) } - log('%s-%s: starting new stream on %s', connection.id, direction, protocols) + this.#log('%s-%s: starting new stream on %s', connection.id, direction, protocols) const muxedStream = await muxer.newStream() try { if (options.signal == null) { - log('No abort signal was passed while trying to negotiate protocols %s falling back to default timeout', protocols) + this.#log('No abort signal was passed while trying to negotiate protocols %s falling back to default timeout', protocols) options.signal = AbortSignal.timeout(30000) @@ -472,7 +472,7 @@ export class DefaultUpgrader implements Upgrader { return muxedStream } catch (err: any) { - log.error('could not create new stream for protocols %s on connection with address %a', protocols, connection.remoteAddr, err) + this.#log.error('could not create new stream for protocols %s on connection with address %a', protocols, connection.remoteAddr, err) if (muxedStream.timeline.close == null) { muxedStream.abort(err) @@ -491,7 +491,7 @@ export class DefaultUpgrader implements Upgrader { muxer.sink(upgradedConn.source), upgradedConn.sink(muxer.source) ]).catch(err => { - log.error(err) + this.#log.error(err) }) } @@ -506,14 +506,14 @@ export class DefaultUpgrader implements Upgrader { await connection.close() } } catch (err: any) { - log.error(err) + this.#log.error(err) } finally { this.events.safeDispatchEvent('connection:close', { detail: connection }) } })().catch(err => { - log.error(err) + this.#log.error(err) }) } @@ -536,19 +536,20 @@ export class DefaultUpgrader implements Upgrader { multiplexer: muxer?.protocol, encryption: cryptoProtocol, transient, + logger: this.components.logger, newStream: newStream ?? errConnectionNotMultiplexed, getStreams: () => { if (muxer != null) { return muxer.streams } else { return [] } }, close: async (options?: AbortOptions) => { // Ensure remaining streams are closed gracefully if (muxer != null) { - log.trace('close muxer') + this.#log.trace('close muxer') await muxer.close(options) } - log.trace('close maconn') + this.#log.trace('close maconn') // close the underlying transport await maConn.close(options) - log.trace('closed maconn') + this.#log.trace('closed maconn') }, abort: (err) => { maConn.abort(err) @@ -585,7 +586,7 @@ export class DefaultUpgrader implements Upgrader { */ async _encryptInbound (connection: Duplex, Source>): Promise { const protocols = Array.from(this.connectionEncryption.keys()) - log('handling inbound crypto protocol selection', protocols) + this.#log('handling inbound crypto protocol selection', protocols) try { const { stream, protocol } = await mss.handle(connection, protocols, { @@ -597,7 +598,7 @@ export class DefaultUpgrader implements Upgrader { throw new Error(`no crypto module found for ${protocol}`) } - log('encrypting inbound connection...') + this.#log('encrypting inbound connection...') return { ...await encrypter.secureInbound(this.components.peerId, stream), @@ -614,7 +615,7 @@ export class DefaultUpgrader implements Upgrader { */ async _encryptOutbound (connection: MultiaddrConnection, remotePeerId?: PeerId): Promise { const protocols = Array.from(this.connectionEncryption.keys()) - log('selecting outbound crypto protocol', protocols) + this.#log('selecting outbound crypto protocol', protocols) try { const { stream, protocol } = await mss.select(connection, protocols, { @@ -626,7 +627,7 @@ export class DefaultUpgrader implements Upgrader { throw new Error(`no crypto module found for ${protocol}`) } - log('encrypting outbound connection to %p', remotePeerId) + this.#log('encrypting outbound connection to %p', remotePeerId) return { ...await encrypter.secureOutbound(this.components.peerId, stream, remotePeerId), @@ -643,17 +644,17 @@ export class DefaultUpgrader implements Upgrader { */ async _multiplexOutbound (connection: MultiaddrConnection, muxers: Map): Promise<{ stream: Duplex, Source, Promise>, muxerFactory?: StreamMuxerFactory }> { const protocols = Array.from(muxers.keys()) - log('outbound selecting muxer %s', protocols) + this.#log('outbound selecting muxer %s', protocols) try { const { stream, protocol } = await mss.select(connection, protocols, { writeBytes: true }) - log('%s selected as muxer protocol', protocol) + this.#log('%s selected as muxer protocol', protocol) const muxerFactory = muxers.get(protocol) return { stream, muxerFactory } } catch (err: any) { - log.error('error multiplexing outbound stream', err) + this.#log.error('error multiplexing outbound stream', err) throw new CodeError(String(err), codes.ERR_MUXER_UNAVAILABLE) } } @@ -664,7 +665,7 @@ export class DefaultUpgrader implements Upgrader { */ async _multiplexInbound (connection: MultiaddrConnection, muxers: Map): Promise<{ stream: Duplex, Source, Promise>, muxerFactory?: StreamMuxerFactory }> { const protocols = Array.from(muxers.keys()) - log('inbound handling muxers %s', protocols) + this.#log('inbound handling muxers %s', protocols) try { const { stream, protocol } = await mss.handle(connection, protocols, { writeBytes: true @@ -673,7 +674,7 @@ export class DefaultUpgrader implements Upgrader { return { stream, muxerFactory } } catch (err: any) { - log.error('error multiplexing inbound stream', err) + this.#log.error('error multiplexing inbound stream', err) throw new CodeError(String(err), codes.ERR_MUXER_UNAVAILABLE) } } diff --git a/packages/libp2p/test/connection-manager/auto-dial.spec.ts b/packages/libp2p/test/connection-manager/auto-dial.spec.ts index 9e45ae8c7b..b9d32357a2 100644 --- a/packages/libp2p/test/connection-manager/auto-dial.spec.ts +++ b/packages/libp2p/test/connection-manager/auto-dial.spec.ts @@ -12,6 +12,7 @@ import pWaitFor from 'p-wait-for' import Sinon from 'sinon' import { stubInterface } from 'sinon-ts' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import { defaultComponents } from '../../src/components.js' import { AutoDial } from '../../src/connection-manager/auto-dial.js' import { LAST_DIAL_FAILURE_KEY } from '../../src/connection-manager/constants.js' import { matchPeerId } from '../fixtures/match-peer-id.js' @@ -73,11 +74,11 @@ describe('auto-dial', () => { getDialQueue: Sinon.stub().returns([]) }) - autoDialler = new AutoDial({ + autoDialler = new AutoDial(defaultComponents({ peerStore, connectionManager, events - }, { + }), { minConnections: 10, autoDialInterval: 10000 }) @@ -127,11 +128,11 @@ describe('auto-dial', () => { getDialQueue: Sinon.stub().returns([]) }) - autoDialler = new AutoDial({ + autoDialler = new AutoDial(defaultComponents({ peerStore, connectionManager, events - }, { + }), { minConnections: 10 }) autoDialler.start() @@ -181,11 +182,11 @@ describe('auto-dial', () => { }]) }) - autoDialler = new AutoDial({ + autoDialler = new AutoDial(defaultComponents({ peerStore, connectionManager, events - }, { + }), { minConnections: 10 }) autoDialler.start() @@ -207,11 +208,11 @@ describe('auto-dial', () => { getDialQueue: Sinon.stub().returns([]) }) - autoDialler = new AutoDial({ + autoDialler = new AutoDial(defaultComponents({ peerStore, connectionManager, events - }, { + }), { minConnections: 10, autoDialInterval: 10000 }) @@ -258,11 +259,11 @@ describe('auto-dial', () => { getDialQueue: Sinon.stub().returns([]) }) - autoDialler = new AutoDial({ + autoDialler = new AutoDial(defaultComponents({ peerStore, connectionManager, events - }, { + }), { minConnections: 10, autoDialPeerRetryThreshold: 2000 }) diff --git a/packages/libp2p/test/connection-manager/dial-queue.spec.ts b/packages/libp2p/test/connection-manager/dial-queue.spec.ts index 9a4ae50f5f..ed07ab79e5 100644 --- a/packages/libp2p/test/connection-manager/dial-queue.spec.ts +++ b/packages/libp2p/test/connection-manager/dial-queue.spec.ts @@ -1,6 +1,7 @@ /* eslint-env mocha */ import { mockConnection, mockDuplex, mockMultiaddrConnection } from '@libp2p/interface-compliance-tests/mocks' +import { peerLogger } from '@libp2p/logger' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { multiaddr, resolvers } from '@multiformats/multiaddr' import { WebRTC } from '@multiformats/multiaddr-matcher' @@ -10,6 +11,7 @@ import pDefer from 'p-defer' import sinon from 'sinon' import { type StubbedInstance, stubInterface } from 'sinon-ts' import { DialQueue } from '../../src/connection-manager/dial-queue.js' +import type { ComponentLogger } from '@libp2p/interface' import type { Connection } from '@libp2p/interface/connection' import type { ConnectionGater } from '@libp2p/interface/connection-gater' import type { PeerId } from '@libp2p/interface/peer-id' @@ -23,15 +25,19 @@ describe('dial queue', () => { peerStore: StubbedInstance transportManager: StubbedInstance connectionGater: StubbedInstance + logger: ComponentLogger } let dialer: DialQueue beforeEach(async () => { + const peerId = await createEd25519PeerId() + components = { - peerId: await createEd25519PeerId(), + peerId, peerStore: stubInterface(), transportManager: stubInterface(), - connectionGater: stubInterface() + connectionGater: stubInterface(), + logger: peerLogger(peerId) } }) diff --git a/packages/libp2p/test/connection-manager/index.spec.ts b/packages/libp2p/test/connection-manager/index.spec.ts index 4cde008591..1197294233 100644 --- a/packages/libp2p/test/connection-manager/index.spec.ts +++ b/packages/libp2p/test/connection-manager/index.spec.ts @@ -3,6 +3,7 @@ import { TypedEventEmitter } from '@libp2p/interface/events' import { KEEP_ALIVE } from '@libp2p/interface/peer-store/tags' import { mockConnection, mockDuplex, mockMultiaddrConnection, mockMetrics } from '@libp2p/interface-compliance-tests/mocks' +import { defaultLogger } from '@libp2p/logger' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' @@ -365,7 +366,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, deny: [ @@ -393,7 +395,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, maxConnections: 1 @@ -425,7 +428,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, inboundConnectionThreshold: 1 @@ -461,7 +465,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, maxConnections: 1, @@ -497,7 +502,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, maxIncomingPendingConnections: 1 @@ -543,7 +549,8 @@ describe('Connection Manager', () => { peerStore: stubInterface(), transportManager: stubInterface(), connectionGater: stubInterface(), - events: new TypedEventEmitter() + events: new TypedEventEmitter(), + logger: defaultLogger() }, { ...defaultOptions, maxIncomingPendingConnections: 1 diff --git a/packages/libp2p/test/connection/compliance.spec.ts b/packages/libp2p/test/connection/compliance.spec.ts index 41059425b7..715f946f4e 100644 --- a/packages/libp2p/test/connection/compliance.spec.ts +++ b/packages/libp2p/test/connection/compliance.spec.ts @@ -1,5 +1,6 @@ import tests from '@libp2p/interface-compliance-tests/connection' import peers from '@libp2p/interface-compliance-tests/peers' +import { peerLogger } from '@libp2p/logger' import * as PeerIdFactory from '@libp2p/peer-id-factory' import { multiaddr } from '@multiformats/multiaddr' import { createConnection } from '../../src/connection/index.js' @@ -13,6 +14,7 @@ describe('connection compliance', () => { * certain values for testing. */ async setup (properties) { + const localPeer = await PeerIdFactory.createEd25519PeerId() const remoteAddr = multiaddr('/ip4/127.0.0.1/tcp/8081') const remotePeer = await PeerIdFactory.createFromJSON(peers[0]) let openStreams: Stream[] = [] @@ -29,6 +31,7 @@ describe('connection compliance', () => { encryption: '/secio/1.0.0', multiplexer: '/mplex/6.7.0', status: 'open', + logger: peerLogger(localPeer), newStream: async (protocols) => { const id = `${streamId++}` const stream: Stream = { diff --git a/packages/libp2p/test/connection/index.spec.ts b/packages/libp2p/test/connection/index.spec.ts index d249731f55..9fd8121d7f 100644 --- a/packages/libp2p/test/connection/index.spec.ts +++ b/packages/libp2p/test/connection/index.spec.ts @@ -1,3 +1,4 @@ +import { defaultLogger } from '@libp2p/logger' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'aegir/chai' @@ -17,7 +18,8 @@ function defaultConnectionInit (): any { newStream: Sinon.stub(), close: Sinon.stub(), abort: Sinon.stub(), - getStreams: Sinon.stub() + getStreams: Sinon.stub(), + logger: defaultLogger() } } diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts index 1bb24ea8dc..be3572c218 100644 --- a/packages/logger/src/index.ts +++ b/packages/logger/src/index.ts @@ -1,9 +1,8 @@ /** * @packageDocumentation * - * A map that reports it's size to the libp2p [Metrics](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/metrics#readme) system. - * - * If metrics are disabled a regular map is used. + * A logger for libp2p based on the venerable [debug](https://www.npmjs.com/package/debug) + * module. * * @example * @@ -33,6 +32,7 @@ import debug from 'debug' import { base32 } from 'multiformats/bases/base32' import { base58btc } from 'multiformats/bases/base58' import { base64 } from 'multiformats/bases/base64' +import { truncatePeerId } from './utils.js' import type { PeerId } from '@libp2p/interface/peer-id' import type { Multiaddr } from '@multiformats/multiaddr' import type { Key } from 'interface-datastore' @@ -80,6 +80,10 @@ export interface Logger { enabled: boolean } +export interface ComponentLogger { + forComponent(name: string): Logger +} + function createDisabledLogger (namespace: string): debug.Debugger { const logger = (): void => {} logger.enabled = false @@ -93,6 +97,94 @@ function createDisabledLogger (namespace: string): debug.Debugger { return logger } +export interface PeerLoggerOptions { + prefixLength: number + suffixLength: number +} + +/** + * Create a component logger that will prefix any log messages with a truncated + * peer id. + * + * @example + * + * ```TypeScript + * import { peerLogger } from '@libp2p/logger' + * import { peerIdFromString } from '@libp2p/peer-id' + * + * const peerId = peerIdFromString('12D3FooBar') + * const logger = peerLogger(peerId) + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "12…oBar:my-component hello world" + * ``` + */ +export function peerLogger (peerId: PeerId, options: Partial = {}): ComponentLogger { + return prefixLogger(truncatePeerId(peerId, options)) +} + +/** + * Create a component logger that will prefix any log messages with the passed + * string. + * + * @example + * + * ```TypeScript + * import { prefixLogger } from '@libp2p/logger' + * + * const logger = prefixLogger('my-node') + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "my-node:my-component hello world" + * ``` + */ +export function prefixLogger (prefix: string): ComponentLogger { + return { + forComponent (name: string) { + return logger(`${prefix}:${name}`) + } + } +} + +/** + * Create a component logger + * + * @example + * + * ```TypeScript + * import { defaultLogger } from '@libp2p/logger' + * import { peerIdFromString } from '@libp2p/peer-id' + * + * const logger = defaultLogger() + * + * const log = logger.forComponent('my-component') + * log.info('hello world') + * // logs "my-component hello world" + * ``` + */ +export function defaultLogger (): ComponentLogger { + return { + forComponent (name: string) { + return logger(name) + } + } +} + +/** + * Creates a logger for the passed component name. + * + * @example + * + * ```TypeScript + * import { logger } from '@libp2p/logger' + * + * const log = logger('my-component') + * log.info('hello world') + * // logs "my-component hello world" + * ``` + */ export function logger (name: string): Logger { // trace logging is a no-op by default let trace: debug.Debugger = createDisabledLogger(`${name}:trace`) diff --git a/packages/logger/src/utils.ts b/packages/logger/src/utils.ts new file mode 100644 index 0000000000..27b4ebcc62 --- /dev/null +++ b/packages/logger/src/utils.ts @@ -0,0 +1,10 @@ +import type { PeerLoggerOptions } from './index.js' +import type { PeerId } from '@libp2p/interface/peer-id' + +export function truncatePeerId (peerId: PeerId, options: Partial = {}): string { + const prefixLength = options.prefixLength ?? 2 + const suffixLength = options.suffixLength ?? 4 + + const peerIdString = peerId.toString() + return `${peerIdString.substring(0, prefixLength)}…${peerIdString.substring(peerIdString.length, peerIdString.length - suffixLength)}` +} diff --git a/packages/logger/test/index.spec.ts b/packages/logger/test/index.spec.ts index 8b8b1b6731..5c1c9dccd6 100644 --- a/packages/logger/test/index.spec.ts +++ b/packages/logger/test/index.spec.ts @@ -9,7 +9,7 @@ import { base64 } from 'multiformats/bases/base64' import sinon from 'sinon' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as unint8ArrayToString } from 'uint8arrays/to-string' -import { logger } from '../src/index.js' +import { logger, peerLogger } from '../src/index.js' describe('logger', () => { it('creates a logger', () => { @@ -23,6 +23,19 @@ describe('logger', () => { expect(log).to.have.nested.property('trace.enabled').that.is.not.true() }) + it('creates a peer logger', () => { + const peerId = peerIdFromString('12D3KooWLkHeUp6r5unBZKbYwV54CgKVxHuJDroFoigr8mF11CKW') + const logger = peerLogger(peerId) + const log = logger.forComponent('hello') + + expect(log).to.be.a('function') + expect(log).to.a.property('enabled').that.is.not.true() + expect(log).to.have.property('error').that.is.a('function') + expect(log).to.have.nested.property('error.enabled').that.is.not.true() + expect(log).to.have.property('trace').that.is.a('function') + expect(log).to.have.nested.property('trace.enabled').that.is.not.true() + }) + it('creates a logger with logging enabled', () => { debug.enable('enabled-logger') diff --git a/packages/logger/test/utils.spec.ts b/packages/logger/test/utils.spec.ts new file mode 100644 index 0000000000..5525ab0d82 --- /dev/null +++ b/packages/logger/test/utils.spec.ts @@ -0,0 +1,11 @@ +import { peerIdFromString } from '@libp2p/peer-id' +import { expect } from 'aegir/chai' +import { truncatePeerId } from '../src/utils.js' + +describe('utils', () => { + it('should truncate a peer id', () => { + const peerId = peerIdFromString('QmZ8eiDPqQqWR17EPxiwCDgrKPVhCHLcyn6xSCNpFAdAZb') + + expect(truncatePeerId(peerId)).to.equal('Qm…dAZb') + }) +})