USB serial port logger with automatic session management for Linux.
- Logs serial data from USB devices with syslog-style timestamps
- Multi-device support - single service handles multiple USB devices
- Session tracking - each session gets a unique ID (format:
MMDD-HHMM-xxxxxxxx) - USB device identity - tracks vendor:product:serial to detect device swaps
- Export tool - list and export sessions by device, session ID, or USB identity
- Automatic daily log rotation (new folder per day)
- New log file on service restart
- Auto-start on device hotplug
- Config file changes auto-applied (no manual reload needed)
- Security hardened (dedicated user, systemd sandboxing, input validation)
Any Linux distribution with systemd. Install from source with the bundled
install.sh (run as root).
Requirements: Linux kernel 4.x+, systemd, bash 4.4+, and the runtime dependencies listed below.
Not supported: macOS, Windows, BSD, non-systemd Linux (OpenRC, runit, etc.)
One-line install (defaults to the latest release):
curl -fsSL https://raw.githubusercontent.com/matterizelabs/ulog/main/install.sh | sudo bashPin a specific release:
curl -fsSL https://raw.githubusercontent.com/matterizelabs/ulog/main/install.sh | sudo ULOG_VERSION=v1.1.3 bashOr from a checkout:
git clone https://github.com/matterizelabs/ulog.git
cd ulog
sudo ./install.shinstall.sh creates the ulog service user, installs the scripts under
/usr/bin and /usr/lib/ulog, writes the default config under /etc/ulog.d,
generates the systemd unit and udev rules, and enables the services.
Uninstall with sudo ./uninstall.sh (log data in /var/log/ulog is left in
place).
Install-time paths can be overridden with environment variables:
| Variable | Default | Purpose |
|---|---|---|
ULOG_BIN_DIR |
/usr/bin |
Where ulog, ulog-genconfig, ulog-export are installed |
ULOG_LIB_DIR |
/usr/lib/ulog |
Where ulog-common.sh is installed |
ULOG_ETC_DIR |
/etc |
Where config and ulog.d live |
ULOG_CHECKSUMS |
(unset) | Optional sha256sum -c file to verify sources before installing |
Edit /etc/ulog.conf:
DEVICE=/dev/ttyUSB0
BAUD=115200
LOG_DIR=/var/log/ulog/ttyUSB0Create a config file per device in /etc/ulog.d/:
# /etc/ulog.d/sensor1.conf
DEVICE=/dev/ttyUSB0
BAUD=115200
# /etc/ulog.d/sensor2.conf
DEVICE=/dev/ttyUSB1
BAUD=9600
# /etc/ulog.d/gps.conf
DEVICE=/dev/ttyACM0
BAUD=38400Each device gets its own log directory: /var/log/ulog/<device_name>/
Changes are auto-applied when config files are saved.
Enable and start:
systemctl enable --now ulog.service ulog-rollover.timer ulog-genconfig.pathView logs:
# Single device
tail -f /var/log/ulog/ttyUSB0/$(date +%Y-%m-%d)/*.log
# All devices
tail -f /var/log/ulog/*/$(date +%Y-%m-%d)/*.logCheck status:
systemctl status ulog.service
journalctl -u ulog.service -fEach logging session is assigned a unique ID in the format MMDD-HHMM-xxxxxxxx (e.g., 0120-1830-a1b2c3d4). Sessions are tracked in a session.index file per device with start/end times and USB device identity.
ulog tracks the USB vendor ID, product ID, and serial number for each device. This allows:
- Detecting when a different physical device is connected to the same port
- Filtering exports by specific hardware (useful with multiple identical adapters)
Identity format: vendor:product:serial (e.g., 10c4:ea60:0001a2b3)
# List all sessions for a device
ulog-export --list --device ttyUSB0
# List sessions from all devices
ulog-export --list --allOutput:
SESSION START END IDENTITY FILES
====================================================================================================
0120-1830-a1b2 2026-01-20 18:30:00 2026-01-20 19:45:00 10c4:ea60:0001a2b3 2 file(s)
0120-2000-c3d4 2026-01-20 20:00:00 ongoing 10c4:ea60:0001a2b3 1 file(s)
# Export specific session
ulog-export --session 0120-1830-a1b2 --device ttyUSB0 --output session.log
# Export latest/current session
ulog-export --device ttyUSB0 --output latest.log
# Export multiple sessions combined
ulog-export --session 0120-1830-a1b2,0120-2000-c3d4 --device ttyUSB0 --output combined.log# Export all sessions from Silicon Labs adapters (vendor 10c4)
ulog-export --vendor 10c4 --output silabs.log
# Export from specific vendor and product (CP210x)
ulog-export --vendor 10c4 --product ea60 --output cp210x.log
# Export from specific physical device (by serial, prefix match)
ulog-export --vendor 10c4 --product ea60 --serial 0001a2b3 --output device1.logAdd a new device:
cat > /etc/ulog.d/mydevice.conf << EOF
DEVICE=/dev/ttyUSB2
BAUD=115200
EOF
chown root:ulog /etc/ulog.d/mydevice.conf
chmod 0640 /etc/ulog.d/mydevice.conf
# Service auto-restarts to pick up new deviceLogs use syslog-style timestamps:
Jan 18 14:30:00 sensor reading: 42.5
Jan 18 14:30:01 sensor reading: 43.1
/var/log/ulog/
ttyUSB0/
session.index # Session tracking database
2026-01-18/
2026-01-18_14-30-00.log
2026-01-19/
2026-01-19_00-00-01.log
ttyUSB1/
session.index
2026-01-18/
2026-01-18_14-30-00.log
ttyACM0/
...
/var/lib/ulog/
sessions/ # Runtime state
ttyUSB0.session # Current session ID
ttyUSB0.identity # Last known USB identity
| Key | Description | Default |
|---|---|---|
DEVICE |
Serial device path | /dev/ttyUSB0 |
BAUD |
Baud rate (positive integer, up to 4000000) | 115200 |
LOG_DIR |
Log directory (must be under /var/log/) | /var/log/ulog/<device> |
Valid baud rate: any positive integer up to 4000000 (socat rejects unsupported rates)
ulog/
src/ # Source files
ulog.sh # Main logger script (multi-device)
ulog-export # Session export/list tool
ulog-genconfig # Config generator
ulog-common.sh # Shared parsing/validation helpers
ulog.conf # Default configuration
services/ # Systemd units
ulog-genconfig.path # Watch config for changes
ulog-genconfig.service
ulog-rollover.service
ulog-rollover.timer
install.sh # System-wide installer (run as root)
uninstall.sh # Matching uninstaller
- Runs as dedicated
uloguser (not root) - Systemd sandboxing (ProtectSystem, PrivateTmp, NoNewPrivileges)
- Input validation on all config values
- Config files must be root-owned (0640)
- Device paths strictly validated
- See SECURITY.md for details
- socat
- moreutils (provides
ts) - coreutils (provides
realpath)
MIT