├── LICENSE ├── log.sh ├── README.md └── test.sh /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Zordrak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -uo pipefail; 4 | 5 | function _log_exception() { 6 | ( 7 | BASHLOG_FILE=0; 8 | BASHLOG_JSON=0; 9 | BASHLOG_SYSLOG=0; 10 | 11 | log 'error' "Logging Exception: ${@}"; 12 | ); 13 | } 14 | 15 | function log() { 16 | local date_format="${BASHLOG_DATE_FORMAT:-+%F %T}"; 17 | local date="$(date "${date_format}")"; 18 | local date_s="$(date "+%s")"; 19 | 20 | local file="${BASHLOG_FILE:-0}"; 21 | local file_path="${BASHLOG_FILE_PATH:-/tmp/$(basename "${0}").log}"; 22 | 23 | local json="${BASHLOG_JSON:-0}"; 24 | local json_path="${BASHLOG_JSON_PATH:-/tmp/$(basename "${0}").log.json}"; 25 | 26 | local syslog="${BASHLOG_SYSLOG:-0}"; 27 | local tag="${BASHLOG_SYSLOG_TAG:-$(basename "${0}")}"; 28 | local facility="${BASHLOG_SYSLOG_FACILITY:-local0}"; 29 | local pid="${$}"; 30 | 31 | local level="${1}"; 32 | local upper="$(echo "${level}" | awk '{print toupper($0)}')"; 33 | local debug_level="${DEBUG:-0}"; 34 | 35 | shift 1; 36 | 37 | local line="${@}"; 38 | 39 | # RFC 5424 40 | # 41 | # Numerical Severity 42 | # Code 43 | # 44 | # 0 Emergency: system is unusable 45 | # 1 Alert: action must be taken immediately 46 | # 2 Critical: critical conditions 47 | # 3 Error: error conditions 48 | # 4 Warning: warning conditions 49 | # 5 Notice: normal but significant condition 50 | # 6 Informational: informational messages 51 | # 7 Debug: debug-level messages 52 | 53 | local -A severities; 54 | severities['DEBUG']=7; 55 | severities['INFO']=6; 56 | severities['NOTICE']=5; # Unused 57 | severities['WARN']=4; 58 | severities['ERROR']=3; 59 | severities['CRIT']=2; # Unused 60 | severities['ALERT']=1; # Unused 61 | severities['EMERG']=0; # Unused 62 | 63 | local severity="${severities[${upper}]:-3}" 64 | 65 | if [ "${debug_level}" -gt 0 ] || [ "${severity}" -lt 7 ]; then 66 | 67 | if [ "${syslog}" -eq 1 ]; then 68 | local syslog_line="${upper}: ${line}"; 69 | 70 | logger \ 71 | --id="${pid}" \ 72 | -t "${tag}" \ 73 | -p "${facility}.${severity}" \ 74 | "${syslog_line}" \ 75 | || _log_exception "logger --id=\"${pid}\" -t \"${tag}\" -p \"${facility}.${severity}\" \"${syslog_line}\""; 76 | fi; 77 | 78 | if [ "${file}" -eq 1 ]; then 79 | local file_line="${date} [${upper}] ${line}"; 80 | echo -e "${file_line}" >> "${file_path}" \ 81 | || _log_exception "echo -e \"${file_line}\" >> \"${file_path}\""; 82 | fi; 83 | 84 | if [ "${json}" -eq 1 ]; then 85 | local json_line="$(printf '{"timestamp":"%s","level":"%s","message":"%s"}' "${date_s}" "${level}" "${line}")"; 86 | echo -e "${json_line}" >> "${json_path}" \ 87 | || _log_exception "echo -e \"${json_line}\" >> \"${json_path}\""; 88 | fi; 89 | 90 | fi; 91 | 92 | local -A colours; 93 | colours['DEBUG']='\033[34m' # Blue 94 | colours['INFO']='\033[32m' # Green 95 | colours['NOTICE']='' # Unused 96 | colours['WARN']='\033[33m' # Yellow 97 | colours['ERROR']='\033[31m' # Red 98 | colours['CRIT']='' # Unused 99 | colours['ALERT']='' # Unused 100 | colours['EMERG']='' # Unused 101 | colours['DEFAULT']='\033[0m' # Default 102 | 103 | local norm="${colours['DEFAULT']}"; 104 | local colour="${colours[${upper}]:-\033[31m}"; 105 | 106 | local std_line="${colour}${date} [${upper}] ${line}${norm}"; 107 | 108 | # Standard Output (Pretty) 109 | case "${level}" in 110 | 'info'|'warn') 111 | echo -e "${std_line}"; 112 | ;; 113 | 'debug') 114 | if [ "${debug_level}" -gt 0 ]; then 115 | echo -e "${std_line}"; 116 | fi; 117 | ;; 118 | 'error') 119 | echo -e "${std_line}" >&2; 120 | if [ "${debug_level}" -gt 0 ]; then 121 | echo -e "Here's a shell to debug with. 'exit 0' to continue. Other exit codes will abort - parent shell will terminate."; 122 | bash || exit "${?}"; 123 | fi; 124 | ;; 125 | *) 126 | log 'error' "Undefined log level trying to log: ${@}"; 127 | ;; 128 | esac 129 | } 130 | 131 | declare prev_cmd="null"; 132 | declare this_cmd="null"; 133 | trap 'prev_cmd=$this_cmd; this_cmd=$BASH_COMMAND' DEBUG \ 134 | && log debug 'DEBUG trap set' \ 135 | || log error 'DEBUG trap failed to set'; 136 | 137 | # This is an option if you want to log every single command executed, 138 | # but it will significantly impact script performance and unit tests will fail 139 | 140 | #trap 'prev_cmd=$this_cmd; this_cmd=$BASH_COMMAND; log debug $this_cmd' DEBUG \ 141 | # && log debug 'DEBUG trap set' \ 142 | # || log error 'DEBUG trap failed to set'; 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bashlog 2 | 3 | A simple but powerful logging library for bash 4 | 5 | ## Simple Usage 6 | 7 | Place log.sh in the directory with your bash script(s) 8 | 9 | Add `. log.sh` to the top of your script(s) 10 | 11 | Add logging lines with level info, warn, error or debug, for example: 12 | 13 | * `log info 'My info message';` 14 | 15 | * `log warn 'My warning message';` 16 | 17 | * `log error 'My error message';` 18 | 19 | * `log debug 'My debugging message';` 20 | 21 | 22 | ## Bash Shell Options 23 | 24 | This library sets the following shell options: 25 | 26 | `set -uo pipefail` 27 | 28 | This is a recommended default for any scripts you write: 29 | * You should not use unbound variables (-u) 30 | * You should ensure pipelines fail if any of the commands in the pipeline fail (-o pipefail) 31 | 32 | This library does _not_ `set -e` because automatically exiting a script 33 | when any error is encountered is lazy and a poor user experience. 34 | Errors should be caught and handled by your scripts. 35 | This can be supported by the use of this library's 'error' log-level. 36 | 37 | If you do not like these settings - change them or remove them. 38 | 39 | ## Debugging 40 | 41 | To enable debugging, set `DEBUG=1` in your script's environment. 42 | 43 | * When `DEBUG=0`, log lines with level 'debug' are not printed. 44 | * When `DEBUG=1`, log lines with level 'debug' are printed. 45 | 46 | ### Interactive debugging of errors 47 | 48 | When debugging is enabled, error messages are followed by an interactive 49 | bash shell from which debugging can take place. 50 | 51 | When debugging activity is complete: exiting the shell with code 0 52 | will allow the rest of your script to continue, exiting the shell 53 | with a non-zero code will cause your script to terminate with the 54 | same non-zero code. 55 | 56 | ### Bash Debug Trap 57 | 58 | This library enables a bash DEBUG trap, that provides you with 59 | a variable called `prev_cmd` to help you write simple and useful 60 | debug logging messages. 61 | 62 | For example: 63 | 64 | ``` 65 | my_command --with very -c -o -m -p -l -e -x -- parameters \ 66 | && log debug "${prev_cmd}" \ 67 | || log error "${prev_cmd}; 68 | ``` 69 | 70 | This will log the exact command that was executed as a debug log 71 | (only when `DEBUG=1`). 72 | 73 | If the command fails, the error message will show the precise command 74 | that failed, and when `DEBUG=1` will then present you with a debug 75 | shell from which you can choose to continue or to exit your script. 76 | 77 | #### Advanced Debugging 78 | 79 | This library contains an alternate debug trap that can be uncommented 80 | which will log a debug log entry (when `DEBUG=1`) for every single 81 | command that is executed throughout your script. 82 | 83 | Enabling this alternate trap will severely impact performance of 84 | your script, and the bashlog testing suite will not be applicable. 85 | 86 | ## File, JSON & Syslog 87 | 88 | When instructed, the logging library can also log output: 89 | * To a file in plain text format: `BASHLOG_FILE=1` 90 | * To a file in JSON format: `BASHLOG_JSON=1` 91 | * To Syslog: `BASHLOG_SYSLOG=1` 92 | 93 | ## Additional environment variables 94 | 95 | ### BASHLOG_DATE_FORMAT 96 | 97 | Default: `+%F %T` 98 | 99 | The date format to use for stdout and file logging (passed to the `date` binary). 100 | 101 | Syslog date format is determined by syslog configuration. 102 | 103 | JSON date format is epoch. 104 | 105 | ### BASHLOG_FILE_PATH 106 | 107 | Default: `/tmp/$(basename {0}).log` 108 | 109 | When `BASHLOG_FILE=1`, logs are written to the file at `$BASHLOG_FILE_PATH` 110 | 111 | This defaults to the name of your script (the one from which you sourced this library), 112 | with a `.log` suffix, in the /tmp directory. 113 | 114 | e.g. yourscript.sh will produce `/tmp/yourscript.sh.log` 115 | 116 | ### BASHLOG_JSON_PATH 117 | 118 | Default: `/tmp/$(basename {0}).log.json` 119 | 120 | When `BASHLOG_JSON=1`, logs are written in JSON format to the file at `$BASHLOG_JSON_PATH` 121 | 122 | This defaults to the name of your script (the one from which you sourced this library), 123 | with a `.log.json` suffix, in the /tmp directory. 124 | 125 | e.g. yourscript.sh will produce `/tmp/yourscript.sh.log.json` 126 | 127 | ### BASHLOG_SYSLOG_FACILITY 128 | 129 | Default: `local0` 130 | 131 | When `BASHLOG_SYSLOG=1`, logs are written to Syslog. 132 | 133 | This determines the syslog facility to use (local0 <-> local7) 134 | 135 | ### BASHLOG_SYSLOG_TAG 136 | 137 | Default: `$(basename {0})` 138 | 139 | When BASHLOG_SYSLOG=1, logs are written to Syslog. 140 | 141 | This determines the syslog tag to use, defaulting to the name of your script, 142 | e.g. `yourscript.sh` 143 | 144 | ## Recommended Usage 145 | 146 | ``` 147 | . log.sh; 148 | 149 | cat << 'EOF' > /etc/yum.repos.d/smoo.repo && 150 | [smoo] 151 | name=smoo 152 | baseurl=file:///tmp/smoo/ 153 | enabled=1 154 | gpgcheck=0 155 | EOF 156 | log debug "Smoo repo yum repository configuration written successfully" \ 157 | || log error "Failed to write smoo repo yum repository configuration"; 158 | 159 | 160 | # Install the smoo agent 161 | yum -y install smoo-agent \ 162 | && log info "smoo-agent RPM installed successfully" \ 163 | || log error "Failed to install smoo-agent RPM"; 164 | 165 | # Change into the newly installed repository 166 | cd /srv/smoo \ 167 | && log debug "${prev_cmd}" \ 168 | || log error "${prev_cmd}"; 169 | 170 | /opt/smoo-agent/bin/tests /srv/smoo \ 171 | && log info "Smoo Tests Succeeded" \ 172 | || log error "Smoo Tests Failed"; 173 | 174 | # Execute the smoo-agent 175 | /opt/smoo-agent/bin/smoo \ 176 | -r \ 177 | --config config.json \ 178 | --log_level "info" \ 179 | && log info "Smoo Agent execution completed successfully" \ 180 | || log error "Smoo Agent execution failed!"; 181 | 182 | if [ "${DEBUG:-0}" -eq "1" ]; then 183 | log debug "Entering end-of-script debug shell. Exit shell to continue."; 184 | /bin/bash; 185 | fi; 186 | 187 | log info 'Smoo Agent control finished'; 188 | 189 | exit 0; 190 | ``` 191 | 192 | ## Testing 193 | 194 | A full test suite is provided alongside the bashlog library in `test.sh`. 195 | 196 | The test suite itself has been tested on Slackware Linux. If your syslog 197 | configuration behaves differently to the default in Slackware, the test 198 | suite may fail. Feel free to provide feedback / pull requests to improve it. 199 | 200 | The test suite requires sudo privileges in order to create files that 201 | cannot then be accessed. I could replace this with non-root privilege 202 | modification - and maybe I will - but not today. 203 | 204 | ### Interactive Testing 205 | 206 | If you pass `TEST_INTERACTIVE=1` to test.sh, the automated tests will be 207 | followed by interactive tests which test the debug shell mechanisms. 208 | 209 | User input is required, following instructional prompts in order to 210 | properly validate the functionality. 211 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -uo pipefail; 4 | 5 | # Interactive debug testing is off by default. 6 | # To turn it on, pass TEST_INTERACTIVE=1 7 | # Correct exit code from this test script 8 | # will then be '1' instead of '0' 9 | declare interactive="${TEST_INTERACTIVE:-0}"; 10 | 11 | function result() { 12 | local level="${1}"; 13 | shift; 14 | local line="${@}"; 15 | 16 | case "${level}" in 17 | ok) 18 | echo -e "\t\033[32mOK: ${line}\033[0m"; 19 | ;; 20 | fail) 21 | echo -e "\t\033[31mFAIL: ${line}\033[0m"; 22 | echo -e "Abandoning tests due to failure"; 23 | exit 1; 24 | ;; 25 | *) 26 | echo "UWOTM8?!"; 27 | exit 1; 28 | ;; 29 | esac; 30 | } 31 | 32 | sudo rm -f "/tmp/${0}.log"; 33 | sudo rm -f "/tmp/${0}.log.json"; 34 | 35 | source log.sh; 36 | 37 | declare random_string="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"; 38 | declare stdout; 39 | declare fileout; 40 | declare jsonout; 41 | declare syslogout; 42 | 43 | declare BASHLOG_FILE=1; 44 | declare BASHLOG_JSON=1; 45 | declare BASHLOG_SYSLOG=1; 46 | 47 | ## 48 | # INFO 49 | ## 50 | 51 | echo "Testing 'info'"; 52 | 53 | rm -f "/tmp/${0}.log"; 54 | rm -f "/tmp/${0}.log.json"; 55 | 56 | BASHLOG_FILE=1; 57 | BASHLOG_JSON=1; 58 | BASHLOG_SYSLOG=1; 59 | DEBUG=0; 60 | 61 | stdout="$(log 'info' "${random_string}")"; 62 | fileout="$(tail -n1 /tmp/${0}.log)"; 63 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 64 | syslogout="$(sudo tail -n1 /var/log/messages)"; 65 | 66 | grep -q -E $'^\033\[32m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] '"${random_string}"$'\033\[0m$' <<<"${stdout}" \ 67 | && result ok 'info -> stdout' \ 68 | || result fail 'info -> stdout'; 69 | 70 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] ${random_string}$" <<<"${fileout}" \ 71 | && result ok 'info -> file' \ 72 | || result fail 'info -> file'; 73 | 74 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"info","message":"'"${random_string}"'"}$' <<<"${jsonout}" \ 75 | && result ok 'info -> json file' \ 76 | || result fail 'info -> json file'; 77 | 78 | grep -q -E "$(basename ${0})\[${$}\]: INFO: ${random_string}$" <<<"${syslogout}" \ 79 | && result ok 'info -> syslog (/var/log/messages)' \ 80 | || result fail 'info -> syslog (/var/log/messages)'; 81 | 82 | ## 83 | # WARN 84 | ## 85 | 86 | echo "Testing 'warn'"; 87 | 88 | rm -f "/tmp/${0}.log"; 89 | rm -f "/tmp/${0}.log.json"; 90 | 91 | BASHLOG_FILE=1; 92 | BASHLOG_JSON=1; 93 | BASHLOG_SYSLOG=1; 94 | DEBUG=0; 95 | 96 | stdout="$(log 'warn' "${random_string}")"; 97 | fileout="$(tail -n1 /tmp/${0}.log)"; 98 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 99 | syslogout="$(sudo tail -n1 /var/log/syslog)"; 100 | 101 | grep -q -E $'^\033\[33m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[WARN\] '"${random_string}"$'\033\[0m$' <<<"${stdout}" \ 102 | && result ok 'warn -> stdout' \ 103 | || result fail 'warn -> stdout'; 104 | 105 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[WARN\] ${random_string}$" <<<"${fileout}" \ 106 | && result ok 'warn -> file' \ 107 | || result fail 'warn -> file'; 108 | 109 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"warn","message":"'"${random_string}"'"}$' <<<"${jsonout}" \ 110 | && result ok 'warn -> json file' \ 111 | || result fail 'warn -> json file'; 112 | 113 | grep -q -E "$(basename ${0})\[${$}\]: WARN: ${random_string}$" <<<"${syslogout}" \ 114 | && result ok 'warn -> syslog (/var/log/syslog)' \ 115 | || result fail 'warn -> syslog (/var/log/syslog)'; 116 | 117 | ## 118 | # ERROR 119 | ## 120 | 121 | echo "Testing: 'error'"; 122 | 123 | rm -f "/tmp/${0}.log"; 124 | rm -f "/tmp/${0}.log.json"; 125 | 126 | BASHLOG_FILE=1; 127 | BASHLOG_JSON=1; 128 | BASHLOG_SYSLOG=1; 129 | DEBUG=0; 130 | 131 | stderr="$(log 'error' "${random_string}" 2>&1 1>/dev/null)"; 132 | fileout="$(tail -n1 /tmp/${0}.log)"; 133 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 134 | syslogout="$(sudo tail -n1 /var/log/syslog)"; 135 | 136 | grep -q -E $'^\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] '"${random_string}"$'\033\[0m$' <<<"${stderr}" \ 137 | && result ok 'error -> stderr' \ 138 | || result fail 'error -> stderr'; 139 | 140 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] ${random_string}$" <<<"${fileout}" \ 141 | && result ok 'error -> file' \ 142 | || result fail 'error -> file'; 143 | 144 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"error","message":"'"${random_string}"'"}$' <<<"${jsonout}" \ 145 | && result ok 'error -> json file' \ 146 | || result fail 'error -> json file'; 147 | 148 | grep -q -E "$(basename ${0})\[${$}\]: ERROR: ${random_string}$" <<<"${syslogout}" \ 149 | && result ok 'error -> syslog (/var/log/syslog)' \ 150 | || result fail 'error -> syslog (/var/log/syslog)'; 151 | 152 | ## 153 | # DEBUG OFF 154 | ## 155 | 156 | echo "Testing 'debug', DEBUG=0"; 157 | 158 | rm -f "/tmp/${0}.log"; 159 | rm -f "/tmp/${0}.log.json"; 160 | 161 | BASHLOG_FILE=1; 162 | BASHLOG_JSON=1; 163 | BASHLOG_SYSLOG=1; 164 | DEBUG=0; 165 | 166 | # If there's no output, there'll be no file 167 | touch "/tmp/${0}.log"; 168 | touch "/tmp/${0}.log.json"; 169 | 170 | stdout="$(log 'debug' "${random_string}")"; 171 | fileout="$(tail -n1 /tmp/${0}.log)"; 172 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 173 | syslogout="$(sudo tail -n1 /var/log/debug)"; 174 | 175 | grep -q -E $'^\033\[34m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[DEBUG\] '"${random_string}"$'\033\[0m$' <<<"${stdout}" \ 176 | && result fail 'debug -> stdout' \ 177 | || result ok 'debug -> stdout'; 178 | 179 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[DEBUG\] ${random_string}$" <<<"${fileout}" \ 180 | && result fail 'debug -> file' \ 181 | || result ok 'debug -> file'; 182 | 183 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"debug","message":"'"${random_string}"'"}$' <<<"${jsonout}" \ 184 | && result fail 'debug -> json file' \ 185 | || result ok 'debug -> json file'; 186 | 187 | grep -q -E "$(basename ${0})\[${$}\]: DEBUG: ${random_string}$" <<<"${syslogout}" \ 188 | && result fail 'debug -> syslog (/var/log/debug)' \ 189 | || result ok 'debug -> syslog (/var/log/debug)'; 190 | 191 | ## 192 | # DEBUG ON 193 | ## 194 | 195 | echo "Testing 'debug', DEBUG=1"; 196 | 197 | rm -f "/tmp/${0}.log"; 198 | rm -f "/tmp/${0}.log.json"; 199 | 200 | BASHLOG_FILE=1; 201 | BASHLOG_JSON=1; 202 | BASHLOG_SYSLOG=1; 203 | DEBUG=1; 204 | 205 | stdout="$(log 'debug' "${random_string}")"; 206 | fileout="$(tail -n1 /tmp/${0}.log)"; 207 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 208 | syslogout="$(sudo tail -n1 /var/log/debug)"; 209 | 210 | grep -q -E $'^\033\[34m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[DEBUG\] '"${random_string}"$'\033\[0m$' <<<"${stdout}" \ 211 | && result ok 'debug -> stdout' \ 212 | || result fail 'debug -> stdout'; 213 | 214 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[DEBUG\] ${random_string}$" <<<"${fileout}" \ 215 | && result ok 'debug -> file' \ 216 | || result fail 'debug -> file'; 217 | 218 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"debug","message":"'"${random_string}"'"}$' <<<"${jsonout}" \ 219 | && result ok 'debug -> json file' \ 220 | || result fail 'debug -> json file'; 221 | 222 | grep -q -E "$(basename ${0})\[${$}\]: DEBUG: ${random_string}$" <<<"${syslogout}" \ 223 | && result ok 'debug -> syslog (/var/log/debug)' \ 224 | || result fail 'debug -> syslog (/var/log/debug)'; 225 | 226 | ## 227 | # BAD LEVEL, DEBUG OFF 228 | ## 229 | 230 | echo "Testing: BAD LEVEL ('snooch'), DEBUG=0"; 231 | 232 | rm -f "/tmp/${0}.log"; 233 | rm -f "/tmp/${0}.log.json"; 234 | 235 | BASHLOG_FILE=1; 236 | BASHLOG_JSON=1; 237 | BASHLOG_SYSLOG=1; 238 | DEBUG=0; 239 | 240 | stderr="$(log 'snooch' "${random_string}" 2>&1 1>/dev/null)"; 241 | fileout="$(tail -n1 /tmp/${0}.log)"; 242 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 243 | syslogout="$(sudo tail -n1 /var/log/syslog)"; 244 | 245 | grep -q -E $'^\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Undefined log level trying to log: '"${random_string}"$'\033\[0m$' <<<"${stderr}" \ 246 | && result ok 'snooch -> stderr' \ 247 | || result fail 'snooch -> stderr'; 248 | 249 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Undefined log level trying to log: ${random_string}$" <<<"${fileout}" \ 250 | && result ok 'snooch -> file' \ 251 | || result fail 'snooch -> file'; 252 | 253 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"error","message":"Undefined log level trying to log: '"${random_string}"'"}$' <<<"${jsonout}" \ 254 | && result ok 'snooch -> json file' \ 255 | || result fail 'snooch -> json file'; 256 | 257 | grep -q -E "$(basename ${0})\[${$}\]: ERROR: Undefined log level trying to log: ${random_string}$" <<<"${syslogout}" \ 258 | && result ok 'snooch -> syslog (/var/log/syslog)' \ 259 | || result fail 'snooch -> syslog (/var/log/syslog)'; 260 | 261 | ## 262 | # BAD LEVEL, DEBUG ON 263 | ## 264 | 265 | echo "Testing: BAD LEVEL ('snooch'), DEBUG=1"; 266 | 267 | rm -f "/tmp/${0}.log"; 268 | rm -f "/tmp/${0}.log.json"; 269 | 270 | BASHLOG_FILE=1; 271 | BASHLOG_JSON=1; 272 | BASHLOG_SYSLOG=1; 273 | DEBUG=1; 274 | 275 | stderr="$(echo | log 'snooch' "${random_string}" 2>&1 1>/dev/null)"; 276 | fileout="$(tail -n1 /tmp/${0}.log)"; 277 | jsonout="$(tail -n1 /tmp/${0}.log.json)"; 278 | syslogout="$(sudo tail -n1 /var/log/syslog)"; 279 | 280 | grep -q -E $'^\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Undefined log level trying to log: '"${random_string}"$'\033\[0m$' <<<"${stderr}" \ 281 | && result ok 'snooch -> stderr' \ 282 | || result fail 'snooch -> stderr'; 283 | 284 | grep -q -E "^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Undefined log level trying to log: ${random_string}$" <<<"${fileout}" \ 285 | && result ok 'snooch -> file' \ 286 | || result fail 'snooch -> file'; 287 | 288 | grep -q -E '^{"timestamp":"[0-9]{10}","level":"error","message":"Undefined log level trying to log: '"${random_string}"'"}$' <<<"${jsonout}" \ 289 | && result ok 'snooch -> json file' \ 290 | || result fail 'snooch -> json file'; 291 | 292 | grep -q -E "$(basename ${0})\[${$}\]: ERROR: Undefined log level trying to log: ${random_string}$" <<<"${syslogout}" \ 293 | && result ok 'snooch -> syslog (/var/log/syslog)' \ 294 | || result fail 'snooch -> syslog (/var/log/syslog)'; 295 | 296 | ## 297 | # INFO, FILE IO EXCEPTION, DEBUG OFF 298 | ## 299 | 300 | echo "Testing: 'info', IO Exception (file), DEBUG=0"; 301 | 302 | rm -f "/tmp/${0}.log"; 303 | 304 | BASHLOG_FILE=1; 305 | BASHLOG_JSON=0; 306 | BASHLOG_SYSLOG=0; 307 | DEBUG=0; 308 | 309 | sudo touch "/tmp/${0}.log"; 310 | 311 | stderr="$(log 'info' "${random_string}" 2>&1 1>/dev/null)"; 312 | 313 | sudo rm -f "/tmp/${0}.log"; 314 | 315 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log: Permission denied' <<<"${stderr}" \ 316 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] '"${random_string}"'" >> "/tmp/'"$(basename ${0})"$'.log"\033\[0m$' <<<"${stderr}" \ 317 | && result ok 'info -> file, Permission denied -> stderr' \ 318 | || result fail 'info -> file, Permission denied -> stderr'; 319 | 320 | ## 321 | # INFO, FILE IO EXCEPTION, DEBUG ON 322 | ## 323 | 324 | echo "Testing: 'info', IO Exception (file), DEBUG=1"; 325 | 326 | rm -f "/tmp/${0}.log"; 327 | 328 | BASHLOG_FILE=1; 329 | BASHLOG_JSON=0; 330 | BASHLOG_SYSLOG=0; 331 | DEBUG=1; 332 | 333 | sudo touch "/tmp/${0}.log"; 334 | 335 | stderr="$(echo | log 'info' "${random_string}" 2>&1 1>/dev/null)"; 336 | 337 | sudo rm -f "/tmp/${0}.log"; 338 | 339 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log: Permission denied' <<<"${stderr}" \ 340 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[INFO\] '"${random_string}"'" >> "/tmp/'"$(basename ${0})"$'.log"\033\[0m$' <<<"${stderr}" \ 341 | && result ok 'info -> file, Permission denied -> stderr' \ 342 | || result fail 'info -> file, Permission denied -> stderr'; 343 | 344 | ## 345 | # INFO, JSON FILE IO EXCEPTION, DEBUG OFF 346 | ## 347 | 348 | echo "Testing: 'info', IO Exception (json), DEBUG=0"; 349 | 350 | rm -f "/tmp/${0}.log.json"; 351 | 352 | BASHLOG_FILE=0; 353 | BASHLOG_JSON=1; 354 | BASHLOG_SYSLOG=0; 355 | DEBUG=0; 356 | 357 | sudo touch "/tmp/${0}.log.json"; 358 | 359 | stderr="$(log 'info' "${random_string}" 2>&1 1>/dev/null)"; 360 | 361 | sudo rm -f "/tmp/${0}.log.json"; 362 | 363 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log.json: Permission denied' <<<"${stderr}" \ 364 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "{"timestamp":"[0-9]{10}","level":"info","message":"'"${random_string}"'"}" >> "/tmp/'"$(basename ${0})"$'.log.json"\033\[0m$' <<<"${stderr}" \ 365 | && result ok 'info -> file, Permission denied -> stderr' \ 366 | || result fail 'info -> file, Permission denied -> stderr'; 367 | 368 | ## 369 | # INFO, JSON FILE IO EXCEPTION, DEBUG ON 370 | ## 371 | 372 | echo "Testing: 'info', IO Exception (json), DEBUG=1"; 373 | 374 | rm -f "/tmp/${0}.log.json"; 375 | 376 | BASHLOG_FILE=0; 377 | BASHLOG_JSON=1; 378 | BASHLOG_SYSLOG=0; 379 | DEBUG=1; 380 | 381 | sudo touch "/tmp/${0}.log.json"; 382 | 383 | stderr="$(echo | log 'info' "${random_string}" 2>&1 1>/dev/null)"; 384 | 385 | sudo rm -f "/tmp/${0}.log.json"; 386 | 387 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log.json: Permission denied' <<<"${stderr}" \ 388 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "{"timestamp":"[0-9]{10}","level":"info","message":"'"${random_string}"'"}" >> "/tmp/'"$(basename ${0})"$'.log.json"\033\[0m$' <<<"${stderr}" \ 389 | && result ok 'info -> file, Permission denied -> stderr' \ 390 | || result fail 'info -> file, Permission denied -> stderr'; 391 | 392 | ## 393 | # WARN, FILE IO EXCEPTION, DEBUG OFF 394 | ## 395 | 396 | echo "Testing: 'warn', IO Exception (file), DEBUG=0"; 397 | 398 | rm -f "/tmp/${0}.log"; 399 | 400 | BASHLOG_FILE=1; 401 | BASHLOG_JSON=0; 402 | BASHLOG_SYSLOG=0; 403 | DEBUG=0; 404 | 405 | sudo touch "/tmp/${0}.log"; 406 | 407 | stderr="$(log 'warn' "${random_string}" 2>&1 1>/dev/null)"; 408 | 409 | sudo rm -f "/tmp/${0}.log"; 410 | 411 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log: Permission denied' <<<"${stderr}" \ 412 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[WARN\] '"${random_string}"'" >> "/tmp/'"$(basename ${0})"$'.log"\033\[0m$' <<<"${stderr}" \ 413 | && result ok 'warn -> file, Permission denied -> stderr' \ 414 | || result fail 'warn -> file, Permission denied -> stderr'; 415 | 416 | ## 417 | # ERROR, FILE IO EXCEPTION, DEBUG OFF 418 | ## 419 | 420 | echo "Testing: 'error', IO Exception (file), DEBUG=0"; 421 | 422 | rm -f "/tmp/${0}.log"; 423 | 424 | BASHLOG_FILE=1; 425 | BASHLOG_JSON=0; 426 | BASHLOG_SYSLOG=0; 427 | DEBUG=0; 428 | 429 | sudo touch "/tmp/${0}.log"; 430 | 431 | stderr="$(log 'error' "${random_string}" 2>&1 1>/dev/null)"; 432 | 433 | sudo rm -f "/tmp/${0}.log"; 434 | 435 | grep -q -E $'^./log.sh: line [0-9]+: /tmp/'"$(basename ${0})"'.log: Permission denied' <<<"${stderr}" \ 436 | && grep -q -E $'\033\[31m[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] Logging Exception: echo -e "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[ERROR\] '"${random_string}"'" >> "/tmp/'"$(basename ${0})"$'.log"\033\[0m$' <<<"${stderr}" \ 437 | && result ok 'error -> file, Permission denied -> stderr' \ 438 | || result fail 'error -> file, Permission denied -> stderr'; 439 | 440 | ## 441 | # INTERACTIVE DEBUG 442 | ## 443 | 444 | if [ "${interactive}" -eq 1 ]; then 445 | 446 | echo "Testing: Debug Interaction, DEBUG=1"; 447 | 448 | BASHLOG_FILE=0; 449 | BASHLOG_JSON=0; 450 | BASHLOG_SYSLOG=0; 451 | DEBUG=1; 452 | 453 | echo -e "\n\t\033[32mLogging a normal successful debug message to stdout\033[0m"; 454 | log 'debug' 'A normal successful debug message'; 455 | 456 | echo -e "\n\t\033[32mLogging an error message to stdout that should provide a debug shell\n\tExit the shell with ^D, 'exit 0' or 'exit'.\n\tIf you exit with a non-zero code, testing will be abandoned, this script will exit without further warning.\033[0m"; 457 | log 'error' 'An error message'; 458 | 459 | result ok 'Interactive Shell. We have errored and continued.'; 460 | 461 | echo -e "\n\t\033[32mLogging an error message to stdout that should provide a debug shell\n\tExit the shell with 'exit 1' or a non-zero code of your choice.\n\tThe test is successful if this script exits with the same code\033[0m"; 462 | log 'error' 'An error message'; 463 | 464 | result fail 'Interactive Shell. This script should have exited with a non-zero code'; 465 | 466 | fi; 467 | 468 | exit 0; 469 | --------------------------------------------------------------------------------