diff --git a/README.md b/README.md index a53b364..1636dd9 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,16 @@ **simpleLog4sh**是一个极为简单的shell日志框架,甚至不应该叫日志框架。 与其他shell日志框架不同,它仅仅是一个: -**只有几十行的纯shell脚本,没有任何依赖**。 +**只有几百行的纯shell脚本,没有任何依赖**。 但通过简单的封装,这个小shell可以提供诸如: 1. logInfo,logDebug等多级别日志输出 -2. 日志记录带有时间戳和日志级别 +2. 日志记录带有时间戳和日志级别,并可控制输出日志的级别 3. 将日志输出到指定文件 4. 日志文件按日期进行归档 5. 抛出异常 +6. 自动清理日志 +7. 接管stdout/stederr **simpleLog4sh**不期望做到像Apache日志框架一样复杂,仅仅在您写toy小程序的时候,能帮助你实现最简单的日志功能,而不必总是echo。 @@ -35,6 +37,11 @@ logDebug "hello, world" # 推荐将所有的内容用双引号包围 . ../src/simplelog4sh.sh ``` +如果需要覆盖默认配置,可提供配置文件 +``` +. ../src/simpleLog4sh.source ../src/simpleLog4sh.cfg +``` + ## logXXX语句 simpleLog4sh提供了四个日志级别的方法,他们分别是 1. `logDebug` diff --git a/src/simpleLog4sh.cfg b/src/simpleLog4sh.cfg index 9f72358..3b7566b 100755 --- a/src/simpleLog4sh.cfg +++ b/src/simpleLog4sh.cfg @@ -1,22 +1,30 @@ -# simpleLog4sh配置文件 +# simpleLog4sh configuration file -# 日志文件存储的根目录(默认为/tmp/simpleLog4sh) -simpleLog4sh_LOG_DIR="/tmp/simpleLog4sh-logs" +# root directory for log files (deafult: /tmp/simpleLog4sh) +# simpleLog4sh_LOG_DIR="/tmp/simpleLog4sh" -# 日志文件的前缀(默认为simpleLog4sh) -simpleLog4sh_LOG_FILE_NAME_PREFIX="simpleLog" +# prfeix name of log files (default: simpleLog4sh) +# simpleLog4sh_LOG_FILE_NAME_PREFIX="simpleLog4sh" -# 日志级别,可以设置为 ALL TRACE DEBUG INFO WARN ERROR OFF(默认为ALL) +# control the maximum number (in days) of archive files to keep (default: -1 to keep all) +# e.g. if simpleLog4sh_MAX_HISTORY=3 and today is Aug 8th, Aug 5-7th's log will be kept. +# AKA log files on and before Aug 4th will be deleted. +# If you don't want to delete old log files automatically, set as a negative number such as -1 +# simpleLog4sh_MAX_HISTORY=-1 + +# log level: ALL TRACE DEBUG INFO WARN ERROR OFF (default: ALL) # simpleLog4sh_LOG_LEVEL="ALL" -# 日志文件的后缀(默认为.log) +# suffix name of log files (default: .log) # simpleLog4sh_LOG_FILE_NAME_SUFFIX=".log" -# 标准输出重定向文件的后缀,空表示不重定向标准输出(默认为空) +# suffix name of stdout log (default: "") +# keep it empty if you don't want to redirect stdout # simpleLog4sh_LOG_STDOUT_SUFFIX=".out" -# 标准错误输出重定向文件的后缀,空表示不重定向(默认为空) +# suffix name of stderr log (default: "") +# keep it empty if you don't want to redirect stderr # simpleLog4sh_LOG_STDERR_SUFFIX=".err" -# 是否显示完成的函数调用路径(默认为false) -simpleLog4sh_LOG_FULL_CALLPATH="true" +# show full call path of functions (default: false) +# simpleLog4sh_LOG_FULL_CALLPATH="false" \ No newline at end of file diff --git a/src/simpleLog4sh.source b/src/simpleLog4sh.source index e2dc5bd..0b220e2 100755 --- a/src/simpleLog4sh.source +++ b/src/simpleLog4sh.source @@ -1,49 +1,117 @@ #--------------- -# shell日志工具,提供类似log4j的日志级别功能 -# +# Simple log for shell # author: maoshuai #--------------- -# 避免被多次导入 +# source only once if [ "${_simpleLog4sh_sourced}" ]; then return fi -export _simpleLog4sh_sourced=1 +_simpleLog4sh_sourced=1 + + +####################### methods for client code + +# refresh log name, client can call this method to switch log name +refreshLogger(){ + export LOGGER_NAME="$1" + setStd +} + +# print error message and exit +throw() +{ + if [ $# -ne 0 ];then + logEchoError "$@" + logError "$@" + fi + exit 1 +} + +# keep an copy of echo message +logEcho() +{ + echo "$@" + _doLog "ECHO " $simpleLog4sh_LOG_LEVEL_NUM_OFF "$@" + +} + +# keep an copy of error message +logEchoError() +{ + echo "(ERROR)" "$@" >&2 + _doLog "ECHO_ERROR" $simpleLog4sh_LOG_LEVEL_NUM_OFF "$@" +} + +logTrace() +{ + _doLog "TRACE" $simpleLog4sh_LOG_LEVEL_NUM_TRACE "$@" +} + +logDebug() +{ + _doLog "DEBUG" $simpleLog4sh_LOG_LEVEL_NUM_DEBUG "$@" +} + +logInfo() +{ + _doLog "INFO " $simpleLog4sh_LOG_LEVEL_NUM_INFO "$@" +} + +logWarn() +{ + _doLog "WARN" $simpleLog4sh_LOG_LEVEL_NUM_WARN "$@" +} + +logError() +{ + _doLog "ERROR" $simpleLog4sh_LOG_LEVEL_NUM_ERROR "$@" +} + +# +############################## simpleLog4sh_main(){ - # 设置默认配置 - # 日志文件存储的根目录 + # setting default configurations: + # root directory for log files: simpleLog4sh_LOG_DIR="/tmp/simpleLog4sh" - # 日志级别 + # log level simpleLog4sh_LOG_LEVEL="ALL" - # 日志文件前缀 + # prfeix name of log files simpleLog4sh_LOG_FILE_NAME_PREFIX="simpleLog4sh" - # 日志文件后缀 + # control the maximum number (in days) of archive files to keep (default: -1, keep all) + simpleLog4sh_MAX_HISTORY=-1 + # suffix name of log files simpleLog4sh_LOG_FILE_NAME_SUFFIX=".log" - # 标准输出重定向,为空表示不重定向 + # suffix name of stdout log + # keep it empty if you don't want to redirect stdout simpleLog4sh_LOG_STDOUT_SUFFIX="" - # 标准错误输出重定向,为空表示不重定向 + # suffix name of stderr log + # keep it empty if you don't want to redirect stderr simpleLog4sh_LOG_STDERR_SUFFIX="" - # 是否显示完成的函数调用路径(默认为false) + # show full call path of functions simpleLog4sh_LOG_FULL_CALLPATH="false" + # load configuration file if it is provided. cfgFile=$1 - - - # 运行用户自定义配置,配置文件需与shell同目录,并且名称为simpleLog4sh.cfg if [ x"$cfgFile" != "x" ];then if [ -f $cfgFile ];then . $cfgFile fi fi + # loading configuration is complete now + # ensure the output directory is writable + if [ -d $simpleLog4sh_LOG_DIR ];then + if [ ! -w $simpleLog4sh_LOG_DIR ];then + echo "simpleLog4sh_LOG_DIR is not writeable directory: $simpleLog4sh_LOG_DIR" >&2 + exit 1 + fi + fi - # 到此为止配置已经结束 - - - # 日志级别常量,大于这个常量的才会打印 + # log level number simpleLog4sh_LOG_LEVEL_NUM_ALL=-9000 simpleLog4sh_LOG_LEVEL_NUM_TRACE=50 simpleLog4sh_LOG_LEVEL_NUM_DEBUG=100 @@ -52,17 +120,7 @@ simpleLog4sh_main(){ simpleLog4sh_LOG_LEVEL_NUM_ERROR=400 simpleLog4sh_LOG_LEVEL_NUM_OFF=9000 - # 配置校验开始 - # 日志目录的校验,目录存在必须保证可写 - if [ -d $simpleLog4sh_LOG_DIR ];then - if [ ! -w $simpleLog4sh_LOG_DIR ];then - echo "simpleLog4sh_LOG_DIR is not writeable directory: $simpleLog4sh_LOG_DIR" >&2 - exit 1 - fi - fi - - # 日志级别的转换 - # 配置文件中的日志级别字符串,转换为数字 + # interpret the human readable log level name to correspondent log level number if [ x$simpleLog4sh_LOG_LEVEL = x"ALL" ];then simpleLog4sh_LOG_LEVEL_NUM=$simpleLog4sh_LOG_LEVEL_NUM_ALL elif [ x$simpleLog4sh_LOG_LEVEL = x"TRACE" ];then @@ -77,40 +135,39 @@ simpleLog4sh_main(){ simpleLog4sh_LOG_LEVEL_NUM=$simpleLog4sh_LOG_LEVEL_NUM_ERROR elif [ x$simpleLog4sh_LOG_LEVEL = x"OFF" ];then simpleLog4sh_LOG_LEVEL_NUM=$simpleLog4sh_LOG_LEVEL_NUM_OFF - else # 默认打印所有日志 + else + # by default, log level is ALL simpleLog4sh_LOG_LEVEL_NUM=$simpleLog4sh_LOG_LEVEL_NUM_ALL fi - # 默认的log名 + # setting default log name, which may be override lately defaultLoggerName="root" + # redirect stdout and stderr setStd + # remove old logs + _log_removeStaleLogs + } +# redirect stdout and stderr setStd(){ - # 设置重定向 if [ x"$simpleLog4sh_LOG_STDOUT_SUFFIX" != "x" ];then exec >>$(_getCurrentStdOutFile) fi - # 设置重定向 if [ x"$simpleLog4sh_LOG_STDERR_SUFFIX" != "x" ];then exec 2>>$(_getCurrentStdErrFile) fi } -# 刷新logger名 -refreshLogger(){ - export LOGGER_NAME="$1" - setStd -} -# 获取当前日志文件的地址 -# 每天滚动一个日志文件 + +# get log file name by date _getCurrentLogFile() { local logDate=$(date +"%Y%m%d") @@ -136,8 +193,7 @@ _getLoggerName(){ -# 获取当前stdErr文件的地址 -# 每天滚动一个日志文件 +# get stdout log file name by date _getCurrentStdOutFile() { local logDate=$(date +"%Y%m%d") @@ -152,8 +208,7 @@ _getCurrentStdOutFile() echo $todayLogFile } -# 获取当前stdErr文件的地址 -# 每天滚动一个日志文件 +# get stederr log file name by date _getCurrentStdErrFile() { local logDate=$(date +"%Y%m%d") @@ -168,7 +223,7 @@ _getCurrentStdErrFile() echo $todayLogFile } -# get call function +# get call function path _getCallPath(){ if [ "x$simpleLog4sh_LOG_FULL_CALLPATH" == "xtrue" ];then local len=${#FUNCNAME[@]} @@ -189,74 +244,44 @@ _getCallPath(){ } +# inner methdo called by all logXXX function _doLog(){ local levelName="$1" local levelNum="$2" shift 2 if [ $simpleLog4sh_LOG_LEVEL_NUM -le $levelNum ];then - timestamp="$(date +%Y-%m-%d\ %H:%M:%S.%N | cut -c 1-23)" + timestamp="$(date +%Y-%m-%d\ %H:%M:%S)" echo "$timestamp [$(_getCallPath)] ($levelName) $@" >> `_getCurrentLogFile` fi } +# remove stale logs xx days before +_log_removeStaleLogs(){ + # empty or non-positive inidcate keep all logs + if [ x"$simpleLog4sh_MAX_HISTORY" == x"" ];then + return + fi + if [ $simpleLog4sh_MAX_HISTORY -lt 0 ];then + return + fi -####################### 以下是暴露给用户的方法 - -# 抛异常 -throw() -{ - if [ $# -ne 0 ];then - logEchoError "$@" # 首先打印在stdError - logError "$@" # 记录到error - fi - exit 1 -} - -# 代替echo,输出到标准输出 -logEcho() -{ - echo "$@" - _doLog "ECHO " $simpleLog4sh_LOG_LEVEL_NUM_OFF "$@" - -} - -# 代替echo,输出到标准错误输出 -logEchoError() -{ - echo "(ERROR)" "$@" >&2 - _doLog "ECHO_ERROR" $simpleLog4sh_LOG_LEVEL_NUM_OFF "$@" -} - -logTrace() -{ - _doLog "TRACE" $simpleLog4sh_LOG_LEVEL_NUM_TRACE "$@" -} - -logDebug() -{ - _doLog "DEBUG" $simpleLog4sh_LOG_LEVEL_NUM_DEBUG "$@" -} - -logInfo() -{ - _doLog "INFO " $simpleLog4sh_LOG_LEVEL_NUM_INFO "$@" -} - -logWarn() -{ - _doLog "WARN" $simpleLog4sh_LOG_LEVEL_NUM_WARN "$@" -} - -logError() -{ - _doLog "ERROR" $simpleLog4sh_LOG_LEVEL_NUM_ERROR "$@" + # we don't clean too old log files + # e.g. each time we clean log files generated simpleLog4sh_MAX_HISTORY to simpleLog4sh_MAX_HISTORY+graceDay days before + local graceDay=7 + + local numberOfBackDate=$simpleLog4sh_MAX_HISTORY + + while [ $graceDay -ge 0 ];do + numberOfBackDate=$((numberOfBackDate+1)) + graceDay=$((graceDay-1)) + local dateDir=$(date -d "-$numberOfBackDate day" "+%Y%m%d") + if [ -d "$simpleLog4sh_LOG_DIR/$dateDir" ];then + logTrace "delete stale log: $simpleLog4sh_LOG_DIR/$dateDir" + rm -rf "$simpleLog4sh_LOG_DIR/$dateDir" + fi + done } -# -############################## - - - simpleLog4sh_main "$@" \ No newline at end of file