How to output logs to a file from the content of a service with systemd

journalctljournaldloggingsystemctlsystemd

Well, I have a service configured with systemctl. This is the config file:

[Unit]
Description=The description of the service (:
After=network.target

[Service]
ExecStartPre=/bin/echo 'Starting Service' >> /var/log/node/myapp.log
ExecStart=/root/.nvm/versions/node/v5.0.0/bin/node ./bin/www
ExecStopPost=/bin/echo 'Stopping Service' >> /var/log/node/myapp.log
Restart=always
#StandardOutput=/var/log/node/myapp.log
#StandardError=/var/log/node/myapp.log
SyslogIdentifier=myapp
Environment=NODE_ENV=production
WorkingDirectory=/home/user/node-apps/theapp

[Install]
WantedBy=multi-user.target

What I need?:

1) The ExecStartPre and ExecStopPost could write the message 'starting service' or 'stopping service' to the file /var/log/node/myapp.log. With the above configuration, doesn't work, it only outputs 'Starting Service' >> /var/log/node/myapp.log and 'Stopping Service' >> /var/log/node/myapp.log to journalctl. (I checked with journalctl -u myapp)

2) I need that instead of the all logs of the app, outputs to the journalctl, could output to a file. Ex: /var/log/node/myapp.log. I mean, if in my app, I have a console.log(), this could be there.

With upstart, I can do it in this way:

script

exec start-stop-daemon --start --make-pidfile --pidfile /var/run/upstart-yourapp.pid --chdir /var/www/yourapp/--chuid user:usergroup --exec /usr/bin/node index.js >> /var/log/yourapp.upstart.log 2>&1

end script

pre-start script
    # Date format same as (new Date()).toISOString() for consistency
    echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Starting" >> /var/log/yourapp.upstart.log
end script

post-stop script
    rm /var/run/upstart-ghost.pid
    echo "[`date -u +%Y-%m-%dT%T.%3NZ`] (sys) Stopping" >> /var/log/yourapp.upstart.log
end script

But, it is possible to do it with systemctl?

Best Answer

I encountered a similar issue. As explained here, it turns out that you can't redirect output directly within ExecStartPre, ExecStart, or ExecStopPost commands - systemd will interpret the > or >> as arguments. The solution is to execute your command using sh -c.

There's also one other issue I ran into when trying to use the date command in my systemd script: %m is a special code referring to the machine ID of the current host. So in order to have it output the month you need to escape the percent sign by using two percent signs (%%). Putting it all together, the systemd version of the above pre and post-scripts is:

# Date format same as (new Date()).toISOString() for consistency
ExecStartPre = /bin/sh -c 'echo "[`date -u +%Y-%%m-%dT%T.%3NZ`] (sys) Starting" >> /var/log/yourapp.upstart.log'
ExecStopPost = /bin/sh -c 'rm /var/run/upstart-ghost.pid; echo "[`date -u +%Y-%%m-%dT%T.%3NZ`] (sys) Stopping" >> /var/log/yourapp.upstart.log'