-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement sdk log watching and progress on log modal
- Loading branch information
Showing
4 changed files
with
126 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
export const MIN_CHAIN_BALANCE = 1; // 1 Wei | ||
// TODO edit this based on experiments | ||
export const WARP_DEPLOY_GAS_UNITS = BigInt(1e7); | ||
export const REFUND_FEE_PADDING_FACTOR = 1.1; | ||
export const REFUND_FEE_PADDING_FACTOR = 1.2; | ||
export const MIN_DEPLOYER_BALANCE_TO_SHOW = BigInt(1e15); // 0.001 ETH |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Modal, SegmentedControl } from '@hyperlane-xyz/widgets'; | ||
import { pino } from 'pino'; | ||
import { useState } from 'react'; | ||
import { H3 } from '../../components/text/Headers'; | ||
import { LOG_LEVELS, useSdkLogs } from './useSdkLogs'; | ||
|
||
export function LogModal({ isOpen, close }: { isOpen: boolean; close: () => void }) { | ||
const sdkLogs = useSdkLogs(); | ||
const [filterLevel, setFilterLevel] = useState(LOG_LEVELS[1]); | ||
const filterLevelValue = getLevelValue(filterLevel); | ||
|
||
return ( | ||
<Modal isOpen={isOpen} close={close} panelClassname="p-4"> | ||
<H3 className="text-center">Deployment Logs</H3> | ||
<SegmentedControl options={LOG_LEVELS} onChange={(l) => setFilterLevel(l!)} /> | ||
<ul className="list-none space-y-0.5 text-sm"> | ||
{sdkLogs.map(([timestamp, level, message], i) => | ||
getLevelValue(level) >= filterLevelValue ? ( | ||
<li className="nth-child(even):bg-blue-500/5" key={i}> | ||
{new Date(timestamp).toLocaleTimeString()}: {message} | ||
</li> | ||
) : null, | ||
)} | ||
</ul> | ||
</Modal> | ||
); | ||
} | ||
|
||
function getLevelValue(level: string): number { | ||
return pino.levels.values[level] || 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { | ||
configureRootLogger, | ||
getRootLogger, | ||
LogFormat, | ||
LogLevel as LogLevelWithOff, | ||
} from '@hyperlane-xyz/utils'; | ||
import { useInterval } from '@hyperlane-xyz/widgets'; | ||
import { Logger } from 'pino'; | ||
import { useCallback, useEffect, useState } from 'react'; | ||
|
||
export const LOG_LEVELS = Object.values(LogLevelWithOff).filter((l) => l !== LogLevelWithOff.Off); | ||
export type LogLevel = (typeof LOG_LEVELS)[number]; | ||
// Tuple of timestamp, level, message | ||
type Log = [number, LogLevel, string]; | ||
let logBuffer: Array<Log> = []; | ||
|
||
export function useSdkLogWatcher() { | ||
useEffect(() => { | ||
// TODO confirm this line doesn't break the log watching | ||
configureRootLogger(LogFormat.JSON, LogLevelWithOff.Debug); | ||
const onLog = (timestamp: number, level: LogLevel, ...args: any) => { | ||
const message = `${args}`.replaceAll('[object Object],', '').trim(); | ||
logBuffer.push([timestamp, level, message]); | ||
}; | ||
const rootLogger = getRootLogger(); | ||
// NOTE ABOUT PINO: | ||
// Pino sucks. Splitting it's log output to multiple transports doesn't seem | ||
// to be possible. There is a way to specify transports at logger init time | ||
// but it requires the use of worker threads which greatly complicates the | ||
// bundling and runtime requirements for the utils lib. The following two | ||
// method calls hack in wrappers for the log methods to force a call to onLog. | ||
wrapChildMethod(rootLogger, onLog); | ||
wrapLogMethods(rootLogger, onLog); | ||
|
||
return () => { | ||
// Replace global rootLogger with new one | ||
// TODO this may not work since deployer files already got ran and bound to first rootLogger | ||
configureRootLogger(LogFormat.JSON, LogLevelWithOff.Debug); | ||
logBuffer = []; | ||
}; | ||
}, []); | ||
} | ||
|
||
export function useSdkLogs() { | ||
const [logs, setLogs] = useState<Array<Log>>([]); | ||
const syncLogs = useCallback(() => setLogs([...logBuffer]), []); | ||
useInterval(syncLogs, 250); | ||
return logs; | ||
} | ||
|
||
/** | ||
* Add a layer of indirection to the logger's 'child' method | ||
* so that any children created from it have their log methods wrapped. | ||
*/ | ||
function wrapChildMethod( | ||
logger: Logger, | ||
onLog: (timestamp: number, level: LogLevel, ...args: any) => void, | ||
) { | ||
const defaultChild = logger.child.bind(logger); | ||
// @ts-ignore allow spread argument | ||
logger.child = (...args: any) => { | ||
// @ts-ignore allow spread argument | ||
const childLogger = defaultChild(...args); | ||
wrapLogMethods(childLogger, onLog); | ||
return childLogger; | ||
}; | ||
} | ||
|
||
/** | ||
* Add a layer of indirection to the logger's log methods | ||
* so that they trigger the onLog callback each time they're called. | ||
*/ | ||
function wrapLogMethods( | ||
logger: Logger, | ||
onLog: (timestamp: number, level: LogLevel, ...args: any) => void, | ||
) { | ||
for (const level of LOG_LEVELS) { | ||
const defaultMethod = logger[level].bind(logger); | ||
const wrappedMethod = (...args: any) => { | ||
// @ts-ignore allow spread argument | ||
defaultMethod(...args); | ||
onLog(Date.now(), level, ...args); | ||
}; | ||
logger[level] = wrappedMethod; | ||
} | ||
} |