Last active
May 13, 2025 22:33
-
-
Save MstWntd/646429e25d8f5fa713792e716dcd9de1 to your computer and use it in GitHub Desktop.
Python and BASH script templates, focused on debugging with pdb, logging, error capture. improvements are welcome.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# vim:fenc=utf-8 | |
""" | |
Author : %USER% | |
Email : %MAIL% | |
Version : 0.1 | |
Created : %FDATE% | |
Last Modified: | |
Host Machine : %HOST% | |
Description : %HERE% | |
""" | |
import argparse, logging, os, sys | |
def exc_hndlr(etype, value, tb): | |
logger.critical( | |
"Uncaught exception: {0}".format(str(value)), | |
exc_info=(etype, value, tb)) | |
if not args.stdo: | |
import traceback | |
traceback.print_exception(etype, value, tb) | |
if os.isatty(1) and os.isatty(2): | |
import pdb | |
print('') | |
pdb.post_mortem(tb) | |
def parse_args(): | |
def _str2bool(v): | |
if v.lower() in ('yes', 'true', 't', 'y', '1'): | |
return True | |
elif v.lower() in ('no', 'false', 'f', 'n', '0'): | |
return False | |
else: | |
raise argparse.ArgumentTypeError('Boolean value expected.') | |
parser = argparse.ArgumentParser() | |
parser.add_argument( | |
'--debug', | |
nargs='?', | |
const=1, | |
type=_str2bool, | |
default=False, | |
help='Debug Mode') | |
parser.add_argument( | |
'--cprofile', | |
nargs='?', | |
const=1, | |
type=_str2bool, | |
default=False, | |
help='Run main with cProfile') | |
parser.add_argument( | |
'--stdo', | |
nargs='?', | |
const=1, | |
type=bool, | |
default=True, | |
help='Enable output to console') | |
parser.add_argument( | |
'--pdb', | |
nargs='?', | |
const=1, | |
type=bool, | |
default=False, | |
help='Enable PDB') | |
parser.add_argument( | |
'--syslog', | |
nargs='?', | |
const=1, | |
type=bool, | |
default=False, | |
help='Enable output to syslog') | |
parser.add_argument( | |
'--logdir', | |
nargs='?', | |
const=1, | |
type=str, | |
default='/x/a/core/logs/', | |
help='set log dir, default is /x/a/core/logs/') | |
return parser.parse_known_args() | |
def logger_init(logname=os.path.basename(__file__)[:-3]): | |
from logging import Formatter | |
import time | |
if args.debug: | |
loglvl = logging.DEBUG | |
fmt = '%(asctime)s\t%(levelname)s\t%(name)s\t%(filename)s::%(funcName)s():%(lineno)-3s\t%(message)s' | |
else: | |
loglvl = logging.INFO | |
fmt = '%(asctime)s\t%(levelname)s\t%(message)s' | |
logging.Formatter.converter = time.gmtime | |
logging.Formatter.default_msec_format = '%s.%03d' | |
logdir = "./logs/" if not os.path.isdir(args.logdir) else args.logdir | |
app_log = "%s/%s.log" % (logdir, logname) | |
if not os.path.exists(logdir): | |
os.makedirs(logdir) | |
logging.basicConfig(filename=app_log, level=loglvl, format=fmt) | |
global logger | |
logger = logging.getLogger() | |
if args.stdo: | |
console = logging.StreamHandler(sys.stdout) | |
console.setLevel(loglvl) | |
if os.isatty(1) and os.isatty(2): | |
from copy import copy | |
class ColoredFormatter(Formatter): | |
RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(31,38) | |
MAPPING = { | |
'WARNING' : YELLOW, | |
'INFO' : CYAN, | |
'DEBUG' : BLUE, | |
'CRITICAL' : RED+10, | |
'ERROR' : RED, | |
} | |
PREFIX = '\033[' | |
RESET = '\033[0m' | |
def __init__(self, pattern): | |
Formatter.__init__(self, pattern) | |
def format(self, record): | |
colored_record = copy(record) | |
levelname = colored_record.levelname | |
seq = self.MAPPING.get(levelname, self.WHITE) | |
colored_levelname = ('{0}1m{0}{1}m{2}{3}') \ | |
.format(self.PREFIX, seq, levelname, self.RESET) | |
colored_record.levelname = colored_levelname | |
return Formatter.format(self, colored_record) | |
cf = ColoredFormatter(fmt) | |
console.setFormatter(cf) | |
else: | |
console.setFormatter(Formatter(fmt)) | |
logger.addHandler(console) | |
if args.pdb: | |
import pdb | |
def info(tye, value, tb): | |
if os.isatty(1) and os.isatty(2): | |
logger.info('pdb triggered..') | |
import traceback | |
traceback.print_exception(tye, value, tb) | |
pdb.post_mortem(tb) | |
else: | |
logger.error('Cant start PDB without TTY') | |
if os.isatty(1) and os.isatty(2): | |
sys.excepthook = info | |
pdb.set_trace() | |
else: | |
logger.error('Cant start PDB without TTY') | |
if args.syslog: | |
from logging.handlers import SysLogHandler | |
syslog = SysLogHandler(address='/dev/log', facility=SysLogHandler.LOG_LOCAL5) | |
syslog.setLevel(loglvl) | |
syslog.setFormatter(logging.Formatter(fmt)) | |
logger.addHandler(syslog) | |
def main(): | |
logger.info('Hi..') | |
if __name__ == "__main__": | |
args, unknown_args = parse_args() | |
logger_init() | |
sys.excepthook = exc_hndlr | |
if args.cprofile: | |
import cProfile | |
cProfile.run('main()', sort='cumtime') | |
else: | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /bin/bash | |
: <<MISC | |
Author : %USER% | |
Email : %MAIL% | |
Version : 0.1 | |
Created : %FDATE% | |
Last Modified: | |
Host Machine : %HOST% | |
Description : %HERE% | |
MISC | |
declare -A colours; | |
colours=(\ | |
['black']='\E[0;47m'\ | |
['red']='\E[0;31m'\ | |
['error']='\E[0;31m'\ | |
['green']='\E[0;32m'\ | |
['yellow']='\E[0;33m'\ | |
['debug']='\E[0;33m'\ | |
['blue']='\E[0;34m'\ | |
['info']='\E[0;34m'\ | |
['magenta']='\E[0;35m'\ | |
['cyan']='\E[0;36m'\ | |
['white']='\E[0;37m'\ | |
['reset']='\E[0;00m'\ | |
) | |
_log(){ | |
local lvl="${1^^}" | |
local msg=$2 | |
(test "$debug" == "0" && test "$lvl" == "DEBUG") && return 0 | |
test -t 1 && lvl="${colours[${lvl,,}]}${lvl}${colours['reset']}" | |
if [[ "$lvl" == "DEBUG" || "$lvl" == "ERROR" ]];then | |
(>&2 echo -e "$(date -u +"%Y-%m-%d %H:%M:%S.%3N %z")\t${lvl}\t${msg}") | |
else | |
echo -e "$(date -u +"%Y-%m-%d %H:%M:%S.%3N %z")\t${lvl}\t${msg}" | |
fi | |
} | |
show_help(){ | |
cat << HELP | |
Usage: ${BASH_SOURCE[0]} [options] | |
options: | |
-h, --help Show this help. | |
--test Enable test mode. | |
--debug [012] Enable debug logging. | |
HELP | |
} | |
parse_args() | |
{ | |
if [[ "$#" == "0" ]];then | |
show_help | |
exit 0 | |
fi | |
while [[ $# -gt 0 ]]; do | |
opt="$1" | |
shift | |
case "$opt" in | |
-h|\?|--help) | |
show_help | |
exit 0 | |
;; | |
--test) | |
test="1" | |
;; | |
--debug) | |
debug="${1-1}" | |
if [[ "$debug" -gt "1" ]];then | |
export PS4=" \\D{%Y-%m-%d %T %z}\\011debug$debug\\011\${BASH_SOURCE}:\${LINENO}(\${FUNCNAME[0]})\\011" | |
set -xv | |
fi | |
if [[ ! -z "$1" && "$1" != "--"* ]];then | |
shift | |
fi | |
;; | |
esac | |
done | |
[[ "$test" == "1" ]] && _log "info" "Test mode enabled" | |
return 0 | |
} | |
main(){ | |
_log "info" "hello.." | |
} | |
catch(){ | |
local xcode=$? | |
local bcmd="$BASH_COMMAND" | |
if [[ "$xcode" != "0" && "$bcmd" != 'return ' ]];then | |
_log "error" "CMD: $(whoami)@$HOSTNAME:$0 $args" | |
_log "error" "PWD: $PWD" | |
_log "error" "SUDO_USER: $SUDO_USER" | |
_log "fatal" "code: $xcode" | |
_log "fatal" "[$( caller )] $*" | |
_log "fatal" "BASH_SOURCE: ${BASH_SOURCE[*]}" | |
_log "fatal" "BASH_LINENO: ${BASH_LINENO[*]}" | |
_log "fatal" "FUNCNAME: ${FUNCNAME[*]}" | |
fi | |
} | |
set -E | |
trap catch ERR | |
test=0 | |
debug=1 | |
xdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | |
args=$@ | |
parse_args $args | |
main |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I use vim-template to populate the templates straight into my vim instance.