Reputation: 6305
I wrote a logger function that is supposed to output colored text to screen while also redirecting the text to a log file using the tee
command.
This the logger function:
function logger(){
GREEN=$(tput setaf 2)
YELLOW=$(tput setaf 3)
BOLD=$(tput bold)
UNDERLINE=$(tput smul)
NOCOLOR=$(tput sgr0)
case "$1" in
y)
echo -e -n "$(timestamp) ${YELLOW}$2 ${NOCOLOR}\n" | tee -a $log_file
;;
g)
echo -e -n "$(timestamp) ${GREEN}$2 ${NOCOLOR}\n" | tee -a $log_file
;;
b)
echo -e -n "$(timestamp) ${BOLD}$2 ${NOCOLOR}\n" | tee -a $log_file
;;
u)
echo -e -n "$(timestamp) ${UNDERLINE}$2 ${NOCOLOR}\n" | tee -a $log_file
;;
n)
echo -e -n "$(timestamp) $2\n" | tee -a $log_file
;;
*)
echo "Unknown color!"
;;
esac
}
The problem is that when the text is written in the log file, the color codes are also applied and it makes the text seem dirty.
This is how it looks:
2021-08-31 12:36:41 UTC +0000 ^[[1m------------ Now working on account CompanyEUResearchAndDevelopmentJenkinsSlave in region us-east-2 ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 ^[[32mThe following IPs are allocated and associated: ^[(B^[[m
2021-08-31 12:36:42 UTC +0000 13.11.131.202 18.11.34.219 3.11.227.231
2021-08-31 12:36:42 UTC +0000 ----------------------------------------------------------
2021-08-31 12:36:44 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:44 UTC +0000 18.11.91.21
2021-08-31 12:36:45 UTC +0000 ^[[33mIP 18.11.91.21 found in groups: ^[(B^[[m
2021-08-31 12:36:45 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:46 UTC +0000 ^[[32mThe rule containing ip 18.11.91.21 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mReleasing ip 18.11.91.21 with AllocationId eipalloc-07528d88e8794c6db ^[(B^[[m
2021-08-31 12:36:46 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 ^[[33mFound unassociated elastic ips: ^[(B^[[m
2021-08-31 12:36:47 UTC +0000 3.11.248.103
2021-08-31 12:36:48 UTC +0000 ^[[33mIP 3.11.248.103 found in groups: ^[(B^[[m
2021-08-31 12:36:48 UTC +0000 sg-04b6da1d06783ffbf
2021-08-31 12:36:49 UTC +0000 ^[[32mThe rule containing ip 3.11.248.103 has been deleted from security group sg-04b6da1d06783ffbf successfully ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mReleasing ip 3.11.248.103 with AllocationId eipalloc-0a699e32f4ac844dc ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 ^[[32mAllocation released successfully! ^[(B^[[m
2021-08-31 12:36:49 UTC +0000 End of run
This is how use the function:
logger g "No elastic ips found in account ${role_name}"
I wonder what's the "most right" way to do that?
Upvotes: 0
Views: 322
Reputation: 34244
A modification of Vaphell's answer (towards bottom of page) that removes the color codes from the tee
stream before appending/writing to $log_file
:
Change this:
echo -e -n "$(timestamp) ${GREEN}$2 ${NOCOLOR}\n" | tee -a $log_file
To this:
echo -e -n "$(timestamp) ${GREEN}$2 ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
Since you're likely looking to call logger()
quite often, performance is going to be a big issue, and the biggest improvements are going to come from eliminating as many (unnecessary) subprocess calls as possible.
tl;dr
echo
calls, one to stdout, one to $log_file
$(timestamp)
call which I'm assuming includes a further subprocess call to date
tput
subprocess calls) just once, ie, eliminate the repeated 5x subprocess calls (to tput
) on each call to logger()
use 2x separate echo
calls
per user1934428's comment ... calling echo
two times, once to stdout, once to (append to) $log_file
is another option, eg:
Instead of this:
echo -e -n "$(timestamp) ${GREEN}$2 ${NOCOLOR}\n" | tee >(sed 's/\x1B[\[\(][0-9;]*[BJKmsu]//g' >> $log_file)
Use this:
echo -e -n "$(timestamp) ${GREEN}$2 ${NOCOLOR}\n"
echo -e -n "$2\n" >> $log_file
Using the following test:
time for i in {1..100}
do
mylogger g hello > /dev/null
done
NOTES:
/usr/bin/logger
so I've named my function mylogger()
tput
calls each time the function is called (otherwise the timings for the following tests would be even worse)Results:
real 0m8.739s # echo ... | tee >(sed ... >> $log_file)
user 0m3.448s
sys 0m4.706s
real 0m0.105s # echo ... ; echo ...
user 0m0.047s
sys 0m0.047s
As you can see, the use of two separate echo
commands is going to be a LOT faster than the | tee >(sed ...)
solution ... or any other solution that requires making (unnecessary) subprocess calls
eliminate the subprocess $(timestamp)
(and date
) call
I'm assuming the $(timestamp)
call is a custom function/binary that generates a date/time string from the current date
; if running bash 4.2
(or better) consider using printf
to generate the date/time string and store in a local variable, eg:
printf -v NOW '%(%F %H:%M:%S %Z %z)T' -1
NOTES:
date
and therefore is going to run quite a bit fasterprintf
call directly in the logger()
function thus eliminating the $(timestamp)
subprocess callload the color variables (ie, tput
subprocess calls) just once
As I mentioned in one of my comments, consider populating the color variables once thus eliminating the current process of making 5x subprocess calls to tput
each time the logger()
function is called; a very simple solution that continues to use OP's current <color>=$(tput ...)
format:
function load_colors() {
if [[ -z "${NOCOLOR}" ]]
then
export GREEN=$(tput setaf 2)
export YELLOW=$(tput setaf 3)
export BOLD=$(tput bold)
export UNDERLINE=$(tput smul)
export NOCOLOR=$(tput sgr0)
fi
}
function logger() {
load_colors
case "$1" in
... snip ...
esac
}
Upvotes: 1
Reputation: 11026
Try piping before tee
by the command ansi2txt
.
For example:
echo -e -n "$(timestamp) ${UNDERLINE}$2 ${NOCOLOR}\n" | ansi2txt | tee -a $log_file
Upvotes: 0