All files logger.js

64.51% Statements 40/62
48.97% Branches 24/49
80% Functions 12/15
69.81% Lines 37/53

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119        3x     3x 3x   3x 3x                                   22x                 22x     22x 22x           22x 22x 22x 33x 33x 33x   22x       22x         3x 3x       3x     3x         3x 3x     3x 3x   3x       3x               28x 22x   22x   22x   28x       16x   2x 20x 6x           3x  
// Zero-dependency logger with level/namespace filtering for ACP adapter
// stdout is reserved for ACP protocol stream; all logs go to stderr or file
import { createWriteStream } from 'fs';
 
const LEVELS = { error: 0, warn: 1, info: 2, debug: 3, trace: 4 };
 
function parseNamespaces(str) {
  const include = [];
  const exclude = [];
 
  Eif (!str || str === '*') {
    return { include: ['*'], exclude: [] };
  }
 
  for (const part of str
    .split(',')
    .map((s) => s.trim())
    .filter(Boolean)) {
    if (part.startsWith('-')) {
      exclude.push(part.slice(1));
    } else {
      include.push(part);
    }
  }
 
  return { include: include.length ? include : ['*'], exclude };
}
 
function matchPattern(ns, pattern) {
  Eif (pattern === '*') return true;
  if (pattern.endsWith(':*')) {
    const prefix = pattern.slice(0, -1);
    return ns === prefix.slice(0, -1) || ns.startsWith(prefix);
  }
  return ns === pattern;
}
 
function matchNamespace(ns, patterns) {
  for (const excl of patterns.exclude) {
    if (matchPattern(ns, excl)) return false;
  }
  for (const incl of patterns.include) {
    Eif (matchPattern(ns, incl)) return true;
  }
  return false;
}
 
function formatContext(ctx) {
  Iif (!ctx || typeof ctx !== 'object') return '';
  const parts = [];
  for (const [k, v] of Object.entries(ctx)) {
    Iif (v === undefined) continue;
    const val = typeof v === 'object' ? JSON.stringify(v) : String(v);
    parts.push(`${k}=${val}`);
  }
  return parts.length ? ' ' + parts.join(' ') : '';
}
 
function formatText(ts, level, ns, msg, ctx) {
  return `${ts} [${level}] [${ns}] ${msg}${formatContext(ctx)}`;
}
 
// Determine effective log level
function resolveLevel() {
  const envLevel = process.env.AMP_ACP_LOG_LEVEL?.toLowerCase();
  Iif (envLevel && LEVELS[envLevel] !== undefined) {
    return LEVELS[envLevel];
  }
  // If DEBUG is set, default to debug level
  Iif (process.env.DEBUG) {
    return LEVELS.debug;
  }
  return LEVELS.info;
}
 
// Resolve namespace filter from env vars
function resolveNamespaces() {
  const explicit = process.env.AMP_ACP_LOG_NAMESPACES;
  Iif (explicit) return parseNamespaces(explicit);
 
  // Backward compat: DEBUG env var for namespace filtering
  const debug = process.env.DEBUG;
  Iif (debug) return parseNamespaces(debug);
 
  return parseNamespaces('*');
}
 
// Module-level config (evaluated once at import)
const config = {
  stream: process.env.AMP_ACP_LOG ? createWriteStream(process.env.AMP_ACP_LOG, { flags: 'a' }) : process.stderr,
  level: resolveLevel(),
  format: process.env.AMP_ACP_LOG_FORMAT || 'text',
  namespaces: resolveNamespaces(),
};
 
function log(level, ns, msg, ctx = {}) {
  if (LEVELS[level] > config.level) return;
  Iif (!matchNamespace(ns, config.namespaces)) return;
 
  const ts = new Date().toISOString();
  const output =
    config.format === 'json' ? JSON.stringify({ ts, level, ns, msg, ...ctx }) : formatText(ts, level, ns, msg, ctx);
 
  config.stream.write(output + '\n');
}
 
export function createLogger(namespace) {
  return {
    error: (msg, ctx) => log('error', namespace, msg, ctx),
    warn: (msg, ctx) => log('warn', namespace, msg, ctx),
    info: (msg, ctx) => log('info', namespace, msg, ctx),
    debug: (msg, ctx) => log('debug', namespace, msg, ctx),
    trace: (msg, ctx) => log('trace', namespace, msg, ctx),
  };
}
 
// Root logger for global error handlers
export const rootLog = createLogger('acp:root');