You can do:
: > log # empty log file if necessary
{ { {
...the script
} 3>&- | tee -a log >&3 3>&-
exit "${PIPESTATUS[0]}"
} 2>&1 | tee -a log 3>&-
} 3>&1
exit "${PIPESTATUS[@]}"
You could also write it as:
: > log # empty log file if necessary
exec 2> >(tee -a log >&2) > >(tee -a log)
...the script
But because bash is not waiting for those processes started with >(...), that has the nasty effect of sometimes outputting something to the terminal after the command has returned which can have even nastier effects (like silently discarding that output) if the terminal "tostop" attribute is on.
In any case, by making stdout a pipe in both solutions, and because two commands independently output the output and error messages, this will affect output buffering and the order the output and error messages are displayed.