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

Draft
wants to merge 3 commits into
base: dev
Choose a base branch
from
Draft

Feat/logtail pino #427

wants to merge 3 commits into from

Conversation

mani99brar
Copy link
Contributor

@mani99brar mani99brar commented Jun 18, 2025

PR-Codex overview

This PR introduces monitoring capabilities to the validator-cli and relayer-cli projects by adding heartbeat signals and logging features. It enhances environment variable management and updates dependencies to support the new functionality.

Detailed summary

  • Added LOGTAIL_TOKEN and HEARTBEAT_URL to .env.dist files for monitoring.
  • Introduced sendHeartbeat function in relayer-cli/src/watcher.ts and relayer-cli/src/relayer.ts for sending heartbeat signals.
  • Implemented heartbeat calls during startup, processing, and shutdown in both watch and start functions.
  • Enhanced logging in relayer-cli/src/utils/logger.ts and validator-cli/src/utils/logger.ts to use pino for structured logging.
  • Updated package.json files to include new dependencies: @logtail/pino, pino, and pino-pretty.
  • Added env utility for managing environment variables in both relayer-cli/src/utils/env.ts and validator-cli/src/utils/env.ts.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Added support for sending heartbeat signals to a monitoring service, improving observability of relayer and validator operations.
    • Introduced optional environment variables for monitoring and logging integration (LOGTAIL_TOKEN, HEARTBEAT_URL).
  • Refactor

    • Replaced direct console logging with structured logging using the pino library, with optional Logtail integration for advanced log management.
    • Centralized environment variable handling for improved configuration management.
  • Chores

    • Updated dependencies to include logging and monitoring packages.

Copy link
Contributor

coderabbitai bot commented Jun 18, 2025

Walkthrough

The changes introduce structured logging and heartbeat monitoring to both the relayer and validator CLI tools. New environment variables (LOGTAIL_TOKEN, HEARTBEAT_URL) are added for external logging and monitoring integration. Logging is refactored to use the pino library, optionally with Logtail transport, and heartbeat signals are sent periodically via HTTPS requests.

Changes

File(s) Change Summary
relayer-cli/.env.dist, validator-cli/.env.dist Added LOGTAIL_TOKEN and HEARTBEAT_URL environment variables for monitoring/logging.
relayer-cli/package.json, validator-cli/package.json Added dependencies: @logtail/pino, pino, and devDependency pino-pretty.
relayer-cli/src/utils/env.ts, validator-cli/src/utils/env.ts Introduced env utility with require and optional methods for environment variable access.
relayer-cli/src/utils/logger.ts, validator-cli/src/utils/logger.ts Refactored logging to use pino logger, with Logtail integration if configured; replaced direct console logging.
relayer-cli/src/relayer.ts Added sendHeartbeat function; integrated heartbeat calls at startup, periodically, and on shutdown.
validator-cli/src/watcher.ts Added sendHeartbeat function; integrated heartbeat calls at startup, before each network process, and shutdown.

Sequence Diagram(s)

Heartbeat Integration (Relayer/Validator)

sequenceDiagram
    participant CLI
    participant Env
    participant Logger
    participant MonitoringService

    CLI->>Env: Load HEARTBEAT_URL
    CLI->>Logger: Initialize logger (with LOGTAIL_TOKEN if set)
    CLI->>MonitoringService: sendHeartbeat() at startup
    loop Main Loop
        CLI->>MonitoringService: sendHeartbeat() periodically
        CLI->>Logger: Log events
    end
    CLI->>MonitoringService: sendHeartbeat() at shutdown
Loading

Poem

Hopping through logs, a bunny does cheer,
With pino and Logtail, the messages are clear.
Heartbeats are sent, a thump in the night,
Monitoring and logging, both shining bright.
With every new hop, this code feels just right!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @coderabbitai title anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Jun 18, 2025

Deploy Preview for veascan ready!

Name Link
🔨 Latest commit 68d799d
🔍 Latest deploy log https://app.netlify.com/projects/veascan/deploys/6852a0730add9a00080d6d17
😎 Deploy Preview https://deploy-preview-427--veascan.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
55.4% Duplication on New Code (required ≤ 10%)

See analysis details on SonarQube Cloud

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (2)
validator-cli/src/utils/env.ts (1)

2-8: Keep logic consistent with relayer fix

If you accept the tightened value === undefined check in the relayer utility, mirror it here to prevent subtle behavioural differences between the two CLIs.

relayer-cli/src/utils/logger.ts (1)

17-36: Same dependency / env handling concerns

Replicate the fixes applied to the validator logger:

  • Move pino-pretty to runtime deps.
  • Fetch LOGTAIL_TOKEN via the env helper for consistency.
🧹 Nitpick comments (6)
relayer-cli/src/utils/env.ts (2)

