<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: fan “muratfan” wei</title>
    <description>The latest articles on DEV Community by fan “muratfan” wei (@fan_muratfanwei).</description>
    <link>https://dev.to/fan_muratfanwei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4024184%2Fc99e014b-26d2-42ea-8601-10121abcb2be.png</url>
      <title>DEV Community: fan “muratfan” wei</title>
      <link>https://dev.to/fan_muratfanwei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fan_muratfanwei"/>
    <language>en</language>
    <item>
      <title>Monitoring</title>
      <dc:creator>fan “muratfan” wei</dc:creator>
      <pubDate>Fri, 10 Jul 2026 15:05:34 +0000</pubDate>
      <link>https://dev.to/fan_muratfanwei/monitoring-4ebk</link>
      <guid>https://dev.to/fan_muratfanwei/monitoring-4ebk</guid>
      <description></description>
    </item>
    <item>
      <title>The memory spike that left no trace in the logs"</title>
      <dc:creator>fan “muratfan” wei</dc:creator>
      <pubDate>Fri, 10 Jul 2026 14:16:01 +0000</pubDate>
      <link>https://dev.to/fan_muratfanwei/the-memory-spike-that-left-no-trace-in-the-logs-3b2a</link>
      <guid>https://dev.to/fan_muratfanwei/the-memory-spike-that-left-no-trace-in-the-logs-3b2a</guid>
      <description>&lt;h1&gt;
  
  
  canonical_url: leave empty so this dev.to post is the canonical home,
&lt;/h1&gt;

&lt;h1&gt;
  
  
  or point it at your GitHub write-up if you prefer that as the original.
&lt;/h1&gt;




&lt;p&gt;A memory spike showed up on a Grafana dashboard for a small lab I run. I checked the logs. They said nothing happened.&lt;/p&gt;

&lt;p&gt;That "nothing" turned out to be the whole clue.&lt;/p&gt;

&lt;p&gt;This is a short detective story about network observability — and about why "just check the logs" is only half the job. I got the first two guesses wrong, which is the interesting part, so I'll leave the mistakes in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;The lab is four &lt;strong&gt;Arista cEOS&lt;/strong&gt; routers plus a couple of Alpine L2 switches, wired up with &lt;a href="https://containerlab.dev/" rel="noopener noreferrer"&gt;containerlab&lt;/a&gt; and instrumented end to end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;routers → gNMI&lt;/strong&gt; streaming telemetry (OpenConfig + EOS-native), collected by &lt;code&gt;gnmic&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;switches → SNMP&lt;/strong&gt; via &lt;code&gt;snmp_exporter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;everything into &lt;strong&gt;Prometheus&lt;/strong&gt;, visualized in &lt;strong&gt;Grafana&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The spike itself was modest — about &lt;strong&gt;+130 MB&lt;/strong&gt;, gone in &lt;strong&gt;90 seconds&lt;/strong&gt;. Easy to ignore. But it was a perfect excuse to test the thing I actually care about: &lt;em&gt;when a graph twitches, can the stack tell you why?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Act 1 — First guess (wrong)
&lt;/h2&gt;

&lt;p&gt;The memory metric moved on all four routers &lt;strong&gt;in lockstep&lt;/strong&gt;. First clue: in cEOS, &lt;code&gt;/system/memory&lt;/code&gt; reports the &lt;strong&gt;underlying host&lt;/strong&gt;, not the individual device — so a "device memory spike" is often really a host-level event, and the four numbers move together because they're all reading the same VM.&lt;/p&gt;

&lt;p&gt;I pulled per-process memory (I'd wired up gNMI's EOS-native &lt;code&gt;/Kernel/proc&lt;/code&gt; tree, joined to process names in PromQL) and did a quick before/after comparison. It showed a burst of short-lived &lt;code&gt;systemctl&lt;/code&gt; processes. Case closed, I said.&lt;/p&gt;

&lt;p&gt;I was wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act 2 — The rabbit hole
&lt;/h2&gt;

