Unified logging: rsyslog as the system logger, the ForgeFIRM log tree

rsyslog replaces busybox syslogd/klogd (VIRTUAL-RUNTIME_base-utils-syslog,
trimmed PACKAGECONFIG) and becomes the only log writer: the appended
/etc/rsyslog.conf sets the inputs and the ff_line format and includes
the per-logger rules that `forgectrl --render-syslog` renders from the
machine settings at boot. forgefirm-logrotate becomes forgefirm-logging:
render before rsyslog starts (S19), sweep the pre-syslog log files into
/data/forgefirm/legacy-logs once, and rotate the tree at boot and hourly
by rename + HUP instead of copytruncate. Pins bumped to the pushed
forgectrl (syslog emitter, Logs tab, export), grblHAL-glowforge (syslog
emitter) and python3-gfhardware apps (syslog handlers, capture dir);
the CI harnesses set FFLOG_STDERR=1 so failure diagnostics keep the
controller's log lines. BRINGUP carries the bench validation checklist
(Next work item 14); this is an image change and rides the next flash.
This commit is contained in:
ScottW514
2026-08-15 14:37:33 -04:00
parent b08e5ab929
commit 6050c0e703
16 changed files with 257 additions and 95 deletions
+10
View File
@@ -7,3 +7,13 @@ DISTRO_VERSION = "0.0.0"
DISTRO_FEATURES:remove = " \
3g alsa avahi bluetooth bluez5 ext2 ipv6 irda nfc nfs opengl pci pcmcia \
pulseaudio vulkan wayland x11 zeroconf "
# System logger: rsyslog replaces busybox syslogd/klogd. Every ForgeFIRM
# process logs through it and it is the only log writer (one directory
# per logger under /data/log/forgefirm; per-logger levels and the remote
# target come from the machine settings). Trimmed to what the image uses:
# the local socket and kernel inputs, file output, plain UDP/TCP
# forwarding, and rainerscript filters - no TLS, database, HTTP, or
# signing modules (rootfs must fit the 200 MiB factory slot).
VIRTUAL-RUNTIME_base-utils-syslog = "rsyslog"
PACKAGECONFIG:pn-rsyslog = "rsyslogd rsyslogrt klog inet regexp"
@@ -0,0 +1,12 @@
# rsyslog is the ForgeFIRM system logger: the only log writer, one
# directory per logger under /data/log/forgefirm. This appends the
# ForgeFIRM /etc/rsyslog.conf (inputs, line format, and the include of
# the per-logger rules that forgectrl renders at boot); the feature set
# is trimmed in conf/distro/forgefirm.conf (PACKAGECONFIG:pn-rsyslog).
FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"
do_install:append() {
# The stock rotation set covers /var/log files this image never
# writes; the ForgeFIRM tree has its own (forgefirm-logging).
rm -f ${D}${sysconfdir}/logrotate.d/logrotate.rsyslog
}
@@ -9,7 +9,7 @@ PV = "0.1.0"
SRC_URI = "git://github.com/ScottW514/forgectrl.git;protocol=https;branch=main"
# Pinned; bump deliberately after pushing forgectrl changes.
SRCREV = "801f1f3a1f90c38157429ee1e4ff79b0c2fe68e3"
SRCREV = "b4e01c5b24c75d09d6784c690d1c10654bc46da3"
S = "${WORKDIR}/git"
@@ -11,7 +11,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=62f8bb455fcc4bf177ecab380f71cd5d"
SRC_URI = "git://github.com/ScottW514/python3-gfhardware.git;protocol=https;branch=master"
# Pinned; bump deliberately (AUTOREV is not reproducible).
SRCREV = "c3d1790e6cd8f11eee7b386b5314d53454a1c2db"
SRCREV = "a0a174d8e9a6b4b6e3abe63acc51018199df4202"
# Bump PV with every SRCREV move: the hash-derived package version is not
# monotonic on its own and buildhistory QA fails the build when it sorts
@@ -0,0 +1,80 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: forgefirm-logging
# Required-Start: $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: ForgeFIRM logging tree: rsyslog rules, rotation
### END INIT INFO
# Runs before rsyslog (S19 < S20):
# 1. renders the per-logger rsyslog rules and log directories from the
# machine settings (forgectrl --render-syslog) - this is why a log
# level changed in the control panel applies at the next reboot;
# 2. moves log files from before the syslog tree out of the way, once;
# 3. rotates at boot (bounds carry-over bloat), then hourly from a small
# background loop (the image runs no cron daemon).
FORGECTRL=/usr/bin/forgectrl
LOGROTATE=/usr/sbin/logrotate
CONF=/etc/logrotate.conf
PIDFILE=/var/run/forgefirm-logging.pid
LEGACY=/data/forgefirm/legacy-logs
render() {
[ -x "$FORGECTRL" ] || return 0
# syslog is not up yet at this point: report to the console
"$FORGECTRL" --render-syslog || echo "forgefirm-logging: render-syslog failed"
}
# Files written by ForgeFIRM before it logged through syslog. Moved, not
# deleted, so nothing on the bench is lost; the factory's own
# /data/glowforge.log and /data/log/glowforge* are not ours and stay.
sweep_legacy() {
moved=0
for f in /data/forgectrl.log /data/forgectrl.log.* /data/forgectrl.log-* \
/data/gfcloud.log /data/gfcloud.log.* /data/gfhome.log /data/gfhome.log.*; do
[ -e "$f" ] || continue
mkdir -p "$LEGACY"
mv "$f" "$LEGACY/" && moved=1
done
for d in /data/log/gfcloud /data/log/gfhome; do
[ -d "$d" ] || continue
mkdir -p "$LEGACY"
mv "$d" "$LEGACY/" && moved=1
done
[ $moved -eq 1 ] && \
echo "forgefirm-logging: pre-syslog log files moved to $LEGACY (delete when no longer needed)"
return 0
}
case "$1" in
start)
render
sweep_legacy
[ -x "$LOGROTATE" ] || exit 0
"$LOGROTATE" "$CONF" 2>/dev/null
( while :; do
sleep 3600
"$LOGROTATE" "$CONF" 2>/dev/null
done ) &
echo $! > "$PIDFILE"
;;
stop)
[ -r "$PIDFILE" ] && kill "$(cat "$PIDFILE")" 2>/dev/null
rm -f "$PIDFILE"
;;
restart)
$0 stop
$0 start
;;
render)
render
;;
*)
echo "Usage: $0 {start|stop|restart|render}"
exit 1
;;
esac
exit 0
@@ -0,0 +1,17 @@
# The ForgeFIRM logging tree: one directory per logger under
# /data/log/forgefirm, rsyslog the only writer. Size-capped, so a
# chatty debug level cannot fill /data; rotation renames and HUPs
# rsyslog (never copytruncate - no lost lines).
/data/log/forgefirm/*/*.log {
size 2M
rotate 3
nodateext
compress
missingok
notifempty
create 0640 root root
sharedscripts
postrotate
pkill -HUP rsyslogd 2>/dev/null || true
endscript
}
@@ -0,0 +1,30 @@
SUMMARY = "ForgeFIRM logging tree: rsyslog rules at boot, rotation"
DESCRIPTION = "Boot-time glue for the ForgeFIRM logging tree \
(/data/log/forgefirm/<logger>/): renders the per-logger rsyslog rules \
from the machine settings before rsyslog starts (forgectrl \
--render-syslog), moves pre-syslog log files out of the way once, and \
drives size-capped logrotate at boot and hourly (a full /data breaks \
settings, update staging, and the controllers' own writes)."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = " \
file://forgefirm.logrotate \
file://forgefirm-logging.init \
"
S = "${WORKDIR}"
inherit update-rc.d
INITSCRIPT_NAME = "forgefirm-logging"
# 19: before rsyslog's own script (syslog, S20) so the rendered rules
# exist when it reads its config.
INITSCRIPT_PARAMS = "start 19 2 3 4 5 ."
RDEPENDS:${PN} = "logrotate rsyslog forgectrl"
do_install() {
install -Dm 0644 ${WORKDIR}/forgefirm.logrotate ${D}${sysconfdir}/logrotate.d/forgefirm
install -Dm 0755 ${WORKDIR}/forgefirm-logging.init ${D}${sysconfdir}/init.d/forgefirm-logging
}
@@ -1,43 +0,0 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: forgefirm-logrotate
# Required-Start: $local_fs
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: rotate the /data logs at boot and hourly
### END INIT INFO
# The image runs no cron daemon, so rotation is driven here: once at
# every boot (bounds carry-over bloat), then hourly from a small
# background loop (bounds growth across long uptimes - a full /data
# breaks settings, update staging, and the controllers' own writes).
LOGROTATE=/usr/sbin/logrotate
CONF=/etc/logrotate.conf
PIDFILE=/var/run/forgefirm-logrotate.pid
case "$1" in
start)
[ -x "$LOGROTATE" ] || exit 0
"$LOGROTATE" "$CONF" 2>/dev/null
( while :; do
sleep 3600
"$LOGROTATE" "$CONF" 2>/dev/null
done ) &
echo $! > "$PIDFILE"
;;
stop)
[ -r "$PIDFILE" ] && kill "$(cat "$PIDFILE")" 2>/dev/null
rm -f "$PIDFILE"
;;
restart)
$0 stop
$0 start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0
@@ -1,12 +0,0 @@
# ForgeFIRM logs on the persistent /data partition. copytruncate: the
# daemon and both controllers keep their log fds open for their whole
# lifetime, so the file must be truncated in place, never moved out
# from under them.
/data/forgectrl.log /data/glowforge.log /data/gfcloud.log /data/gfhome.log {
size 1M
rotate 3
copytruncate
compress
missingok
notifempty
}
@@ -1,26 +0,0 @@
SUMMARY = "Log rotation for the ForgeFIRM logs on /data"
DESCRIPTION = "Size-capped rotation for the daemon and controller logs \
on the persistent /data partition: a full /data breaks settings, update \
staging, and the controllers' own writes. Rotation runs at every boot \
and hourly while the machine is up."
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = " \
file://forgefirm.logrotate \
file://forgefirm-logrotate.init \
"
S = "${WORKDIR}"
inherit update-rc.d
INITSCRIPT_NAME = "forgefirm-logrotate"
INITSCRIPT_PARAMS = "defaults 40"
RDEPENDS:${PN} = "logrotate"
do_install() {
install -Dm 0644 ${WORKDIR}/forgefirm.logrotate ${D}${sysconfdir}/logrotate.d/forgefirm
install -Dm 0755 ${WORKDIR}/forgefirm-logrotate.init ${D}${sysconfdir}/init.d/forgefirm-logrotate
}
@@ -11,7 +11,7 @@ PV = "0.1.0"
# upstream).
SRC_URI = "gitsm://github.com/ScottW514/grblHAL-glowforge.git;protocol=https;branch=main"
# Pinned; bump deliberately after pushing grblHAL-glowforge changes.
SRCREV = "b629c188227a0ad4eb47095cf9ebe7783a2b9a74"
SRCREV = "67d026d39ac03286937774f5e28946b88c44d490"
SRC_URI += "file://grblhal.init"
@@ -24,9 +24,12 @@ IMAGE_INSTALL:remove = "gfui-client"
# to the inactive rootfs slot.
# ffboot: boot-slot inventory and switching (also ships fw_env.config).
# slotmigrate: boot-time reclaim of the legacy p4 layout (grows /data).
# forgefirm-logrotate: size-capped rotation of the /data logs (boot +
# hourly; a full /data breaks settings, updates, and controller writes).
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome gfcloud v4l-utils fwup ffboot slotmigrate forgefirm-logrotate"
# forgefirm-logging: the ForgeFIRM logging tree - renders the per-logger
# rsyslog rules from the settings before rsyslog starts, and drives
# size-capped rotation (boot + hourly; a full /data breaks settings,
# updates, and controller writes). rsyslog itself comes in through
# VIRTUAL-RUNTIME_base-utils-syslog (conf/distro/forgefirm.conf).
IMAGE_INSTALL:append = " grblhal-glowforge forgectrl gfhome gfcloud v4l-utils fwup ffboot slotmigrate forgefirm-logging"
# NXP's firmware EULA covers the i.MX VPU/EPDC blobs the BSP installs, so the
# image ships the license text with them (/usr/share/licenses/firmware-imx).