Skip to content

Commit

Permalink
feat: add debug logging (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexlwn123 authored Sep 23, 2024
1 parent 0f0db4f commit 828fbe3
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 58 deletions.
2 changes: 2 additions & 0 deletions examples/vite-core/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { FedimintWallet } from '@fedimint/core-web'

const wallet = new FedimintWallet()

wallet.setLogLevel('debug')

wallet.open()

export { wallet }
14 changes: 14 additions & 0 deletions packages/core-web/src/FedimintWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
FederationService,
RecoveryService,
} from './services'
import { logger, type LogLevel } from './utils/logger'

const DEFAULT_CLIENT_NAME = 'fm-default' as const

Expand Down Expand Up @@ -63,13 +64,17 @@ export class FedimintWallet {
this.federation = new FederationService(this.client)
this.recovery = new RecoveryService(this.client)

logger.info('FedimintWallet instantiated')

if (!lazy) {
this.initialize()
}
}

async initialize() {
logger.info('Initializing WorkerClient')
await this.client.initialize()
logger.info('WorkerClient initialized')
}

async waitForOpen() {
Expand Down Expand Up @@ -124,4 +129,13 @@ export class FedimintWallet {
isOpen() {
return this._isOpen
}

/**
* Sets the log level for the library.
* @param level The desired log level ('DEBUG', 'INFO', 'WARN', 'ERROR', 'NONE').
*/
setLogLevel(level: LogLevel) {
logger.setLevel(level)
logger.info(`Log level set to ${level}.`)
}
}
61 changes: 61 additions & 0 deletions packages/core-web/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const logLevels = ['debug', 'info', 'warn', 'error', 'none'] as const
export type LogLevel = (typeof logLevels)[number]

export class Logger {
private level: LogLevel

constructor(level: LogLevel = 'none') {
this.level = level
}

setLevel(level: LogLevel) {
this.level = level
}

coerceLevel(level: string): LogLevel {
if (logLevels.includes(level.toLocaleUpperCase() as LogLevel)) {
return level.toLocaleUpperCase() as LogLevel
}
return 'info'
}

log(level: string, message: string, ...args: any[]) {
const logLevel = this.coerceLevel(level)
if (!this.shouldLog(logLevel)) {
return
}
const consoleFn = console[logLevel]
consoleFn(`[${logLevel.toUpperCase()}] ${message}`, ...args)
}

debug(message: string, ...args: any[]) {
this.log('debug', message, ...args)
}

info(message: string, ...args: any[]) {
this.log('info', message, ...args)
}

warn(message: string, ...args: any[]) {
this.log('warn', message, ...args)
}

error(message: string, ...args: any[]) {
this.log('error', message, ...args)
}

private shouldLog(
messageLevel: LogLevel,
): messageLevel is Exclude<LogLevel, 'none'> {
const levels: LogLevel[] = ['debug', 'info', 'warn', 'error', 'none']
const messageLevelIndex = levels.indexOf(messageLevel)
const currentLevelIndex = levels.indexOf(this.level)
return (
currentLevelIndex <= messageLevelIndex &&
this.level !== 'none' &&
messageLevel !== 'none'
)
}
}

export const logger = new Logger()
35 changes: 33 additions & 2 deletions packages/core-web/src/worker/WorkerClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
StreamError,
StreamResult,
} from '../types/wallet'
import { logger } from '../utils/logger'

// Handles communication with the wasm worker
// TODO: Move rpc stream management to a separate "SubscriptionManager" class
Expand All @@ -21,6 +22,8 @@ export class WorkerClient {
})
this.worker.onmessage = this.handleWorkerMessage.bind(this)
this.worker.onerror = this.handleWorkerError.bind(this)
logger.info('WorkerClient instantiated')
logger.debug('WorkerClient', this.worker)
}

// Idempotent setup - Loads the wasm module
Expand All @@ -30,16 +33,31 @@ export class WorkerClient {
return this.initPromise
}

private handleWorkerLogs(event: MessageEvent) {
const { type, level, message, ...data } = event.data
logger.log(level, message, ...data)
}

private handleWorkerError(event: ErrorEvent) {
console.error('Worker error', JSON.stringify(event))
logger.error('Worker error', event)
}

private handleWorkerMessage(event: MessageEvent) {
const { type, requestId, ...data } = event.data
if (type === 'log') {
this.handleWorkerLogs(event.data)
}
const streamCallback = this.requestCallbacks.get(requestId)
// TODO: Handle errors... maybe have another callbacks list for errors?
logger.debug('WorkerClient - handleWorkerMessage', event.data)
if (streamCallback) {
streamCallback(data) // {data: something} OR {error: something}
} else {
logger.warn(
'WorkerClient - handleWorkerMessage - received message with no callback',
requestId,
event.data,
)
}
}