&lt;p&gt;Chasing those &lt;code&gt;systemctl&lt;/code&gt; processes led somewhere I didn't expect. In &lt;code&gt;/var/log/messages&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;rsyslog.service: Scheduled restart job, restart counter is at 2478505
rsyslog.service: Main process exited, code=exited, status=1/FAILURE
rsyslog.service: Failed with result 'exit-code'.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;rsyslog was crash-looping — 2.4 million restarts.&lt;/strong&gt; Root cause, once I dug in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ss &lt;span class="nt"&gt;-lxp&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;journal/syslog
&lt;span class="go"&gt;u_dgr UNCONN ... /run/systemd/journal/syslog ... users:(("systemd",pid=1,fd=88))
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;systemd (PID 1) holds &lt;code&gt;/run/systemd/journal/syslog&lt;/code&gt; as a &lt;strong&gt;socket-activation&lt;/strong&gt; listener. rsyslog's &lt;code&gt;imuxsock&lt;/code&gt; wasn't consuming the passed fd — it tried to bind the socket itself, got &lt;code&gt;EADDRINUSE&lt;/code&gt;, and exited. And the restart limiter was disabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;on-failure&lt;/span&gt;
&lt;span class="py"&gt;RestartUSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;100ms&lt;/span&gt;
&lt;span class="py"&gt;StartLimitIntervalUSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0   # rate limiting OFF -&amp;gt; never gives up&lt;/span&gt;
&lt;span class="py"&gt;NRestarts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2490662&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So it looped every 100 ms, forever. Completely harmless — EOS logs through its own database, not this rsyslog — and nobody had ever noticed, because you only see it if you run long enough &lt;em&gt;and&lt;/em&gt; watch at the process level. A great bonus find.&lt;/p&gt;

&lt;p&gt;But it was &lt;strong&gt;constant background noise&lt;/strong&gt;, not the cause of &lt;em&gt;this&lt;/em&gt; spike. Back to 13:52.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act 3 — What the logs said: nothing
&lt;/h2&gt;

&lt;p&gt;I checked every log layer, on all four devices, at the moment of the spike:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Result at spike&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;EOS agent log&lt;/td&gt;
&lt;td&gt;&lt;code&gt;show logging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;no events&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConfigAgent&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/log/agents/ConfigAgent-*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;only the boot-init line&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sysdb&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/log/agents/Sysdb-*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;last entry days earlier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux / systemd&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/var/log/messages&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;only rsyslog noise, 0 other lines&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Zero events. Nothing logged at all.&lt;/p&gt;

&lt;p&gt;And that "nothing" is itself a strong signal: no config change (those log &lt;code&gt;%SYS-5-CONFIG_I&lt;/code&gt;), no state event, no OS event. Whatever moved the memory did so &lt;strong&gt;silently&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Act 4 — The time series told the real story
&lt;/h2&gt;

&lt;p&gt;Back to the metrics, but with discipline this time — full time series instead of a two-point comparison. The reliable signal wasn't &lt;code&gt;systemctl&lt;/code&gt; at all; that count was inflated by stale series (a metric-expiry artifact). It was &lt;strong&gt;ConfigAgent + Sysdb&lt;/strong&gt; rising together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;13:22 – 13:50   3182 MB   flat for 28 minutes
13:52           3236 MB   +54 MB, one sharp jump
13:54 – 14:36   3192 → 3183 MB   slow decay, never repeats
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;+54 MB, simultaneously, across all four routers. Flat before, one jump, slow decay, one-off in three hours.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Silent. Simultaneous. One-off. That shape points to one thing: a &lt;strong&gt;read&lt;/strong&gt; — something querying all four devices at once. Because here's the thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Config &lt;strong&gt;writes&lt;/strong&gt; get logged. Config &lt;strong&gt;reads&lt;/strong&gt; don't. There's an entire class of activity your logs will never show you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Act 5 — Then I proved it
&lt;/h2&gt;

