Repository: hyperlane-xyz/hyperlane-monorepo (language: rust)

Issue:
Script for monitor status and grafana dashboard



Discussion / hints:

Fix patch:
diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts
index 5e0614591f..07b9abfb24 100644
--- a/typescript/infra/scripts/agent-utils.ts
+++ b/typescript/infra/scripts/agent-utils.ts
@@ -200,7 +200,10 @@ export function withOutputFile<T>(args: Argv<T>) {
 }
 
 export function withWarpRouteId<T>(args: Argv<T>) {
-  return args.describe('warpRouteId', 'warp route id').string('warpRouteId');
+  return args
+    .describe('warpRouteId', 'warp route id')
+    .string('warpRouteId')
+    .choices('warpRouteId', Object.values(WarpRouteIds));
 }
 
 export function withWarpRouteIdRequired<T>(args: Argv<T>) {
diff --git a/typescript/infra/scripts/warp-routes/monitor/status.ts b/typescript/infra/scripts/warp-routes/monitor/status.ts
new file mode 100644
index 0000000000..a6908a1fe0
--- /dev/null
+++ b/typescript/infra/scripts/warp-routes/monitor/status.ts
@@ -0,0 +1,106 @@
+import chalk from 'chalk';
+
+import {
+  LogFormat,
+  LogLevel,
+  configureRootLogger,
+  rootLogger,
+} from '@hyperlane-xyz/utils';
+
+import { HelmManager } from '../../../src/utils/helm.js';
+import { WarpRouteMonitorHelmManager } from '../../../src/warp/helm.js';
+import {
+  assertCorrectKubeContext,
+  getArgs,
+  withWarpRouteIdRequired,
+} from '../../agent-utils.js';
+import { getEnvironmentConfig } from '../../core-utils.js';
+
+const orange = chalk.hex('#FFA500');
+const GRAFANA_LINK =
+  'https://abacusworks.grafana.net/d/ddz6ma94rnzswc/warp-routes?orgId=1&var-warp_route_id=';
+const LOG_AMOUNT = 5;
+
+async function main() {
+  configureRootLogger(LogFormat.Pretty, LogLevel.Info);
+  const { environment, warpRouteId } = await withWarpRouteIdRequired(
+    getArgs(),
+  ).parse();
+
+  const config = getEnvironmentConfig(environment);
+  await assertCorrectKubeContext(config);
+
+  try {
+    const podWarpRouteId = `${WarpRouteMonitorHelmManager.getHelmReleaseName(
+      warpRouteId,
+    )}-0`;
+
+    rootLogger.info(chalk.grey.italic(`Fetching pod status...`));
+    const pod = HelmManager.runK8sCommand(
+      'get pod',
+      podWarpRouteId,
+      environment,
+    );
+    rootLogger.info(chalk.green(pod));
+
+    rootLogger.info(chalk.gray.italic(`Fetching latest logs...`));
+    const latestLogs = HelmManager.runK8sCommand(
+      'logs',
+      podWarpRouteId,
+      environment,
+      [`--tail=${LOG_AMOUNT}`],
+    );
+    formatAndPrintLogs(latestLogs);
+
+    rootLogger.info(
+      orange.bold(`Grafana Dashboard Link: ${GRAFANA_LINK}${warpRouteId}`),
+    );
+  } catch (error) {
+    rootLogger.error(error);
+    process.exit(1);
+  }
+}
+
+function formatAndPrintLogs(rawLogs: string) {
+  try {
+    const logs = rawLogs
+      .trim()
+      .split('\n')
+      .map((line) => JSON.parse(line));
+    logs.forEach((log) => {
+      const { time, module, msg, labels, balance, valueUSD } = log;
+      const timestamp = new Date(time).toISOString();
+      const chain = labels?.chain_name || 'Unknown Chain';
+      const token = labels?.token_name || 'Unknown Token';
+      const warpRoute = labels?.warp_route_id || 'Unknown Warp Route';
+      const tokenStandard = labels?.token_standard || 'Unknown Standard';
+      const tokenAddress = labels?.token_address || 'Unknown Token Address';
+      const walletAddress = labels?.wallet_address || 'Unknown Wallet';
+
+      let logMessage =
+        chalk.gray(`[${timestamp}] `) + chalk.white(`[${module}] `);
+      logMessage += chalk.blue(`${warpRoute} `);
+      logMessage += chalk.green(`${chain} `);
+      logMessage += chalk.blue.italic(`Token: ${token} (${tokenAddress}) `);
+      logMessage += chalk.green.italic(`${tokenStandard} `);
+      logMessage += chalk.blue.italic(`Wallet: ${walletAddress} `);
+
+      if (balance) {
+        logMessage += chalk.yellow.italic(`Balance: ${balance} `);
+      }
+      if (valueUSD) {
+        logMessage += chalk.green.italic(`Value (USD): ${valueUSD} `);
+      }
+      logMessage += chalk.white(`→ ${msg}\n`);
+
+      rootLogger.info(logMessage);
+    });
+  } catch (error) {
+    rootLogger.error(`Failed to parse logs: ${error}`);
+  }
+}
+
+main().catch((err) => {
+  rootLogger.error('Error in main:', err);
+  process.exit(1);
+});
diff --git a/typescript/infra/src/utils/helm.ts b/typescript/infra/src/utils/helm.ts
index 4bd23f1047..4d18a1fc43 100644
--- a/typescript/infra/src/utils/helm.ts
+++ b/typescript/infra/src/utils/helm.ts
@@ -1,3 +1,5 @@
+import { execSync } from 'child_process';
+
 import { DockerConfig } from '../config/agent/agent.js';
 import {
   HelmChartConfig,
@@ -211,4 +213,19 @@ export abstract class HelmManager<T = HelmValues> {
     // Split on new lines and remove empty strings
     return output.split('\n').filter(Boolean);
   }
+
+  static runK8sCommand(
+    command: string,
+    podId: string,
+    namespace: string,
+    args: string[] = [],
+  ) {
+    const argsString = args.join(' ');
+    return execSync(
+      `kubectl ${command} ${podId} -n ${namespace} ${argsString}`,
+      {
+        encoding: 'utf-8',
+      },
+    );
+  }
 }


Test patch: