Skip to content

Commit 2f15506

Browse files
MoonBoi9001claude
andcommitted
fix(logging): forbid pino reserved keys as per-call log fields
Add an eslint rule across the agent, common and cli packages banning pino's reserved keys (name, level, time, pid, hostname, msg, v) as per-call log fields, which silently emit a duplicate JSON key. It caught two more real collisions (a `name` and a `msg`), fixed here too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KJmMVG736T3xefovirWryK
1 parent a23f4e6 commit 2f15506

5 files changed

Lines changed: 49 additions & 13 deletions

File tree

packages/indexer-agent/.eslintrc.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,18 @@ module.exports = {
22
root: true,
33
parser: '@typescript-eslint/parser',
44
plugins: ['@typescript-eslint'],
5-
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier']
5+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
6+
rules: {
7+
// Pino emits a per-call field whose key matches a logger binding as a SECOND JSON
8+
// key; strict parsers keep only the last. Forbid pino reserved keys as per-call fields.
9+
'no-restricted-syntax': [
10+
'error',
11+
{
12+
selector:
13+
"CallExpression[callee.property.name=/^(trace|debug|info|warn|error|fatal)$/] > ObjectExpression > Property[key.name=/^(name|level|time|pid|hostname|msg|v)$/]",
14+
message:
15+
'Do not pass a pino reserved key (name, level, time, pid, hostname, msg, v) as a per-call log field: it collides with the logger bindings and emits a duplicate JSON key that strict parsers silently drop. Use a distinct key (e.g. subgraphName) or set bindings via logger.child({ ... }).',
16+
},
17+
],
18+
},
619
}

packages/indexer-agent/src/db/cli/umzug.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const sequelize = new Sequelize({
5050
logging: false,
5151
})
5252

53-
logger.debug('Successfully connected to DB', { name: database })
53+
logger.debug('Successfully connected to DB', { database })
5454

5555
export const migrator = new Umzug({
5656
migrations: {

packages/indexer-cli/.eslintrc.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,18 @@ module.exports = {
22
root: false,
33
parser: '@typescript-eslint/parser',
44
plugins: ['@typescript-eslint'],
5-
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier']
5+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
6+
rules: {
7+
// Pino emits a per-call field whose key matches a logger binding as a SECOND JSON
8+
// key; strict parsers keep only the last. Forbid pino reserved keys as per-call fields.
9+
'no-restricted-syntax': [
10+
'error',
11+
{
12+
selector:
13+
"CallExpression[callee.property.name=/^(trace|debug|info|warn|error|fatal)$/] > ObjectExpression > Property[key.name=/^(name|level|time|pid|hostname|msg|v)$/]",
14+
message:
15+
'Do not pass a pino reserved key (name, level, time, pid, hostname, msg, v) as a per-call log field: it collides with the logger bindings and emits a duplicate JSON key that strict parsers silently drop. Use a distinct key (e.g. subgraphName) or set bindings via logger.child({ ... }).',
16+
},
17+
],
18+
},
619
}

packages/indexer-common/.eslintrc.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,18 @@ module.exports = {
22
root: false,
33
parser: '@typescript-eslint/parser',
44
plugins: ['@typescript-eslint'],
5-
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier']
5+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
6+
rules: {
7+
// Pino emits a per-call field whose key matches a logger binding as a SECOND JSON
8+
// key; strict parsers keep only the last. Forbid pino reserved keys as per-call fields.
9+
'no-restricted-syntax': [
10+
'error',
11+
{
12+
selector:
13+
"CallExpression[callee.property.name=/^(trace|debug|info|warn|error|fatal)$/] > ObjectExpression > Property[key.name=/^(name|level|time|pid|hostname|msg|v)$/]",
14+
message:
15+
'Do not pass a pino reserved key (name, level, time, pid, hostname, msg, v) as a per-call log field: it collides with the logger bindings and emits a duplicate JSON key that strict parsers silently drop. Use a distinct key (e.g. subgraphName) or set bindings via logger.child({ ... }).',
16+
},
17+
],
18+
},
619
}

packages/indexer-common/src/indexer-management/monitor.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,8 @@ export class NetworkMonitor {
164164
* @returns network `alias` if the network is supported, `null` otherwise
165165
*/
166166
async allocationNetworkAlias(allocation: Allocation): Promise<string | null> {
167-
// TODO:
168-
// resolveChainId will throw an Error when we can't resolve the chainId in
169-
// the future, let's get this from the epoch subgraph (perhaps at startup)
170-
// and then resolve it here.
167+
// TODO: resolveChainId will throw when we can't resolve the chainId; in the future
168+
// get this from the epoch subgraph (perhaps at startup) and resolve it here.
171169
try {
172170
const { network: allocationNetworkAlias } = await this.graphNode.subgraphFeatures(
173171
allocation.subgraphDeployment.id,
@@ -947,7 +945,7 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n
947945
} else {
948946
this.logger.error(`Failed to query latest epoch number`, {
949947
err,
950-
msg: err.message,
948+
errorMessage: err.message,
951949
networkID,
952950
networkAlias,
953951
})
@@ -1388,10 +1386,9 @@ Please submit an issue at https://github.com/graphprotocol/block-oracle/issues/n
13881386
return [hexlify(new Uint8Array(32).fill(0)), 0]
13891387
}
13901388

1391-
// poi = undefined, force=true -- submit even if poi is 0x0
1392-
// poi = defined, force=true -- no generatedPOI needed, just submit the POI supplied (with some sanitation?)
1393-
// poi = undefined, force=false -- submit with generated POI if one available
1394-
// poi = defined, force=false -- submit user defined POI only if generated POI matches
1389+
// force=true: poi undefined -> submit even if 0x0; poi defined -> submit the supplied POI
1390+
// force=false: poi undefined -> submit a generated POI if available; poi defined -> submit the
1391+
// user POI only if it matches the generated POI
13951392
switch (force) {
13961393
case true:
13971394
switch (!!poi) {

0 commit comments

Comments
 (0)