Problem solved. 1.Why root no permission to write log into /var/log/app.log? 2.What does /bin/bash -c mean in service unit?

Solution:1

NOTE: The old post was written in the context of OP's original question, before edit, so it is incomplete but in the context of what was originally was asked, it's still valid.

Updated answer:

The key issue here is that

ExecStart=/usr/bin/python /usr/local/bin/app > /var/log/app.log 2>&1

Doesn't have shell running , so your > and 2>&1 redirection won't be understood by systemd.

That's why shell is necessary around the whole command so that redirection can work. As for -c flag for shell, see the old version of the post below.

OLD POST

The -c flag in both bash and sh mean same thing: execute commands as provided within the quotes. There's no great mystery.

Your app that you're trying to run might have different meaning for -c flag, so don't assume all command-line flags are the same for all commands. Without documentation for the app, it's hard to tell what an option is supposed to do.

Potential issue here is that python interpreter will assume -c as its own command-line argument, not to the app. Probably that's the main reason your command fails.

The ExecStart=/usr/bin/python /usr/local/bin/app -c /etc/app.json should be able to process your command. I've tested it with a small script:

Better approach: a script should be made executable with sudo chmod +x /usr/local/bin/app and used by itself. The way it is written originally, with /bin/sh, then python, then calling actual script is kind of redundant. In the example of my test script that would be like so:

Beware that python refers to Python 2.7 on Ubuntu. If you need to use Python3 specifically, use /usr/bin/python3 instead. Most preferred way is for the app to have #!/usr/bin/env python or #!/usr/bin/env python3 as first line.

Solution:2

It's not a matter of permissions. There is simply no shell involved in executing the ExecStart string. Only a command and the options to be passed to that command, but neither redirections nor piping nor concatening with ; nor substitution or any other shell functionality. Thus, your "redirections" are passed as options to your app (only you know how your app handles this).

You can change this by calling a shell and giving your command and redirections as command string to the shell. This is what the -c option does.

But think about why not use the services and logging as intended by systemd: By default stdout and stderr of a systemd unit are sent to syslog. You can configure it with the StandardOutput= line in your service. Have a look at man systemd.exec