Visualising CPU temperature history with Python
Tracking processor temperatures becomes more than a curiosity when your server sits in a suburb where summer routinely pushes past forty degrees. Across Australian capital cities, from Brisbane's muggy January afternoons to Adelaide's furnace-like northerlies, the same heat residents complain about on the news quietly cooks the silicon inside unattended machines. Writing a Python script to parse and visualise CPU temperature logs over time turns that invisible wear into a readable chart, helping you decide when to clean a filter, replace thermal paste, or move a workstation out of a west-facing window.
The approach below uses well-known Python libraries and assumes you already have a stream of readings written to a text log somewhere on disk. It walks through parsing those entries, cleaning them into a pandas DataFrame, plotting them as a matplotlib time series, and scheduling the pipeline so the chart refreshes itself. The same template works for GPU sensors, ambient room probes, or readings from a Pi mounted on a Pilbara weather station.
Gathering temperature logs from the system
Linux distributions running across Australian businesses, from Debian workstations in Perth offices to Red Hat clusters supporting research in Canberra, expose CPU temperatures through the kernel's thermal subsystem. The simplest sources are files at /sys/class/thermal/thermal_zone*/temp, which report millidegrees Celsius, and the lm-sensors package, which gives a human-readable breakdown per core after running sensors-detect once. The sensors command tends to produce friendlier output for downstream parsing.
A small shell loop captures readings on a schedule and tags each line with an ISO 8601 timestamp:
while true; do
echo "$(date -Iseconds) $(sensors | awk '/Core 0/ {print $3}')" >> /var/log/cpu_temps.log
sleep 60
done
Run that under a systemd service or in a detached terminal session, and within a day you have a tidy log of one-minute readings ready for Python. The ISO timestamp format sorts alphabetically and survives timezone conversions, which matters later when you reconcile local wall-clock readings with the rest of your monitoring stack.
Parsing raw log lines with regular expressions
The trick with log parsing is rarely the language itself. It is getting the pattern right on the first pass. A line such as 2024-01-15T14:22:33+10:00 +52.0°C looks straightforward, but variations in whitespace, optional degree symbols, and stray characters from sensor glitches can all trip up a naive parser. A regex such as ^(\S+)\s+\+?(-?\d+\.\d+)°?C?$ captures the timestamp and the numeric reading, with optional plus signs and trailing characters handled gracefully.
Working through the pattern interactively with a Python regular expression debugger saves considerable frustration when the log format drifts, as it inevitably does after firmware updates. Once stable, the pattern slots into a generator that yields (datetime, float) tuples line by line, keeping memory usage flat even when processing months of data collected during a long Adelaide summer.
Structuring clean records in pandas
Once the parser yields tuples, building a DataFrame is a single pd.DataFrame(rows, columns=['ts', 'temp_c']) call away. Telling pandas to treat the timestamp column as a datetime with pd.to_datetime(df['ts'], utc=True) normalises everything to UTC and sidesteps the perennial headache of mixed Australian time zones, where a log file may span AEDT during daylight saving and AEST the rest of the year.
Missing values deserve explicit attention. A loose sensor cable or a momentary glitch in the sensors command produces nan entries that will otherwise produce gaps or spikes in your chart. Filling short holes with df['temp_c'].interpolate(method='time') yields a smoother line without inventing data, while longer outages are best left as visible gaps so they flag underlying trouble rather than being silently smoothed over.
Rendering the time series with matplotlib
The plotting stage is where the project crosses from a sysadmin script into something you can share with a colleague or a client. A line chart with timestamp on the x-axis and temperature on the y-axis immediately surfaces patterns: daily workload spikes, weekend lulls, and the slow upward drift that hints at dried thermal paste or a clogged dust filter. Adding a rolling thirty-minute average as a second line makes the underlying trend easier to read when you zoom out to a full quarter of readings.
A worthwhile refinement in Australian contexts is overlaying the local ambient temperature pulled from the Bureau of Meteorology public feed. Correlating ambient peaks against CPU thermal response during a Melbourne forty-degree day often reveals that what looked like a hardware fault was simply the air-conditioning struggling to keep up. The same approach handles GPU temperatures during long render jobs or readings from a Pi sitting in a Darwin telecommunications hut.
Detecting anomalies and thermal drift
A line chart answers what happened, but for day-to-day operation you usually want to know what is going wrong right now. Adding a threshold band to the matplotlib output, say a warning line at seventy-five degrees and a critical line at eighty-five, turns the picture into a glanceable dashboard. Values that breach those lines for more than a few minutes are flagged in a separate DataFrame, ready for an alerting channel.
Comparing the rolling average against the same window from the previous week is a cheap way to spot creeping thermal drift. A machine that runs ten degrees hotter this week than last, even at idle, often points to a failing fan or a heatsink that has worked loose during a recent transit. Capturing those comparisons in the same script means you can wake up to a Slack message rather than discovering the problem when a process quietly dies during a Sydney summer afternoon.
Automating the pipeline and sharing results
A script that runs only when you remember to launch it quickly becomes a forgotten folder of stale notebooks. Wrapping the parser, the pandas cleaning, and the matplotlib rendering inside a single build_chart() function lets you schedule the whole pipeline with cron or a systemd timer. Emailing the PNG to yourself, dropping it into an S3 bucket, or posting it into a private Mattermost channel keeps the feedback loop tight enough that you actually look at the chart.
For those who publish their tooling, the natural home for the finished script, the sample logs, and the rendered charts sits on the author's developer portfolio alongside other utilities. Pinning the latest visualisation there builds a living record of the hardware you operate, and makes it easy to share the methodology with anyone curious about how you keep an eye on thermal behaviour across a fleet that might span a home office in Hobart and a colocated rack in Macquarie Park.
