From e3c7d7a91cdb6d0cef86ad502f1264493af0876c Mon Sep 17 00:00:00 2001 From: mayankpande88 Date: Thu, 10 Sep 2026 18:31:24 +0530 Subject: [PATCH] feat(nodejs): make Node.js event loop tracing opt-in, default off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attaching the libuv probes reads the entire ELF symbol table of every Node.js process's binary, per pid. node is a large statically linked binary with V8 embedded, so this is expensive: a customer CPU profile attributed 17.5% of a core to instrumentNodejs -> GetSymbol -> readSymbols. What it buys is a single metric, container_nodejs_event_loop_blocked_time_seconds_total. An org-wide code search returns exactly one hit, the metric's own definition — no dashboard, alert, query or application code consumes it. It is collected and stored (24 series on dev) but never read. ENABLE_NODEJS_TRACING defaults to false, matching ENABLE_DOTNET_TRACING rather than the always-on Python probes. Blast radius is limited to that metric. NodejsStats carries only EventLoopBlockedTime, and the probes attach nothing but uv_io_poll_* and uv_io_cb_*. TLS interception for Node.js processes is unaffected — tls.go has no Node-specific handling and works through the libssl/gotls paths — as is L7 tracing, which reads the syscalls rather than libuv. Checked before nodejsChecked so that turning the flag on and restarting instruments processes that were skipped while it was off. Note this does not remove the symbol-table cost generally: python.go and tls.go take the same uncached per-pid path and remain always-on. --- containers/process.go | 5 +++++ flags/flags.go | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/containers/process.go b/containers/process.go index e9b2c92f..7b3b2b3a 100644 --- a/containers/process.go +++ b/containers/process.go @@ -126,6 +126,11 @@ func (p *Process) instrumentPython(cmdline []byte, tracer *ebpftracer.Tracer) { } func (p *Process) instrumentNodejs(exe string, tracer *ebpftracer.Tracer) { + // Checked before nodejsChecked so enabling the flag on a restart still + // instruments processes that were skipped while it was off. + if !*flags.EnableNodejsTracing { + return + } if p.nodejsChecked { return } diff --git a/flags/flags.go b/flags/flags.go index 9533154f..ad65da0f 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -9,12 +9,18 @@ import ( ) var ( - ListenAddress = kingpin.Flag("listen", "Listen address - ip:port or :port").Default("0.0.0.0:80").Envar("LISTEN").String() - CgroupRoot = kingpin.Flag("cgroupfs-root", "The mount point of the host cgroupfs root").Default("/sys/fs/cgroup").Envar("CGROUPFS_ROOT").String() - DisableLogParsing = kingpin.Flag("disable-log-parsing", "Disable container log parsing").Default("false").Envar("DISABLE_LOG_PARSING").Bool() - DisablePinger = kingpin.Flag("disable-pinger", "Don't ping upstreams").Default("true").Envar("DISABLE_PINGER").Bool() - DisableL7Tracing = kingpin.Flag("disable-l7-tracing", "Disable L7 tracing").Default("false").Envar("DISABLE_L7_TRACING").Bool() - EnableDotNetTracing = kingpin.Flag("enable-dotnet-tracing", "Enable .NET CLR tracing").Default("false").Envar("ENABLE_DOTNET_TRACING").Bool() + ListenAddress = kingpin.Flag("listen", "Listen address - ip:port or :port").Default("0.0.0.0:80").Envar("LISTEN").String() + CgroupRoot = kingpin.Flag("cgroupfs-root", "The mount point of the host cgroupfs root").Default("/sys/fs/cgroup").Envar("CGROUPFS_ROOT").String() + DisableLogParsing = kingpin.Flag("disable-log-parsing", "Disable container log parsing").Default("false").Envar("DISABLE_LOG_PARSING").Bool() + DisablePinger = kingpin.Flag("disable-pinger", "Don't ping upstreams").Default("true").Envar("DISABLE_PINGER").Bool() + DisableL7Tracing = kingpin.Flag("disable-l7-tracing", "Disable L7 tracing").Default("false").Envar("DISABLE_L7_TRACING").Bool() + EnableDotNetTracing = kingpin.Flag("enable-dotnet-tracing", "Enable .NET CLR tracing").Default("false").Envar("ENABLE_DOTNET_TRACING").Bool() + // Off by default: the only thing it produces is + // container_nodejs_event_loop_blocked_time_seconds_total, which nothing + // currently consumes, and attaching the probes reads the whole ELF symbol + // table of every Node.js process's binary. Opt-in matches .NET rather than + // the always-on Python probes. + EnableNodejsTracing = kingpin.Flag("enable-nodejs-tracing", "Enable Node.js event loop tracing (attaches libuv uprobes)").Default("false").Envar("ENABLE_NODEJS_TRACING").Bool() DisableGPUMonitoring = kingpin.Flag("disable-gpu-monitoring", "Disable GPU monitoring (NVML)").Default("false").Envar("DISABLE_GPU_MONITORING").Bool() ContainerAllowlist = kingpin.Flag("container-allowlist", "List of allowed containers (regex patterns)").Envar("CONTAINER_ALLOWLIST").Strings()