The world invented tools to do this back in the 1990s. There's no need to reinvent them, poorly, in shell script. They give you strictly size-capped, automatically rotated, rotateable-on-demand, timestamp-named, log files, and timestamps at the beginning of every line of output.
The line timestamps are a non-default option in multilog
, s6-log
, svlogd
, and tinylog
, turned on by a command-line option/argument. cyclog
adds timestamps unconditionally.
TAI64N timestamps, as added by several of these tools, are (as explained at https://unix.stackexchange.com/a/294276/5132) immune to concerns about timezones and DST changes, are easily machine-processed, and are simply converted to human-readable form by the tai64nlocal
filter.
Adapting Kusalananda's cron table entry:
@reboot sudo -u pi sh -c '~pi/ProjectName/launcher.sh 2>&1 | cyclog ~pi/log/crontab'
or:
@reboot sudo -u pi sh -c '~pi/ProjectName/launcher.sh 2>&1 | multilog t ~pi/log/crontab'
There's an inferior way of doing some of this, with the tai64n
filter:
@reboot sudo -u pi sh -c '~pi/ProjectName/launcher.sh 2>&1 | tai64n >> ~pi/crontab.log'
However, this does not do the size capping, the automatic rotation, nor the timestamp naming of the log files. Just short-circuit the inevitable reinvention (poorly, in shell script) of logrotate
that is the next step from that, and the then having to tackle logrotate
's inherent unreliability problems, and skip forward to the 1990s; as above.
Another way to skip forward to the 1990s is to run a service from a service manager, that deals with preventing multiple instances, dæmon context, and proper management mechanisms that allow the service to be stopped/started by the system operator at runtime; rather than using cron
and @reboot
.
Several service managers have service definition mechanisms that completely obviate any need for your launcher.sh
wrapper script and the sudo
stuff. Here's a systemd unit (exhibiting one kind of service definition) converted into a nosh service bundle (exhibiting another kind):
% cat ProjectName.service
[Unit]
Description=https://unix.stackexchange.com/a/559920/5132
[Service]
User=pi
WorkingDirectory=/home/pi/%p
ExecStart=venv/bin/python main.py
[Install]
WantedBy=multi-user.target
%
% system-control convert-systemd-units ./ProjectName.service
%
% system-control print-service-scripts ./ProjectName
start:#!/bin/nosh
start:#Start file generated from ./ProjectName.service
start:true
stop:#!/bin/nosh
stop:#Stop file generated from ./ProjectName.service
stop:true
run:#!/bin/nosh
run:#Run file generated from ./ProjectName.service
run:#https://unix.stackexchange.com/a/559920/5132
run:envuidgid --supplementary -- pi
run:userenv-fromenv
run:chdir /home/pi/ProjectName
run:setuidgid --supplementary -- pi
run:venv/bin/python main.py
restart:#!/bin/sh
restart:#Restart file generated from ./ProjectName.service
restart:sleep 0.1
restart:exec false # ignore script arguments
%
For nosh service management, and daemontools-family service management in general, one also sets up a parallel cyclog@ProjectName
(or some such) service bundle for the logging (running cyclog
, multilog
, et al.), and tells the service manager that the latter is the logging service for the former.
Service managers like the nosh per-user-manager
, systemd, and Upstart even provide mechanisms for users to configure, run, and manage their own, unprivileged, services.
Further reading