pipe-to-axiom.js2.8 KBView on GitHub
#!/usr/bin/env node
// @ts-nocheck
/**
 * Pipe console logs to Axiom
 * 
 * This script intercepts stdout/stderr and sends logs to Axiom
 * while also displaying them in the terminal.
 * 
 * Usage: node scripts/pipe-to-axiom.js <dataset-name> <command...>
 * Example: node scripts/pipe-to-axiom.js cedar-local-logs pnpm dev
 */

import { spawn } from 'child_process';
import { Axiom } from '@axiomhq/js';

const DATASET_NAME = process.argv[2];
const COMMAND_ARGS = process.argv.slice(3);

if (!DATASET_NAME || COMMAND_ARGS.length === 0) {
  console.error('Usage: node scripts/pipe-to-axiom.js <dataset-name> <command...>');
  console.error('Example: node scripts/pipe-to-axiom.js cedar-local-logs pnpm dev');
  process.exit(1);
}

// Initialize Axiom client
const axiom = new Axiom({
  token=[redacted] || '',
  orgId: 'cedar-gcuj',
});

function pipeToAxiom(line) {
  if (!line.trim()) return;

  const event = {
    message: line.trim(),
    _time: new Date().toISOString(),
  };

  // fire-and-forget, non-blocking (batching API returns void)
  try {
    axiom.ingest(DATASET_NAME, [event]);
  } catch {
    // Silently fail - don't break the main process
  }
}

// Join all command args to handle complex commands like "bash -c '...'"
const fullCommand = COMMAND_ARGS.join(' ');

// Buffer for incomplete lines
let stdoutBuffer = '';
let stderrBuffer = '';

// Spawn the process using shell to handle complex commands
const child = spawn(fullCommand, [], {
  stdio: ['inherit', 'pipe', 'pipe'],
  shell: true,
  env: process.env, // Explicitly pass environment variables
});

// Helper to process buffered lines
function processLines(buffer, newData) {
  const text = buffer + newData;
  const lines = text.split('\n');
  
  // Keep the last line in buffer if it doesn't end with newline
  const completeLines = text.endsWith('\n') ? lines : lines.slice(0, -1);
  const remainingBuffer = text.endsWith('\n') ? '' : lines[lines.length - 1];
  
  // Send complete lines to Axiom
  for (const line of completeLines) {
    if (line.trim()) {
      pipeToAxiom(line);
    }
  }
  
  return remainingBuffer;
}

// Pipe stdout
child.stdout.on('data', (data) => {
  const text = data.toString();
  process.stdout.write(text); // Show in terminal
  stdoutBuffer = processLines(stdoutBuffer, text);
});

// Pipe stderr
child.stderr.on('data', (data) => {
  const text = data.toString();
  process.stderr.write(text); // Show in terminal
  stderrBuffer = processLines(stderrBuffer, text);
});

// Handle process exit
child.on('exit', (code) => {
  // Send any remaining buffered lines
  if (stdoutBuffer.trim()) {
    pipeToAxiom(stdoutBuffer);
  }
  if (stderrBuffer.trim()) {
    pipeToAxiom(stderrBuffer);
  }
  process.exit(code || 0);
});

child.on('error', (error) => {
  console.error('Failed to start process:', error);
  process.exit(1);
});