-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: log every rpc call across all the viem clients used #85
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice one!
*/ | ||
constructor( | ||
private readonly rpcConfig: ProtocolRpcConfig, | ||
contracts: ProtocolContractsAddresses, | ||
privateKey: Hex, | ||
blockNumberService?: BlockNumberService, | ||
private readonly logger?: ILogger, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any particular reason for logger
to be optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was doing this for not needing to inject for tests and scripts but you're right we should have it mandatory--did the refactor
onFetchRequest: async (request) => { | ||
const clonedRequest = { | ||
url: request.url, | ||
method: request.method, | ||
headers: Array.from(request.headers.entries()), | ||
body: await request.clone().text(), | ||
}; | ||
|
||
this.logger?.debug(`Fetch Request`, clonedRequest); | ||
}, | ||
onFetchResponse: async (response) => { | ||
const clonedResponse = { | ||
url: response.url, | ||
status: response.status, | ||
headers: Array.from(response.headers.entries()), | ||
body: await response.clone().text(), | ||
}; | ||
|
||
this.logger?.debug(`Fetch Response`, clonedResponse); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
Mind sharing an example for a logged read request/response + a logged write request/response? Interested in seeing the body mostly, to know if operators will have enough data for their debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i add a comment on same lines, if logger is undefined
, we can omit doing the clone object (i think the delay is minimum but we have await operations on the clone()
method)
however, this question is related to Yaco's one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@0xyaco I am having some difficulties with running the e2e tests but here are some logs from the unit tests:
@ebo-agent/shared:test: stdout | tests/services/logger.spec.ts > Logger Methods > includes metadata in log output
@ebo-agent/shared:test: 2024-11-07 16:26:37 info: Test message {"key":"value"}
@ebo-agent/shared:test:
@ebo-agent/shared:test: stdout | tests/services/logger.spec.ts > Logger Methods > handles errors with metadata
@ebo-agent/shared:test: 2024-11-07 16:26:37 error: Error: An error occurred
@ebo-agent/shared:test: at /Users/jacob/Documents/Programming/EBO-agent/packages/shared/tests/services/logger.spec.ts:28:23
@ebo-agent/shared:test: at file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:146:14
@ebo-agent/shared:test: at file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:533:11
@ebo-agent/shared:test: at runWithTimeout (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:61:7)
@ebo-agent/shared:test: at runTest (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:986:17)
@ebo-agent/shared:test: at processTicksAndRejections (node:internal/process/task_queues:105:5)
@ebo-agent/shared:test: at runSuite (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:1142:15)
@ebo-agent/shared:test: at runSuite (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:1142:15)
@ebo-agent/shared:test: at runFiles (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:1199:5)
@ebo-agent/shared:test: at startTests (file:///Users/jacob/Documents/Programming/EBO-agent/node_modules/.pnpm/@[email protected]/node_modules/@vitest/runner/dist/index.js:1208:3) {"key":"value"}
note the value at the bottom there ^
also, this is what it would look like theoretically (before the below refactor to the JSON format) if I could get the e2e tests running on this branch lol
2024-11-07 16:45:23 debug: Fetch Request {
"url": "https://l2-node.example.com",
"method": "POST",
"headers": [
["Content-Type", "application/json"]
],
"body": "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getBlockByNumber\",\"params\":[\"latest\",false],\"id\":1}"
}
2024-11-07 16:45:23 debug: Fetch Response {
"url": "https://l2-node.example.com",
"status": 200,
"headers": [
["Content-Type", "application/json"]
],
"body": "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"number\":\"0x10d4f\",\"hash\":\"0x...\",\"...\":\"...\"}}"
}
2024-11-07 16:45:25 debug: Fetch Request {
"url": "https://l2-node.example.com",
"method": "POST",
"headers": [
["Content-Type", "application/json"]
],
"body": "{\"jsonrpc\":\"2.0\",\"method\":\"eth_sendRawTransaction\",\"params\":[\"0xf86c808504a817c80082520894...\"],\"id\":2}"
}
2024-11-07 16:45:25 debug: Fetch Response {
"url": "https://l2-node.example.com",
"status": 200,
"headers": [
["Content-Type", "application/json"]
],
"body": "{\"jsonrpc\":\"2.0\",\"id\":2,\"result\":\"0xtransactionHash...\"}"
}
@0xnigir1 got rid of the clone 👍🏻
const { level, message, timestamp, stack, ...meta } = info; | ||
const metaString = Object.keys(meta).length ? ` ${stringify(meta)}` : ""; | ||
return `${timestamp} ${level}: ${stack ?? message ?? ""}${metaString}`; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome!
Now that we have the possibility of passing some extra metadata while logging, I think we are ready to change the logging format from the current one to a more "tooling-consumable" format (ie JSON):
{
"timestamp": <timestamp>,
"level": <level>,
"message": <message>,
"stack": <stack>,
"data": <stack>
}
With stack
and data
included only if their values are defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
✅ refactored the logger class
@@ -10,50 +12,53 @@ export class Logger implements ILogger { | |||
private logger: WinstonLogger; | |||
private static instance: Logger | null; | |||
private level: LogLevel; | |||
private constructor() { | |||
private constructor(transportsArray?: Transport[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we setting the transportsArray
somewhere while initializing the logger?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually I didn't end up needing this so I've removed it
} | ||
return Logger.instance; | ||
} | ||
isValidLogLevel(level: any): level is LogLevel { | ||
return validLogLevels.includes(level); | ||
} | ||
|
||
info(message: string) { | ||
this.logger.info(message); | ||
info(message: string, meta?: unknown) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice usage of unknown
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice pr 💯
packages/shared/package.json
Outdated
@@ -23,7 +23,8 @@ | |||
"keywords": [], | |||
"author": "", | |||
"dependencies": { | |||
"winston": "3.13.1" | |||
"winston": "3.13.1", | |||
"winston-transport": "^4.8.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
carettt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sigh
onFetchRequest: async (request) => { | ||
const clonedRequest = { | ||
url: request.url, | ||
method: request.method, | ||
headers: Array.from(request.headers.entries()), | ||
body: await request.clone().text(), | ||
}; | ||
|
||
this.logger?.debug(`Fetch Request`, clonedRequest); | ||
}, | ||
onFetchResponse: async (response) => { | ||
const clonedResponse = { | ||
url: response.url, | ||
status: response.status, | ||
headers: Array.from(response.headers.entries()), | ||
body: await response.clone().text(), | ||
}; | ||
|
||
this.logger?.debug(`Fetch Response`, clonedResponse); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i add a comment on same lines, if logger is undefined
, we can omit doing the clone object (i think the delay is minimum but we have await operations on the clone()
method)
however, this question is related to Yaco's one
url: response.url, | ||
status: response.status, | ||
headers: Array.from(response.headers.entries()), | ||
body: await response.clone().text(), | ||
body: await response.text(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jahabeebs won't removing the clone()
call here consume the response stream and cause the response
to be used by our protocol provider later to be empty? 🤔
Asking based on this issue I sent you before: wevm/viem#2244
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm yes you're right, I confused what nigiri mentioned 😅
}), | ||
format((info) => { | ||
const { timestamp, level, message, stack, ...meta } = info; | ||
const logEntry: any = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's define LogEntry
type and change this to avoid using any
:
const logEntry: any = { | |
const logEntry: LogEntry = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added better typing ✅
🤖 Linear
Closes GRT-239
Description
https://viem.sh/docs/clients/transports/http.html#onfetchrequest-optional
https://viem.sh/docs/clients/transports/http.html#onfetchresponse-optional
wevm/viem#2244 (ended up using the clone function suggested here) - https://developer.mozilla.org/en-US/docs/Web/API/Response/clone