Skip to content

Commit dacc999

Browse files
authored
fix: use passed logger (#183)
Add a `components`/`init` style constructor so the same logger/etc can be used as other parts of the ecosystem.
1 parent c4ba881 commit dacc999

File tree

4 files changed

+78
-36
lines changed

4 files changed

+78
-36
lines changed

packages/client/src/client.ts

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { NotFoundError, contentRoutingSymbol, peerRoutingSymbol, setMaxListeners } from '@libp2p/interface'
2-
import { logger } from '@libp2p/logger'
32
import { peerIdFromString } from '@libp2p/peer-id'
43
import { multiaddr } from '@multiformats/multiaddr'
54
import { anySignal } from 'any-signal'
@@ -11,26 +10,24 @@ import defer from 'p-defer'
1110
import PQueue from 'p-queue'
1211
import { BadResponseError, InvalidRequestError } from './errors.js'
1312
import { DelegatedRoutingV1HttpApiClientContentRouting, DelegatedRoutingV1HttpApiClientPeerRouting } from './routings.js'
14-
import type { DelegatedRoutingV1HttpApiClient, DelegatedRoutingV1HttpApiClientInit, GetProvidersOptions, GetPeersOptions, GetIPNSOptions, PeerRecord } from './index.js'
15-
import type { ContentRouting, PeerRouting, AbortOptions, PeerId } from '@libp2p/interface'
13+
import type { DelegatedRoutingV1HttpApiClient as DelegatedRoutingV1HttpApiClientInterface, DelegatedRoutingV1HttpApiClientInit, GetProvidersOptions, GetPeersOptions, GetIPNSOptions, PeerRecord, DelegatedRoutingV1HttpApiClientComponents } from './index.js'
14+
import type { ContentRouting, PeerRouting, AbortOptions, PeerId, Logger } from '@libp2p/interface'
1615
import type { Multiaddr } from '@multiformats/multiaddr'
1716
import type { IPNSRecord } from 'ipns'
1817
import type { CID } from 'multiformats'
1918

20-
const log = logger('delegated-routing-v1-http-api-client')
21-
2219
const defaultValues = {
2320
concurrentRequests: 4,
2421
timeout: 30e3,
2522
cacheTTL: 5 * 60 * 1000, // 5 minutes default as per https://specs.ipfs.tech/routing/http-routing-v1/#response-headers
2623
cacheName: 'delegated-routing-v1-cache'
2724
}
2825

29-
export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV1HttpApiClient {
26+
export class DelegatedRoutingV1HttpApiClient implements DelegatedRoutingV1HttpApiClientInterface {
27+
public readonly url: URL
3028
private started: boolean
3129
private readonly httpQueue: PQueue
3230
private readonly shutDownController: AbortController
33-
private readonly clientUrl: URL
3431
private readonly timeout: number
3532
private readonly contentRouting: ContentRouting
3633
private readonly peerRouting: PeerRouting
@@ -40,18 +37,21 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
4037
private readonly cacheName: string
4138
private cache?: Cache
4239
private readonly cacheTTL: number
40+
private log: Logger
41+
4342
/**
4443
* Create a new DelegatedContentRouting instance
4544
*/
46-
constructor (url: string | URL, init: DelegatedRoutingV1HttpApiClientInit = {}) {
45+
constructor (components: DelegatedRoutingV1HttpApiClientComponents, init: DelegatedRoutingV1HttpApiClientInit & { url: string | URL }) {
46+
this.log = components.logger.forComponent('delegated-routing-v1-http-api-client')
4747
this.started = false
4848
this.shutDownController = new AbortController()
4949
setMaxListeners(Infinity, this.shutDownController.signal)
5050
this.httpQueue = new PQueue({
5151
concurrency: init.concurrentRequests ?? defaultValues.concurrentRequests
5252
})
5353
this.inFlightRequests = new Map() // Tracks in-flight requests to avoid duplicate requests
54-
this.clientUrl = url instanceof URL ? url : new URL(url)
54+
this.url = init.url instanceof URL ? init.url : new URL(init.url)
5555
this.timeout = init.timeout ?? defaultValues.timeout
5656
this.filterAddrs = init.filterAddrs
5757
this.filterProtocols = init.filterProtocols
@@ -85,7 +85,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
8585
this.cache = await globalThis.caches?.open(this.cacheName)
8686

8787
if (this.cache != null) {
88-
log('cache enabled with ttl %d', this.cacheTTL)
88+
this.log('cache enabled with ttl %d', this.cacheTTL)
8989
}
9090
}
9191
}
@@ -101,7 +101,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
101101
}
102102

103103
async * getProviders (cid: CID, options: GetProvidersOptions = {}): AsyncGenerator<PeerRecord> {
104-
log('getProviders starts: %c', cid)
104+
this.log('getProviders starts: %c', cid)
105105

106106
const timeoutSignal = AbortSignal.timeout(this.timeout)
107107
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -118,7 +118,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
118118
await onStart.promise
119119

120120
// https://specs.ipfs.tech/routing/http-routing-v1/
121-
const url = new URL(`${this.clientUrl}routing/v1/providers/${cid}`)
121+
const url = new URL(`${this.url}routing/v1/providers/${cid}`)
122122

123123
this.#addFilterParams(url, options.filterAddrs, options.filterProtocols)
124124
const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
@@ -175,12 +175,12 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
175175
} finally {
176176
signal.clear()
177177
onFinish.resolve()
178-
log('getProviders finished: %c', cid)
178+
this.log('getProviders finished: %c', cid)
179179
}
180180
}
181181

182182
async * getPeers (peerId: PeerId, options: GetPeersOptions = {}): AsyncGenerator<PeerRecord> {
183-
log('getPeers starts: %c', peerId)
183+
this.log('getPeers starts: %c', peerId)
184184

185185
const timeoutSignal = AbortSignal.timeout(this.timeout)
186186
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -197,7 +197,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
197197
await onStart.promise
198198

199199
// https://specs.ipfs.tech/routing/http-routing-v1/
200-
const url = new URL(`${this.clientUrl}routing/v1/peers/${peerId.toCID().toString()}`)
200+
const url = new URL(`${this.url}routing/v1/peers/${peerId.toCID().toString()}`)
201201
this.#addFilterParams(url, options.filterAddrs, options.filterProtocols)
202202

203203
const getOptions = { headers: { Accept: 'application/x-ndjson' }, signal }
@@ -241,16 +241,16 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
241241
}
242242
}
243243
} catch (err) {
244-
log.error('getPeers errored:', err)
244+
this.log.error('getPeers errored - %e', err)
245245
} finally {
246246
signal.clear()
247247
onFinish.resolve()
248-
log('getPeers finished: %c', peerId)
248+
this.log('getPeers finished: %c', peerId)
249249
}
250250
}
251251

