Skip to content

Feat/logtail pino #427

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

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion relayer-cli/.env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ TRANSACTION_BATCHER_CONTRACT_SEPOLIA=0xe7953da7751063d0a41ba727c32c762d3523ade8
TRANSACTION_BATCHER_CONTRACT_CHIADO=0xcC0a08D4BCC5f91ee9a1587608f7a2975EA75d73

# Ensure the path ends with a trailing slash ("/")
STATE_DIR="/home/user/vea/relayer-cli/state/"
STATE_DIR="/home/user/vea/relayer-cli/state/"

# optional for monitoring
LOGTAIL_TOKEN=
HEARTBEAT_URL=
3 changes: 3 additions & 0 deletions relayer-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
},
"dependencies": {
"@kleros/vea-contracts": "workspace:^",
"@logtail/pino": "^0.5.5",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.4.5",
"pino": "^9.7.0",
"pm2": "^6.0.5",
"typescript": "^4.9.5"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"pino-pretty": "^13.0.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2"
}
Expand Down
61 changes: 60 additions & 1 deletion relayer-cli/src/relayer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require("dotenv").config();
import { EventEmitter } from "node:events";
import { ethers } from "ethers";
import https from "https";
import { relayBatch, relayAllFrom } from "./utils/relay";
import {
initialize as initializeNonce,
Expand All @@ -11,7 +12,7 @@ import {
getNetworkConfig,
RelayerNetworkConfig,
} from "./utils/relayerHelpers";
import { initialize as initializeEmitter } from "./utils/logger";
import { initialize as initializeEmitter, logger } from "./utils/logger";
import { BotEvents } from "./utils/botEvents";
import { getEpochPeriod, Network } from "./consts/bridgeRoutes";

Expand All @@ -21,6 +22,53 @@ interface RelayerConfig {
emitter: EventEmitter;
}

/**
* Sends a heartbeat signal to the monitoring service
*/
const sendHeartbeat = async (): Promise<void> => {
const HEARTBEAT_URL = process.env.HEARTBEAT_URL;
if (!HEARTBEAT_URL) {
return;
}
try {
const url = new URL(HEARTBEAT_URL);
const options = {
hostname: url.hostname,
port: url.port || 443,
path: url.pathname,
method: "GET",
headers: {
"User-Agent": "Vea-Relayer-CLI/1.0",
},
};

return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = "";
res.on("data", (chunk) => {
data += chunk;
});
res.on("end", () => {
resolve();
});
});

req.on("error", (error) => {
reject(error);
});

req.setTimeout(10000, () => {
req.destroy();
reject(new Error("Heartbeat request timeout"));
});

req.end();
});
} catch (error) {
// Silently fail - heartbeat errors shouldn't affect the main relayer operation
}
};

/**
* Start the relayer
* @param config.networkConfigs The network configurations retrieved from the env.
Expand All @@ -29,14 +77,25 @@ interface RelayerConfig {
*/
export async function start({ networkConfigs, shutdownManager, emitter }: RelayerConfig) {
initializeEmitter(emitter);

// Send startup heartbeat
await sendHeartbeat().catch(() => {});

let delayAmount = 7200 * 1000; // 2 hours in ms
while (!shutdownManager.getIsShuttingDown()) {
for (const networkConfig of networkConfigs) {
delayAmount = await processNetworkConfig(networkConfig, shutdownManager, emitter, delayAmount);
}
emitter.emit(BotEvents.WAITING, delayAmount);

// Send heartbeat before waiting
await sendHeartbeat().catch(() => {});

await delay(delayAmount);
}

// Send shutdown heartbeat
await sendHeartbeat().catch(() => {});
}

/**
Expand Down
13 changes: 13 additions & 0 deletions relayer-cli/src/utils/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const env = {
require: (key: string): string => {
const value = process.env[key];
if (!value) {
throw new Error(`Required environment variable ${key} is not set`);
}
return value;
},

optional: (key: string, defaultValue: string): string => {
return process.env[key] || defaultValue;
},
};
51 changes: 38 additions & 13 deletions relayer-cli/src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { EventEmitter } from "node:events";
import { BotEvents } from "./botEvents";
import { env } from "./env";
import pino from "pino";

/**
* Listens to relevant events of an EventEmitter instance and issues log lines
Expand All @@ -12,51 +14,74 @@ import { BotEvents } from "./botEvents";
* initialize(emitter);
*/