Expand All @@ -50,10 +68,22 @@ export class WorkerClient {
sendSingleMessage(type: string, payload?: any): Promise<any> {
return new Promise((resolve, reject) => {
const requestId = ++this.requestCounter
logger.debug('WorkerClient - sendSingleMessage', requestId, type, payload)
this.requestCallbacks.set(requestId, (response) => {
this.requestCallbacks.delete(requestId)
logger.debug(
'WorkerClient - sendSingleMessage - response',
requestId,
response,
)
if (response.data) resolve(response.data)
else if (response.error) reject(response.error)
else
logger.warn(
'WorkerClient - sendSingleMessage - malformed response',
requestId,
response,
)
})
this.worker.postMessage({ type, payload, requestId })
})
Expand Down Expand Up @@ -95,7 +125,7 @@ export class WorkerClient {
onEnd: () => void = () => {},
): CancelFunction {
const requestId = ++this.requestCounter

logger.debug('WorkerClient - rpcStream', requestId, module, method, body)
let unsubscribe: (value: void) => void = () => {}
let isSubscribed = false

Expand Down Expand Up @@ -177,6 +207,7 @@ export class WorkerClient {
method: string,
body: JSONValue,
): Promise<Response> {
logger.debug('WorkerClient - rpcSingle', module, method, body)
return new Promise((resolve, reject) => {
this.rpcStream<Response>(module, method, body, resolve, reject)
})
Expand Down
119 changes: 63 additions & 56 deletions packages/core-web/src/worker/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,74 +12,81 @@ const handleFree = (requestId) => {
streamCancelMap.delete(requestId)
}

console.log('Worker - init')

self.onmessage = async (event) => {
const { type, payload, requestId } = event.data

if (type === 'init') {
WasmClient = (await import('@fedimint/fedimint-client-wasm')).WasmClient
self.postMessage({ type: 'initialized', data: {}, requestId })
} else if (type === 'open') {
const { clientName } = payload
client = (await WasmClient.open(clientName)) || null
self.postMessage({
type: 'open',
data: { success: !!client },
requestId,
})
} else if (type === 'join') {
const { inviteCode, clientName: joinClientName } = payload
try {
client = await WasmClient.join_federation(joinClientName, inviteCode)
try {
if (type === 'init') {
WasmClient = (await import('@fedimint/fedimint-client-wasm')).WasmClient
self.postMessage({ type: 'initialized', data: {}, requestId })
} else if (type === 'open') {
const { clientName } = payload
client = (await WasmClient.open(clientName)) || null
self.postMessage({
type: 'join',
type: 'open',
data: { success: !!client },
requestId,
})
} catch (e) {
self.postMessage({ type: 'error', error: e.message, requestId })
}
} else if (type === 'rpc') {
const { module, method, body } = payload
console.log('RPC received', module, method, body)
if (!client) {
} else if (type === 'join') {
const { inviteCode, clientName: joinClientName } = payload
try {
client = await WasmClient.join_federation(joinClientName, inviteCode)
self.postMessage({
type: 'join',
data: { success: !!client },
requestId,
})
} catch (e) {
self.postMessage({ type: 'error', error: e.message, requestId })
}
} else if (type === 'rpc') {
const { module, method, body } = payload
console.log('RPC received', module, method, body)
if (!client) {
self.postMessage({
type: 'error',
error: 'WasmClient not initialized',
requestId,
})
return
}
const rpcHandle = await client.rpc(
module,
method,
JSON.stringify(body),
(res) => {
console.log('RPC response', requestId, res)
const data = JSON.parse(res)
self.postMessage({ type: 'rpcResponse', requestId, ...data })

if (data.end !== undefined) {
// Handle stream ending
const handle = streamCancelMap.get(requestId)
handle?.free()
}
},
)
streamCancelMap.set(requestId, rpcHandle)
} else if (type === 'unsubscribe') {
const rpcHandle = streamCancelMap.get(requestId)
if (rpcHandle) {
rpcHandle.cancel()
rpcHandle.free()
streamCancelMap.delete(requestId)
}
} else {
self.postMessage({
type: 'error',
error: 'WasmClient not initialized',
error: 'Unknown message type',
requestId,
})
return
}
const rpcHandle = await client.rpc(
module,
method,
JSON.stringify(body),
(res) => {
console.log('RPC response', requestId, res)
const data = JSON.parse(res)
self.postMessage({ type: 'rpcResponse', requestId, ...data })

if (data.end !== undefined) {
// Handle stream ending
const handle = streamCancelMap.get(requestId)
handle?.free()
}
},
)
streamCancelMap.set(requestId, rpcHandle)
} else if (type === 'unsubscribe') {
const rpcHandle = streamCancelMap.get(requestId)
if (rpcHandle) {
rpcHandle.cancel()
rpcHandle.free()
streamCancelMap.delete(requestId)
}
} else {
self.postMessage({
type: 'error',
error: 'Unknown message type',
requestId,
})
} catch (e) {
console.error('ERROR', e)
self.postMessage({ type: 'error', error: e, requestId })
}
}

self.postMessage({ type: 'init', data: {} })
// self.postMessage({ type: 'init', data: {} })

0 comments on commit 828fbe3

Please sign in to comment.