Module emsg
func ebanner
Display a very prominent banner with a provided message which may be multi-line as well as the ability to provide any number of extra arguments which will be included in the banner in a pretty printed tag=value optionally uppercasing the keys if requested. All of this is implemented with print_value to give consistency in how we log and present information.
OPTIONS
(*) Denotes required options
(&) Denotes options which can be given multiple times
--lowercase, --lower, -l
If enabled, keys will be all lowercased.
--uppercase, --upper, -u
If enabled, keys will be all uppercased.
func eclear
eclear
is used to clear the screen. But it’s more portable than the standard clear
command as it uses tput
to
lookup the correct escape sequence needed to clear your terminal.
func ecolor
ecolor
is used to take a human color term such as black
or with descriptors such as bold black
and emit the ANSII
escape sequences needed to print to the screen to produce the desired color. Since we use a LOT of color messages
through ebash, this function caches the color codes in an associative array to avoid having to lookup the same values
repeatedly.
NOTE: If
EFUNCS_COLOR
is set to0
, this function is disabled and will not return any ANSII escape sequences.
func ecolor_code
ecolor_code
is used to map human color names like black
to the corresponding ANSII color escape code (e.g. 0
).
This function supports the full 256 ANSII color code space.
func edebug
edebug
is a powerful debugging mechanism to conditionally emit selective debugging messages that are statically
in the source code based on the EDEBUG
environment variable. By default, edebug
messages will not produce any output.
Moreover, they do not add any overhead to the code as we return immediately from the edebug
function if debugging is
not enabled.
For example, suppose I have the following in my source code:
edebug "foo just borked rc=${rc}"
You can activate the output from these edebug
statements either wholesale or selectively by setting an environment
variable. Setting EDEBUG=1
will turn on all edebug
output everywhere. We use this pervasively, so that is probably
going to way too much noise.
Instead of turning everything on, you can turn on edebug
just for code in certain files or functions. For example,
using EDEBUG="dtest dmake"
will turn on debugging for any edebug
statements in any scripts named dtest
or dmake
or any functions named dtest
or dmake
.
Another powerful feature edebug
supports is to send the entire output of another command into edebug
without having
to put an if
statement around it and worrying about sending the output to STDERR. This is super easy to do:
cmd | edebug
The value of EDEBUG
is actually a space-separated list of terms. If any of those terms match the filename (just
basename) or the name of the function that contains an edebug
statement, it will generate output.
func edebug_disabled
edebug_disabled
is the logical analogue of edebug_enabled
. It returns success (0) if debugging is disabled and
failure (1) if it is enabled.
func edebug_enabled
edebug_enabled
is a convenience function to check if edebug is currently enabled for the context the caller is calling
from. This will return success (0) if edebug
is enabled, and failure (1) if not. This can then be used to perform
conditional code depending on if debugging is enabled or not.
For example:
if edebug_enabled; then
dmesg > dmesg.out
ip > ip.out
fi
func eend
eend
is used to print an informational ending message suitable to be called after an emsg
function. The format of
this message is dependent upon the return_code
. If 0
, this will print [ ok ]
and if non-zero it will print [ !! ]
.
OPTIONS
(*) Denotes required options
(&) Denotes options which can be given multiple times
--inline, -n
Display eend inline rather than outputting a leading newline. The reason we emit a leading
newline by default is to work properly with emsg functions (e.g. einfo, ewarn, eerror) as
they all emit a message and then a trailing newline to move to the next line. When paired
with an eend, we want that eend message to show up on the SAME line. So we emit some
terminal magic to move up a line, and then right justify the eend message. This doesn't
work very well for non-interactive displays or in CI/CD output so you can disable it.
--inline-offset, -o <value>
Number of characters to offset inline mode by.
ARGUMENTS
return_code
Return code of the command that last ran. Success (0) will cause an 'ok' message and
any non-zero value will emit '!!'.
func eerror
eerror
is used to log error messages to STDERR. They are prefixed with !!
in COLOR_ERROR
which is red
by
default. eerror
is called just like you would normally call echo
.
func eerror_stacktrace
Print an error stacktrace to stderr. This is like stacktrace only it pretty prints the entire stacktrace as a bright red error message with the funct and file:line number nicely formatted for easily display of fatal errors.
Allows you to optionally pass in a starting frame to start the stacktrace at. 0 is the top of the stack and counts up. See also stacktrace and eerror_stacktrace.
OPTIONS
(*) Denotes required options
(&) Denotes options which can be given multiple times
--color, -c <value>
Use the specified color for output messages.
--frame, -f <value>
Frame number to start at. Defaults to 2, which skips this function and its caller.
--skip, -s
Skip the initial error message. Useful if the caller already displayed it.
func efuncs_color
Determine value to use for efuncs_color. If EFUNCS_COLOR
is empty then set it based on if STDERR is attached to a
console or not.
func efuncs_color_as_bool
Get efuncs_color as a boolean string.
func einfo
einfo
is used to log informational messages to STDERR. They are prefixed with >>
in COLOR_INFO
which is green
by
default. einfo
is called just like you would normally call echo
.
func einfos
einfos
is used to log informational sub messages to STDERR. They are intdented and prefixed with a -
in
COLOR_INFOS
which is cyan
by default. einfos
is called just like you would normally call echo
. This is designed
to line up underneath einfo
messages to show submessages.
func einteractive
Check if we are “interactive” or not. For our purposes, we are interactive if STDERR is attached to a terminal or not. This is checked via the bash idiom “[[ -t 2 ]]” where “2” is STDERR. But we can override this default check with the global variable EINTERACTIVE=1.
func einteractive_as_bool
Get einteractive value as a boolean string
func emsg
emsg
is a common function called by all logging functions inside ebash to allow a very configurable and extensible
logging format throughout all ebash code. The extremely configrable formatting of all ebash logging is controllable via
the EMSG_PREFIX
environment variable.
Here are some examples showcasing how configurable this is:
$ EMSG_PREFIX=time ~/ebash_guide
[Nov 12 13:31:16] einfo
[Nov 12 13:31:16] ewarn
[Nov 12 13:31:16] eerror
$ EMSG_PREFIX=all ./ebash_guide
[Nov 12 13:24:19|INFO|ebash_guide:6:main] einfo
[Nov 12 13:24:19|WARN|ebash_guide:7:main] ewarn
[Nov 12 13:24:19|ERROR|ebash_guide:8:main] eerror
In the above you can the timestamp, log level, function name, line number, and filename of the code that generated the message.
Here’s the full list of configurable things you can turn on:
- time
- level
- caller
- pid
- all
func etestmsg
etestmsg
is used to log informational testing related messages to STDERR. This is typically used inside etest
test
code. These log messages are prefixed with ##
in cyan
. etestmsg
is called just like you would normally call
echo
.
func etrace
etrace
is an extremely powerful debugging technique. It essentially allows you to selectively emit a colorized
debugging message for every line of code executed by ebash without having to modify the source code and sprinkle it
with lots of explicit debugging messages. This means you can dynamically debug code in the field without having to make
and source code changes.
This is similar to the builtin bash set -x
option. But ebash takes this a little further by using selective controls
for command tracing rather than blanket turning on set -x
for the entire process lifetime. Additionally, the messages
are prefixed with a configurable color message showing the filename, line number, function name, and PID of the caller.
The color can be configured via ${COLOR_TRACE}
.
For example, suppose I have the following script:
#!/bin/bash
$(etrace --source)
echo "Hi"
a=alpha
b=beta
echo "$(lval a b)"
You can now run the above script with etrace
enabled and get the following output. Not that rather than just the
command being printed as you’d get with set -x
, etraces emits the file, line number and process PID:
$ ETRACE=etrace_test ./etrace_test
[etrace_test:6:main:24467] echo "Hi"
Hi
[etrace_test:7:main:24467] a=alpha
[etrace_test:8:main:24467] b=beta
[etrace_test:9:main:24467] echo "$(lval a b)"
[etrace_test:9:main:25252] lval a b
a="alpha" b="beta"
Like EDEBUG
, ETRACE
is a space-separated list of patterns which will be matched against your current filename and
function name. The etrace functionality has a much higher overhead than does running with edebug enabled, but it can be
immensely helpful when you really need it.
One caveat: you can’t change the value of ETRACE on the fly. The value it had when you sourced ebash is the one that will affect the entire duration of the script.
func ewarn
ewarn
is used to log warning messages to STDERR. They are prefixed with >>
in COLOR_WARN
which is yellow
by
default. ewarn
is called just like you would normally call echo
.
func ewarns
ewarns
is used to log warning sub messages to STDERR. They are intdented and prefixed with a -
in COLOR_WARNS
which is yellow
by default. ewarns
is called just like you would normally call echo
. This is designed to line up
underneath einfo
or ewarn
messages to show submessages.
func expand_vars
Iterate over arguments and interpolate them as-needed and store the resulting “key” and “value” into a provided
associative array. For each entry, if a custom “key=value” syntax is used, then “value” is checked to see if it refers
to another variable. If so, then it is expanded/interpolated using the print_value
function. If it does not reference
another variable name, then it will be used as-is. This implementation allows for maximum flexibility at the call-site
where they want to have some variables reference other variables underlying values, as in:
expand_vars details DIR=PWD
But also sometimes want to be able to just directly provide the string literal to use, as in:
expand_vars details DIR="/home/marshall"
The keys may optionally be uppercased for consistency and quotes may optionally be stripped off of the resulting value we load into the associative array.
OPTIONS
(*) Denotes required options
(&) Denotes options which can be given multiple times
--lowercase
Lowercase the keys for consistency
--quotes
Include quotation marks around value.
--uppercase
Uppercase the keys for consistency
ARGUMENTS
__details
Name of the associative array to load the key=value pairs into.
entries
Variadic list of variables to interpolate and load the resulting values into the
details array.
func lval
Log a list of variable in tag=”value” form similar to our C++ logging idiom. This function is variadic (takes variable number of arguments) and will log the tag=”value” for each of them. If multiple arguments are given, they will be separated by a space, as in: tag=”value” tag2=”value2” tag3=”value3”
This is implemented via calling print_value on each entry in the argument list. The one other really handy thing this does is understand our C++ LVAL2 idiom where you want to log something with a different key. So you can say nice things like:
$(lval PWD=$(pwd) VARS=myuglylocalvariablename)
You can optionally pass in -n or –no-quotes and it will omit the outer-most quotes used on simple variables such as strings and numbers. But array and associative array values are still quoted to avoid ambiguity.
func noansi
Noansi filters out ansi characters such as color codes. It can modify files in place if you specify any. If you do not, it will assume that you’d like it to operate on stdin and repeat the modified output to stdout.
ARGUMENTS
files
Files to modify. If none are specified, operate on stdin and spew to stdout.
func print_value
Print the value for the corresponding variable using a slightly modified version of what is returned by declare -p. This is the lower level function called by lval in order to easily print tag=value for the provided arguments to lval. The type of the variable will dictate the delimiter used around the value portion. Wherever possible this is meant to generally mimic how the types are declared and defined.
Specifically: 1) Strings: delimited by double quotes. 2) Arrays and associative arrays: Delimited by ( ). 3) Packs: You must preceed the pack name with a percent sign (i.e. %pack)
Examples:
1) String: "value1"
2) Arrays: ("value1" "value2 with spaces" "another")
3) Associative Arrays: ([key1]="value1" [key2]="value2 with spaces" )
func tput
tput
is a wrapper around the real tput
command that allows us more control over how to deal with COLUMNS
not being
set properly in non-interactive environments such as our CI/CD build system. We also allow explicitly setting COLUMNS
to something and honoring that and bypassing calling tput
. This is useful in our CI/CD build systems where we do not
have a console so tput cols
would return an error. This also gracefully handles the scenario where tput isn’t installed
at all as in some super stripped down docker containers.