252252
async getIPNS (libp2pKey: CID<unknown, 0x72, 0x00 | 0x12, 1>, options: GetIPNSOptions = {}): Promise<IPNSRecord> {
253-
log('getIPNS starts: %s', libp2pKey)
253+
this.log('getIPNS starts: %s', libp2pKey)
254254

255255
const timeoutSignal = AbortSignal.timeout(this.timeout)
256256
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -264,15 +264,15 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
264264
})
265265

266266
// https://specs.ipfs.tech/routing/http-routing-v1/
267-
const resource = `${this.clientUrl}routing/v1/ipns/${libp2pKey}`
267+
const resource = `${this.url}routing/v1/ipns/${libp2pKey}`
268268

269269
try {
270270
await onStart.promise
271271

272272
const getOptions = { headers: { Accept: 'application/vnd.ipfs.ipns-record' }, signal }
273273
const res = await this.#makeRequest(resource, getOptions)
274274

275-
log('getIPNS GET %s %d', resource, res.status)
275+
this.log('getIPNS GET %s %d', resource, res.status)
276276

277277
// Per IPIP-0513: Handle 404 as "no record found" for backward compatibility
278278
// IPNS is different - we still throw NotFoundError for 404 (backward compat)
@@ -311,18 +311,18 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
311311

312312
return unmarshalIPNSRecord(body)
313313
} catch (err: any) {
314-
log.error('getIPNS GET %s error:', resource, err)
314+
this.log.error('getIPNS GET %s error - %e', resource, err)
315315

316316
throw err
317317
} finally {
318318
signal.clear()
319319
onFinish.resolve()
320-
log('getIPNS finished: %s', libp2pKey)
320+
this.log('getIPNS finished: %s', libp2pKey)
321321
}
322322
}
323323

