diff options
| author | 2026-01-14 22:22:22 +0100 | |
|---|---|---|
| committer | 2026-01-15 06:37:04 +0100 | |
| commit | 74e70ef6e33cc57a1077892deccf6424199ac7ab (patch) | |
| tree | 3ba24598a6bb4226b188f060154092d0db4313b0 /log.sh | |
initial importshlib
Diffstat (limited to 'log.sh')
| -rw-r--r-- | log.sh | 204 |
1 files changed, 204 insertions, 0 deletions
@@ -0,0 +1,204 @@ +#!/bin/sh +# this file is licensed under any of the following SPDX licenses +# to be chosen by the user: +# * 0BSD (https://spdx.org/licenses/0BSD.html) +# * BlueOak-1.0.0 (https://blueoakcouncil.org/license/1.0.0) +# * CC0-1.0 (https://creativecommons.org/publicdomain/zero/1.0/) +# * Unlicense (https://unlicense.org/) + +## log.sh - Bunker Log +# This is the bunker logging system for POSIX sh. +# It provides you with stack-based logging semantics, +# and includes advances features like stdin-forwarding. +# It should not leak any environment variables, +# besides those documented, and potentially `add` on buggy shells. +# Everything here should be POSIXLY correct. +# If it isn't, let me know! <toast (at) bunkerlabs.net> + +## Usage +# Call `log some text here` or `something_with_output | log_stdin`. +# It will then be formatted under the current log tree. +# +# You can manipulate the log tree using log_push and log_pop. +# log_push can have multiple levels pushed on it at once. +# For example, `log_push one two` from the default tree state will result +# in the new tree being blog/one/two. +# log_pop can pop multiple levels at once. To undo the above example, you could +# call `log_pop 2` instead of calling log_pop twice. +# +# Whenever the log tree changes, the next call to `log` or `log_stdin` will +# cause the state of the log tree to be printed in full. + +## Customizing +# You can customize the "root" node of the log tree by setting log_tree to a +# single word (no whitespace as per IFS) before you source this. +# You can set a hard limit to the maximum length of any log level by setting +# log_maxlen. This will truncate log levels when they are pushed. +# You can also force padding on the output by setting log_minlen. +# If log_minlen is negative, it will right-pad, like with printf. +# You can change the way truncation is done by changing log_tstyles, +# which is a space-delineated list of strategies. + +## API Summary +# The following is a summary of all user-facing functions and environment +# variables. Functions are denoted with `(args...)`s. +# * log(msg...): log a message +# * log_stdin(): log lines from stdin; do not use on interactive programs +# * log[_stdin](v|q): log only if VERBOSE is set / QUIET is not set +# * log_push(level...): push levels on the log tree +# * log_pop(amount?): pop levels off the log tree; amount defaults to 1 +# * log_shift(level...): push levels on the log tree after popping that amount +# | equivalent to `log_pop $#; log_push "$@"` +# | with word splitting +# * log_reset(): pop all levels except the root +# * log_tree: initial state of the log tree, set this before sourcing this file +# | to set the default (not removable) log level +# * log_minlen: the minimum length of a log level. Levels that are too short +# | will be space-padded. Padding inserted to the right if negative +# * log_maxlen: the maximum length of a log level. Levels that are too long +# | will be truncated + +# you can initialize your "root" to anything by setting log_tree before +# you source this +# the default is "blog" - bunker log +: ${log_tree:=blog} +log_indent=0 + +# shadow tree, used to calculate indent +# if you push, don't log, and then pop, you shadow tree will = your tree +# => no need to recalculate indents +# in short, if shadow tree doesn't match the tree, +# the indent is recalculated at log-time +log_stree=$log_tree + +# this is a function that allows for selecting different styles of truncation +# $log_tstyles will be tried in a row until the result fits +# as such, it's highly recommended the final option be a terminal one +# available styles, with *s being terminal: +# * cut*: truncates rightwards +# * rcut*: truncates leftwards +# * vowels: removes all vowels +# * numbers: remove all numbers +# * noalnum: remove everything other than alphanumerics +: ${log_tstyles:=cut} +log_truncate() ( + set -- "$*" "$(printf "$*" | wc -c)" \ + "$(echo "$log_tstyles" | cut -d' ' -f1)" + if [ "${log_maxlen:-$2}" -ge "$2" ]; then + echo "$1" + return 0 + fi + case "$3" in + cut) + echo "$1" | cut -c 1-"${log_maxlen:-$2}" + return 0 + ;; + rcut) + start=$(( $2 - ${log_maxlen:-$2} + 1 )) + [ "$start" -lt 1 ] && start=1 + echo "$1" | cut -c "$start"- + return 0 + ;; + vowels) + set -- "$(echo "$1" | sed -e 's/[aeiou]//g')" + ;; + numbers) + set -- "$(echo "$1" | sed -e 's/[0-9]//g')" + ;; + noalnum) + set -- "$(echo "$1" | sed -e 's/[^0-9a-zA-Z]//g')" + ;; + *) return 1 ;; + esac + # goto next method + log_tstyles=$(echo "$log_tstyles" | cut -d' ' -f2-) + log_truncate "$1" +) + +# you can push multiple levels at once +# if a level has embedded spaces, it counts for multiple levels +# all words will be truncated to $log_maxlen, but only if it's set +log_push() { + # do word splitting in case of extra embedded " "s + set -- "$*" + for add in $1; do + # truncate to log_maxlen, if it's set + set -- "$@" "$(log_truncate "$add")" + done + shift + log_tree="$log_tree $@" +} + +# you can pop multiple levels at once, $1 is number to pop +log_pop() { + # default to popping 1 + # save the number of words in the log tree + set -- ${1:-1} $(echo "$log_tree" | wc -w) + + # never pop the last word + if [ $1 -ge $2 ]; then + set -- $(( $2 - 1 )) $2 + fi + log_tree=$(echo "$log_tree" | cut -d' ' -f 1-$(( $2 - $1 )) ) +} + +log_shift() { + # perform word splitting + set -- "$*" + set -- $1 + + log_pop $# + log_push "$@" +} + +log_reset() { + set -- $(echo "$log_tree" | wc -w) + log_pop $(( $1 - 1 )) +} + +# this will read stdin line by line +# to integrate external commands into the log +log_stdin() { + while read -r line; do + log "$line" + done +} + +# echo "$*", but with +# every level will be padded to $log_minlen, if it's set +# you can set it to a negative number to right-pad it +log() { + # we need to recalculate the indent + if [ "$log_indent" -eq 0 ] || [ "$log_tree" != "$log_stree" ]; then + # update the shadow tree and calculate the indentation level + log_stree=$log_tree + log_indent=$(printf "%${log_minlen}s/" $log_tree | wc -c) + + # prefix printing + printf "%${log_minlen}s/" $log_tree + printf "\b: " + else + # indent up to the indent, but print a | in place of the : + # the 2 is because the above has an off-by-one, and this is easier + printf ' %.0s' $(seq 2 "$log_indent") + printf '| ' + fi + echo "$*" +} >&2 + +# *v and *q variants +logv() { + [ -n "$VERBOSE" ] && log "$@" +} + +logq() { + [ -z "$QUIET" ] && log "$@" +} + +log_stdinv() { + [ -n "$VERBOSE" ] && log_stdin "$@" +} + +log_stdinq() { + [ -z "$QUIET" ] && log_stdin "$@" +} |
