I'm going to try and directly answer the questions [I think] you're actually asking.
How should a program handle UNIX signals?
Generally speaking, it shouldn't.
UNIX signals are a somewhat dated communication method, but they're pretty carefully constructed to handle a certain number of situations fairly well. In most cases, unless you know you have a specific need to handle a signal, you should just let the default behavior happen, and not write a handler for it at all.
The bash shell (and probably other shells, but I don't know) will already report to the user if a program exited due to a signal, so for example if it receives SIGSEGV, then Segmentation fault will be printed. So there's no point in the program itself trying to catch that and display a message, because this is already accounted for. Sure, if you're running a GUI program and not running it from a termimal, then messages like this will typically go missing, but for better or worse, this has become standard fare for almost all programs on Linux by now, so users who need to investigate an odd crash will be used to having to retry something from a terminal to see any error messages if the program mysteriously exited before.
Sometimes, you'll want to handle signals specifically, for example, you might want to handle SIGINT and do some cleanup before exiting. If you do this, it's strongly recommended that after receiving some particular signal and doing your cleanup, you then use the raise function passing the same signal to ensure your process still exits with that signal even after doing its custom cleanup. This is because shell behavior is different when a process exits with a signal, especially if it was called from a shell script rather than directly, so if you exited with a zero or non-zero exit status in response to receiving a signal, you interfere with that intended behavior.
Sometimes, you'll want to handle signals that don't mean exiting afterwards. SIGWINCH for example tells command-line programs that the size of a terminal window has changed. SIGUSR1 and SIGUSR2 have the unfortunate default behavior of terminating the process but can be used for anything the program author likes, for example dd responds to SIGUSR1 by printing progress information (and not terminating).
But in all these cases, if you need to handle a particular signal, you'll know you need to.
How should a program report 'normal' errors?
In exactly the same way it would report a 'normal' success.
A command-line program asked to run in 'verbose' mode will typically produce some 'success' messages on stderr (so as to not interfere with its output on stdout); so its 'normal' errors should also go to stderr (whether verbose or not). A GUI program will typically report successes either in message-boxes or just by updating some label or other non-interactable widget, or displaying a closeable notification widget, or the like; so it should display its 'normal' errors in exactly the same way.
A daemon will typically handle requests from somewhere - that could mean something like HTTP requests, or it could mean arbitrary connections to a socket or D-bus address or whatnot - and on success will typically reply with something to indicate success, so therefore on error it should reply in the same way but with something to indicate failure.
Importantly, the error should only cancel that particular 'user action' and the program should generally stay running. Of course, if the error concerns the startup of the whole program, or it's a non-interactive command-line program, then the 'user action' here is 'run the whole program' meaning that in this case the program should exit - but for an interactive command-line program or a GUI program would typically stay running and let the user input more actions after displaying an error - and a daemon would typically reject the errored request and then stay running to take further requests.
How should a daemon report 'abnormal' errors?
By writing to stderr.
systemd will automatically know how to put this in the relevant log, and the system administrator can configure where that goes. And for the odd systems not using systemd or something broadly equivalent to it, it's generally the job of whoever writes a system-specific wrapper for the daemon to take what ends up on stderr and log it properly.
Either way, in general, the process actually 'doing the work' in a daemon should simply write errors to stderr, and not try and implement some more complicated form of logging itself.
(Of course, errors about a particular request - which are not 'abnormal' errors - should not be written to stderr, they should be reported as the response to the request as above. If you write every request-level error to stderr, it means if someone can make a request that results in that error, someone can just repeatedly make that request to spam your log file, which isn't good.)
And finally...
What is Linux's recommendation?
Linux is a kernel. Linux doesn't provide recommendations on this kind of thing, AFAIK.
On the other hand, Linux distros may well have recommendations. Certain shells and desktop environments may have recommendations. systemd has some recommendations. And there are various other conventions that have been established over time that you'll find outside of any particular project. Many of these will agree with each other, but sometimes, recommendations will disagree with each other.
Ultimately, you should either find one good source for this kind of thing and stick to it (for example, if you have a particular desktop environment in mind, then follow their conventions) or research a number of different sources and consider them all yourself.
man logger rsyslog, and follow the "See Also" refs.execd by some other program. Programmers, therefore, were strongly encouraged to encode most of what the "user" (who might actually be another program) needed to know about why a program failed in the process's exit status.