324324
async putIPNS (libp2pKey: CID<unknown, 0x72, 0x00 | 0x12, 1>, record: IPNSRecord, options: AbortOptions = {}): Promise<void> {
325-
log('putIPNS starts: %c', libp2pKey)
325+
this.log('putIPNS starts: %c', libp2pKey)
326326

327327
const timeoutSignal = AbortSignal.timeout(this.timeout)
328328
const signal = anySignal([this.shutDownController.signal, timeoutSignal, options.signal])
@@ -336,7 +336,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
336336
})
337337

338338
// https://specs.ipfs.tech/routing/http-routing-v1/
339-
const resource = `${this.clientUrl}routing/v1/ipns/${libp2pKey}`
339+
const resource = `${this.url}routing/v1/ipns/${libp2pKey}`
340340

341341
try {
342342
await onStart.promise
@@ -346,19 +346,19 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
346346
const getOptions = { method: 'PUT', headers: { 'Content-Type': 'application/vnd.ipfs.ipns-record' }, body, signal }
347347
const res = await this.#makeRequest(resource, getOptions)
348348

349-
log('putIPNS PUT %s %d', resource, res.status)
349+
this.log('putIPNS PUT %s %d', resource, res.status)
350350

351351
if (res.status !== 200) {
352352
throw new BadResponseError('PUT ipns response had status other than 200')
353353
}
354354
} catch (err: any) {
355-
log.error('putIPNS PUT %s error:', resource, err.stack)
355+
this.log.error('putIPNS PUT %s error - %e', resource, err.stack)
356356

357357
throw err
358358
} finally {
359359
signal.clear()
360360
onFinish.resolve()
361-
log('putIPNS finished: %c', libp2pKey)
361+
this.log('putIPNS finished: %c', libp2pKey)
362362
}
363363
}
364364

@@ -384,7 +384,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
384384
Protocols: protocols
385385
}
386386
} catch (err) {
387-
log.error('could not conform record to peer schema', err)
387+
this.log.error('could not conform record to peer schema - %e', err)
388388
}
389389
}
390390

@@ -420,7 +420,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
420420
// Check if the cached response has expired
421421
const expires = parseInt(cachedResponse.headers.get('x-cache-expires') ?? '0', 10)
422422
if (expires > Date.now()) {
423-
log('returning cached response for %s', key)
423+
this.log('returning cached response for %s', key)
424424
return cachedResponse
425425
} else {
426426
// Remove expired response from cache
@@ -433,7 +433,7 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
433433
const existingRequest = this.inFlightRequests.get(key)
434434
if (existingRequest != null) {
435435
const response = await existingRequest
436-
log('deduplicating outgoing request for %s', key)
436+
this.log('deduplicating outgoing request for %s', key)
437437
return response.clone()
438438
}
439439

@@ -464,4 +464,8 @@ export class DefaultDelegatedRoutingV1HttpApiClient implements DelegatedRoutingV
464464
const response = await requestPromise
465465
return response
466466
}
467+
468+
toString (): string {
469+
return `DefaultDelegatedRoutingV1HttpApiClient(${this.url})`
470+
}
467471
}

packages/client/src/index.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,9 @@
8383
* ```
8484
*/
8585

86-
import { DefaultDelegatedRoutingV1HttpApiClient } from './client.js'
87-
import type { AbortOptions, PeerId } from '@libp2p/interface'
86+
import { defaultLogger } from '@libp2p/logger'
87+
import { DelegatedRoutingV1HttpApiClient as DelegatedRoutingV1HttpApiClientClass } from './client.js'
88+
import type { AbortOptions, ComponentLogger, PeerId } from '@libp2p/interface'
8889
import type { Multiaddr } from '@multiformats/multiaddr'
8990
import type { IPNSRecord } from 'ipns'
9091
import type { CID } from 'multiformats/cid'
@@ -155,6 +156,10 @@ export interface DelegatedRoutingV1HttpApiClientInit extends FilterOptions {
155156
cacheName?: string
156157
}
157158

159+
export interface DelegatedRoutingV1HttpApiClientComponents {
160+
logger: ComponentLogger
161+
}
162+
158163
export interface GetIPNSOptions extends AbortOptions {
159164
/**
160165
* By default incoming IPNS records are validated, pass false here to skip
@@ -169,6 +174,11 @@ export type GetProvidersOptions = FilterOptions & AbortOptions
169174
export type GetPeersOptions = FilterOptions & AbortOptions
170175

171176
export interface DelegatedRoutingV1HttpApiClient {
177+
/**
178+
* The URL that requests are sent to
179+
*/
180+
url: URL
181+
172182
/**
173183
* Returns an async generator of {@link PeerRecord}s that can provide the
174184
* content for the passed {@link CID}
@@ -206,7 +216,23 @@ export interface DelegatedRoutingV1HttpApiClient {
206216

207217
/**
208218
* Create and return a client to use with a Routing V1 HTTP API server
219+
*
220+
* @deprecated use `delegatedRoutingV1HttpApiClient` instead - this function will be removed in a future release
221+
*/
222+
export function createDelegatedRoutingV1HttpApiClient (url: URL | string, init: Omit<DelegatedRoutingV1HttpApiClientInit, 'url'> = {}): DelegatedRoutingV1HttpApiClient {
223+
return new DelegatedRoutingV1HttpApiClientClass({
224+
logger: defaultLogger()
225+
}, {
226+
...init,
227+
url: new URL(url)
228+
})
229+
}
230+
231+
/**
232+
* Create and return a client to use with a Routing V1 HTTP API server
233+
*
234+
* TODO: add `url` to `DelegatedRoutingV1HttpApiClientInit` interface and release as breaking change
209235
*/
210-
export function createDelegatedRoutingV1HttpApiClient (url: URL | string, init: DelegatedRoutingV1HttpApiClientInit = {}): DelegatedRoutingV1HttpApiClient {
211-
return new DefaultDelegatedRoutingV1HttpApiClient(new URL(url), init)
236+
export function delegatedRoutingV1HttpApiClient (init: DelegatedRoutingV1HttpApiClientInit & { url: string | URL }): (components: DelegatedRoutingV1HttpApiClientComponents) => DelegatedRoutingV1HttpApiClient {
237+
return (components) => new DelegatedRoutingV1HttpApiClientClass(components, init)
212238
}

packages/client/src/routings.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ export class DelegatedRoutingV1HttpApiClientContentRouting implements ContentRou
8686
throw err
8787
}
8888
}
89+
90+
toString (): string {
91+
return `DelegatedRoutingV1HttpApiClientContentRouting(${this.client.url})`
92+
}
8993
}
9094

9195
/**
@@ -114,4 +118,8 @@ export class DelegatedRoutingV1HttpApiClientPeerRouting implements PeerRouting {
114118
async * getClosestPeers (key: Uint8Array, options: AbortOptions = {}): AsyncIterable<PeerInfo> {
115119
// noop
116120
}
121+
122+
toString (): string {
123+
return `DelegatedRoutingV1HttpApiClientPeerRouting(${this.client.url})`
124+
}
117125
}

packages/client/test/client.spec.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
import { defaultLogger } from '@libp2p/logger'
12
import { expect } from 'aegir/chai'
2-
import { DefaultDelegatedRoutingV1HttpApiClient } from '../src/client.js'
3+
import { DelegatedRoutingV1HttpApiClient } from '../src/client.js'
34
import { itBrowser } from './fixtures/it.js'
45

56
describe('client', () => {
67
itBrowser('should remove cache on stop', async function () {
78
const cacheName = 'test-cache'
89

9-
const client = new DefaultDelegatedRoutingV1HttpApiClient('http://example.com', {
10+
const client = new DelegatedRoutingV1HttpApiClient({
11+
logger: defaultLogger()
12+
}, {
13+
url: 'http://example.com',
1014
cacheName
1115
})
1216
await client.start()

0 commit comments

Comments
 (0)