&lt;p&gt;A hypothesis you can't reproduce is just a story. So I ran a controlled experiment — record baseline, fire the command at all four in parallel, watch the metric.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 1 — a CLI read:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;r &lt;span class="k"&gt;in &lt;/span&gt;1 2 3&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do for &lt;/span&gt;d &lt;span class="k"&gt;in &lt;/span&gt;core1 core2 edge1 edge2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;clab-campus-&lt;span class="nv"&gt;$d&lt;/span&gt; Cli &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"show running-config all"&lt;/span&gt; &amp;amp;
&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;3183 → 3183 MB&lt;/code&gt;. &lt;strong&gt;No movement. Falsified.&lt;/strong&gt; &lt;code&gt;show running-config&lt;/code&gt; is served by the CLI process itself; it never makes ConfigAgent/Sysdb allocate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 2 — a gNMI read:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;r &lt;span class="k"&gt;in &lt;/span&gt;1 2 3&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do for &lt;/span&gt;d &lt;span class="k"&gt;in &lt;/span&gt;core1 core2 edge1 edge2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;gnmic &lt;span class="nt"&gt;-a&lt;/span&gt; clab-campus-&lt;span class="nv"&gt;$d&lt;/span&gt;:6030 &lt;span class="nt"&gt;-u&lt;/span&gt; admin &lt;span class="nt"&gt;-p&lt;/span&gt; admin &lt;span class="nt"&gt;--insecure&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    get &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="s1"&gt;'eos_native:/Sysdb'&lt;/span&gt; &amp;amp;
&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;4080 → 6520 MB&lt;/code&gt;, then a slow decay back toward baseline. &lt;strong&gt;Same shape as the original event. Confirmed.&lt;/strong&gt; (The magnitude is much bigger only because I read the entire &lt;code&gt;/Sysdb&lt;/code&gt; tree; the original was a small subtree.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Root cause &amp;amp; mechanism
&lt;/h2&gt;

&lt;p&gt;The 13:52 spike was a &lt;strong&gt;bulk gNMI read hitting all four devices&lt;/strong&gt; — almost certainly one of my own commands while building the telemetry.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLI reads&lt;/strong&gt; are served by the &lt;code&gt;Cli&lt;/code&gt; process; the output lives in &lt;em&gt;its&lt;/em&gt; memory → the agents' RSS never moves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;gNMI reads&lt;/strong&gt; go through &lt;strong&gt;Octa → Sysdb&lt;/strong&gt;, serializing the state tree into gNMI/protobuf buffers. Those agents use &lt;strong&gt;memory pools&lt;/strong&gt; — they don't hand memory back to the OS immediately, they GC lazily. Hence the signature: sharp jump, slow decay, settles slightly above baseline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empty logs&lt;/strong&gt;, because read operations don't log. Only writes do.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Four log layers said "nothing happened." The metrics said otherwise. Both were right — the event simply lived in the blind spot of one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I kept
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reads leave no trace — only writes log.&lt;/strong&gt; Rely on logs alone and a whole class of activity is invisible. You need metrics &lt;em&gt;and&lt;/em&gt; logs &lt;em&gt;and&lt;/em&gt; elimination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-point comparisons lie. Read the time series.&lt;/strong&gt; My first wrong answer came from two samples; the truth was in the shape over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Know what your metric actually measures.&lt;/strong&gt; "Device memory" was host memory; a process count was inflated by expiry settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A hypothesis isn't a diagnosis until you reproduce it.&lt;/strong&gt; The experiment killed my convenient first theory and confirmed the real one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep, long-running instrumentation surfaces things nobody looks for&lt;/strong&gt; — like a service that had quietly failed two million times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this needed a fancy tool. It needed the willingness to be wrong on the first guess and to keep pulling the thread until the graph and the experiment agreed.&lt;/p&gt;




&lt;p&gt;The whole lab and a longer bilingual write-up are open source, if you want to poke at it or reproduce the experiment:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://github.com/kedicat308/Arista-LLM" rel="noopener noreferrer"&gt;https://github.com/kedicat308/Arista-LLM&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What's the sneakiest &lt;em&gt;"the logs said nothing"&lt;/em&gt; bug you've run into? I'd love to hear it.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>networking</category>
      <category>observability</category>
      <category>monitoring</category>
    </item>
  </channel>
</rss>