const logtailToken = process.env.LOGTAIL_TOKEN;

const loggerOptions = {
transport: logtailToken
? {
target: "@logtail/pino",
options: {
sourceToken: logtailToken,
},
}
: {
target: "pino-pretty",
options: {
colorize: true,
translateTime: "SYS:standard",
ignore: "pid,hostname",
},
},
level: env.optional("LOG_LEVEL", "info"),
};

export const logger = pino(loggerOptions);

export const initialize = (emitter: EventEmitter) => {
return configurableInitialize(emitter);
return configurableInitialize(emitter, logger);
};

export const configurableInitialize = (emitter: EventEmitter) => {
export const configurableInitialize = (emitter: EventEmitter, logger: pino.Logger) => {
// Relayer state logs
emitter.on(BotEvents.STARTED, (chainId, network) => {
console.log(`Relayer started for ${chainId} on ${network}`);
logger.info(`Relayer started for ${chainId} on ${network}`);
});
emitter.on(BotEvents.WAITING, (delayAmount) => {
console.log(`Waiting for next epoch: ${delayAmount} ms`);
logger.info(`Waiting for next epoch: ${delayAmount} ms`);
});
emitter.on(BotEvents.EXIT, () => {
console.log("Exiting");
logger.info("Exiting");
});

// Bot health logs
emitter.on(BotEvents.EXCEPTION, (err) => {
console.error("Uncaught Exception occurred", err);
logger.error("Uncaught Exception occurred", err);
});
emitter.on(BotEvents.PROMISE_REJECTION, (reason, promise) => {
console.error("Unhandled promise rejection:", reason, "at", promise);
logger.error("Unhandled promise rejection:", reason, "at", promise);
});

// Lock file logs
emitter.on(BotEvents.LOCK_CLAIMED, () => {
console.log("Lock claimed");
logger.info("Lock claimed");
});
emitter.on(BotEvents.LOCK_DIRECTORY, (pwd) => {
console.log(`Lock file directory: ${pwd}`);
logger.info(`Lock file directory: ${pwd}`);
});
emitter.on(BotEvents.LOCK_RELEASED, () => {
console.log("Lock released");
logger.info("Lock released");
});

// Message relay logs
emitter.on(BotEvents.RELAY_BATCH, (nonce, tx) => {
console.log(`Relaying batch till nonce ${nonce}: ${tx}`);
logger.info(`Relaying batch till nonce ${nonce}: ${tx}`);
});

emitter.on(BotEvents.RELAY_ALL_FROM, (nonce, msgSenders, tx) => {
console.log(`Relaying all messages from ${msgSenders} with nonce ${nonce}: ${tx}`);
logger.info(`Relaying all messages from ${msgSenders} with nonce ${nonce}: ${tx}`);
});

emitter.on(BotEvents.MESSAGE_EXECUTION_FAILED, (nonce) => {
console.error(`Message execution failed for nonce ${nonce}`);
logger.error(`Message execution failed for nonce ${nonce}`);
});
};
3 changes: 3 additions & 0 deletions validator-cli/.env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ VEAOUTBOX_SUBGRAPH_CHIADO=https://api.studio.thegraph.com/query/user/outbox-arb-

GNOSIS_WETH=0x8d74e5e4DA11629537C4575cB0f33b4F0Dfa42EB

# optional for monitoring
LOGTAIL_TOKEN=
HEARTBEAT_URL=
3 changes: 3 additions & 0 deletions validator-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
"@arbitrum/sdk": "4.0.3",
"@flashbots/ethers-provider-bundle": "^0.6.2",
"@kleros/vea-contracts": "workspace:^",
"@logtail/pino": "^0.5.5",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.4.5",
"pino": "^9.7.0",
"pm2": "^6.0.5",
"typescript": "^4.9.5"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"jest": "^29.7.0",
"pino-pretty": "^13.0.0",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2"
}
Expand Down
13 changes: 13 additions & 0 deletions validator-cli/src/utils/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const env = {
require: (key: string): string => {
const value = process.env[key];
if (!value) {
throw new Error(`Required environment variable ${key} is not set`);
}
return value;
},

optional: (key: string, defaultValue: string): string => {
return process.env[key] || defaultValue;
},
};
Loading
Loading