2-8: Empty string is treated as “missing” – is that intentional?

if (!value) rejects an env var that is set but intentionally blank (e.g. VAR=""). If you only want to fail when the key is truly undefined, check value === undefined instead.

-    if (!value) {
+    if (value === undefined) {

1-13: Avoid copy-pasted utilities across packages

An identical env helper is introduced in two different packages. Duplicating code increases maintenance cost and the risk of divergence. Export a small shared package (e.g. @kleros/vea-env) or move the helper to the monorepo root and import it from both CLI tools.

validator-cli/.env.dist (1)

24-26: Order keys & keep sample files tidy

The linter warning is legit: place HEARTBEAT_URL before LOGTAIL_TOKEN to keep alphabetical order and minimise merge noise.

-LOGTAIL_TOKEN=
-HEARTBEAT_URL=
+HEARTBEAT_URL=
+LOGTAIL_TOKEN=
relayer-cli/.env.dist (1)

26-28: Alphabetical order & trailing newline

  1. Alphabetise: HEARTBEAT_URL should precede LOGTAIL_TOKEN.
  2. Add a trailing blank line to satisfy common linters and POSIX tools.
-LOGTAIL_TOKEN=
-HEARTBEAT_URL=
+HEARTBEAT_URL=
+LOGTAIL_TOKEN=
+
validator-cli/src/watcher.ts (1)

88-96: Tight loop may overload monitoring endpoint

sendHeartbeat() is called every loop iteration (≈ 10 s) per watched network.
If multiple networks are configured the frequency scales linearly and could breach rate-limits.
Consider extracting a single setInterval-based heartbeat or using a token-bucket.

validator-cli/src/utils/logger.ts (1)

19-40: Runtime dependency & env util consistency

  • pino-pretty is loaded via transport – ensure it’s under "dependencies" (see package.json comment).
  • Prefer env.optional("LOGTAIL_TOKEN") instead of reading process.env directly to keep all env look-ups centralised.
-const logtailToken = process.env.LOGTAIL_TOKEN;
+const logtailToken = env.optional("LOGTAIL_TOKEN");
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd7f009 and 68d799d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (10)
  • relayer-cli/.env.dist (1 hunks)
  • relayer-cli/package.json (1 hunks)
  • relayer-cli/src/relayer.ts (4 hunks)
  • relayer-cli/src/utils/env.ts (1 hunks)
  • relayer-cli/src/utils/logger.ts (2 hunks)
  • validator-cli/.env.dist (1 hunks)
  • validator-cli/package.json (1 hunks)
  • validator-cli/src/utils/env.ts (1 hunks)
  • validator-cli/src/utils/logger.ts (3 hunks)
  • validator-cli/src/watcher.ts (3 hunks)
🧰 Additional context used
🪛 dotenv-linter (3.3.0)
relayer-cli/.env.dist

[warning] 24-24: [QuoteCharacter] The value has quote characters (', ")


[warning] 28-28: [EndingBlankLine] No blank line at the end of the file


[warning] 28-28: [UnorderedKey] The HEARTBEAT_URL key should go before the LOGTAIL_TOKEN key

validator-cli/.env.dist

[warning] 26-26: [UnorderedKey] The HEARTBEAT_URL key should go before the LOGTAIL_TOKEN key

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Redirect rules - veascan
  • GitHub Check: Header rules - veascan
  • GitHub Check: Pages changed - veascan
  • GitHub Check: Analyze (javascript)
  • GitHub Check: Scorecard analysis
  • GitHub Check: test
  • GitHub Check: dependency-review

Comment on lines +17 to 21
"@logtail/pino": "^0.5.5",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.4.5",
"pino": "^9.7.0",
"pm2": "^6.0.5",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Pin critical runtime deps instead of using caret ranges

@logtail/pino and pino are pulled in at ^ ranges. Both libraries publish frequent breaking‐change majors; letting npm float to the next major on every fresh install can silently break the CLI in production. Consider hard-pinning to the exact, tested versions and bump them deliberately in a dedicated PR.

-    "@logtail/pino": "^0.5.5",
+    "@logtail/pino": "0.5.5",
 ...
-    "pino": "^9.7.0",
+    "pino": "9.7.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@logtail/pino": "^0.5.5",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.4.5",
"pino": "^9.7.0",
"pm2": "^6.0.5",
"@logtail/pino": "0.5.5",
"@typechain/ethers-v6": "^0.5.1",
"dotenv": "^16.4.5",
"pino": "9.7.0",
"pm2": "^6.0.5",
🤖 Prompt for AI Agents
In relayer-cli/package.json around lines 17 to 21, the dependencies
@logtail/pino and pino use caret (^) version ranges, which can cause automatic
upgrades to breaking major versions. Change these version specifications to
exact versions by removing the caret prefix, pinning them to the currently
tested versions to prevent unintended breaking changes during installs. Update
these versions deliberately in future PRs when needed.

STATE_DIR="/home/user/vea/relayer-cli/state/"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid quoting values in sample .env

The quotes around STATE_DIR will be part of the variable value when parsed by dotenv, which is rarely what you want.

-STATE_DIR="/home/user/vea/relayer-cli/state/"
+STATE_DIR=/home/user/vea/relayer-cli/state/
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
STATE_DIR="/home/user/vea/relayer-cli/state/"
STATE_DIR=/home/user/vea/relayer-cli/state/
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 24-24: [QuoteCharacter] The value has quote characters (', ")

🤖 Prompt for AI Agents
In relayer-cli/.env.dist at line 24, remove the double quotes around the
STATE_DIR value so that the path is assigned without quotes. This prevents the
quotes from being included in the environment variable value when parsed by
dotenv.

Comment on lines 26 to 32
"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"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

pino-pretty must be a runtime dependency

pino-pretty is resolved at runtime via the transport declared in logger.ts. Packaging it under devDependencies means it will be absent in production installs (yarn --prod, Docker multi-stage builds, etc.) and the app will throw on bootstrap (ERR_MODULE_NOT_FOUND: pino-pretty).
Move it to dependencies.

   "devDependencies": {
-    "pino-pretty": "^13.0.0",
+  },
+  "dependencies": {
+    "pino-pretty": "^13.0.0",

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In validator-cli/package.json around lines 26 to 32, move "pino-pretty" from
"devDependencies" to "dependencies" because it is required at runtime by the
transport in logger.ts. This ensures it is installed in production environments
and prevents runtime module not found errors.

Comment on lines +24 to +66
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-Validator-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
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Heartbeat promise may crash the bot

Inside sendHeartbeat you wrap the sync code in a try/catch, but the rejections coming from the returned Promise are not caught, e.g.:

  • network error (req.on("error"))
  • timeout (setTimeout branch)

These propagate to await sendHeartbeat() and can terminate the whole watcher with an unhandled-rejection.

-  await sendHeartbeat();
+  await sendHeartbeat().catch(() => {
+    // best-effort – ignore heartbeat failures
+  });

Additionally, options.path omits query-strings. Use url.pathname + url.search.
Silently fixing both avoids unexpected downtime.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In validator-cli/src/watcher.ts lines 24 to 66, the sendHeartbeat function's
returned Promise can reject on network errors or timeouts, but these rejections
are not caught within the function, risking unhandled promise rejections that
can crash the bot. To fix this, handle the Promise rejections inside
sendHeartbeat by adding a catch block or wrapping the call site with try/catch.
Also, update the options.path to include both url.pathname and url.search to
preserve query strings in the heartbeat request URL.

Comment on lines +91 to +93
// Send heartbeat before waiting
await sendHeartbeat();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Await without catch

Same crash vector on periodic heartbeat:

-    await sendHeartbeat();
+    await sendHeartbeat().catch(() => {});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Send heartbeat before waiting
await sendHeartbeat();
// Send heartbeat before waiting
await sendHeartbeat().catch(() => {});
🤖 Prompt for AI Agents
In relayer-cli/src/relayer.ts around lines 91 to 93, the await call to
sendHeartbeat() is not wrapped in a try-catch block, which can cause the process
to crash if sendHeartbeat throws an error. To fix this, wrap the await
sendHeartbeat() call in a try-catch block and handle or log any errors to
prevent unhandled promise rejections and crashes.

Comment on lines +25 to +70
/**
* 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-Validator-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
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same unhandled-rejection issue and duplicated implementation

  1. The Promise returned by sendHeartbeat can reject → potential crash. Apply the same guard as suggested for the validator.
  2. User-Agent claims Vea-Validator-CLI/1.0; copy-paste artefact.
  3. Nearly identical code now exists in two places. Extract to a shared util (heartbeat.ts) to avoid divergence.
-        "User-Agent": "Vea-Validator-CLI/1.0",
+        "User-Agent": "Vea-Relayer-CLI/1.0",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* 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-Validator-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
}
};
/**
* 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
}
};
🤖 Prompt for AI Agents
In relayer-cli/src/relayer.ts between lines 25 and 70, the sendHeartbeat
function returns a Promise that can reject without being caught, risking
unhandled rejections; fix this by adding a catch block or handling the rejection
where sendHeartbeat is called. Also, update the User-Agent header to correctly
reflect the relayer CLI instead of the copied "Vea-Validator-CLI/1.0" string.
Finally, extract this heartbeat sending logic into a shared utility file (e.g.,
heartbeat.ts) to avoid code duplication and ensure maintainability.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant