1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 15:04:42 +00:00

feat: optimize log system and add mo log command

- Add get_timestamp() helper and optimize log rotation
- Create mo log viewer with search/filter capabilities
- Improve test coverage to 18.4% with better assertions
- Add security fixes for grep injection prevention
This commit is contained in:
tw93
2026-01-28 19:46:01 +08:00
parent f815a5f28c
commit 64f79a59d8
6 changed files with 385 additions and 45 deletions

View File

@@ -9,6 +9,7 @@ MOLE_COMMANDS=(
"status:Monitor system health"
"purge:Remove old project artifacts"
"installer:Find and remove installer files"
"log:View operation logs"
"touchid:Configure Touch ID for sudo"
"completion:Setup shell tab completion"
"update:Update to latest version"

View File

@@ -44,17 +44,25 @@ rotate_log_once() {
export MOLE_LOG_ROTATED=1
local max_size="$LOG_MAX_SIZE_DEFAULT"
if [[ -f "$LOG_FILE" ]] && [[ $(get_file_size "$LOG_FILE") -gt "$max_size" ]]; then
mv "$LOG_FILE" "${LOG_FILE}.old" 2> /dev/null || true
ensure_user_file "$LOG_FILE"
if [[ -f "$LOG_FILE" ]]; then
local size
size=$(get_file_size "$LOG_FILE")
if [[ "$size" -gt "$max_size" ]]; then
mv "$LOG_FILE" "${LOG_FILE}.old" 2>/dev/null || true
ensure_user_file "$LOG_FILE"
fi
fi
# Rotate operations log (5MB limit)
if [[ "${MO_NO_OPLOG:-}" != "1" ]]; then
local oplog_max_size="$OPLOG_MAX_SIZE_DEFAULT"
if [[ -f "$OPERATIONS_LOG_FILE" ]] && [[ $(get_file_size "$OPERATIONS_LOG_FILE") -gt "$oplog_max_size" ]]; then
mv "$OPERATIONS_LOG_FILE" "${OPERATIONS_LOG_FILE}.old" 2> /dev/null || true
ensure_user_file "$OPERATIONS_LOG_FILE"
if [[ -f "$OPERATIONS_LOG_FILE" ]]; then
local size
size=$(get_file_size "$OPERATIONS_LOG_FILE")
if [[ "$size" -gt "$oplog_max_size" ]]; then
mv "$OPERATIONS_LOG_FILE" "${OPERATIONS_LOG_FILE}.old" 2>/dev/null || true
ensure_user_file "$OPERATIONS_LOG_FILE"
fi
fi
fi
}
@@ -63,51 +71,62 @@ rotate_log_once() {
# Logging Functions
# ============================================================================
# Get current timestamp (centralized for consistency)
get_timestamp() {
date '+%Y-%m-%d %H:%M:%S'
}
# Log informational message
log_info() {
echo -e "${BLUE}$1${NC}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] INFO: $1" >> "$LOG_FILE" 2> /dev/null || true
local timestamp
timestamp=$(get_timestamp)
echo "[$timestamp] INFO: $1" >>"$LOG_FILE" 2>/dev/null || true
if [[ "${MO_DEBUG:-}" == "1" ]]; then
echo "[$timestamp] INFO: $1" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "[$timestamp] INFO: $1" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
# Log success message
log_success() {
echo -e " ${GREEN}${ICON_SUCCESS}${NC} $1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] SUCCESS: $1" >> "$LOG_FILE" 2> /dev/null || true
local timestamp
timestamp=$(get_timestamp)
echo "[$timestamp] SUCCESS: $1" >>"$LOG_FILE" 2>/dev/null || true
if [[ "${MO_DEBUG:-}" == "1" ]]; then
echo "[$timestamp] SUCCESS: $1" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "[$timestamp] SUCCESS: $1" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
# Log warning message
# shellcheck disable=SC2329
log_warning() {
echo -e "${YELLOW}$1${NC}"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] WARNING: $1" >> "$LOG_FILE" 2> /dev/null || true
local timestamp
timestamp=$(get_timestamp)
echo "[$timestamp] WARNING: $1" >>"$LOG_FILE" 2>/dev/null || true
if [[ "${MO_DEBUG:-}" == "1" ]]; then
echo "[$timestamp] WARNING: $1" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "[$timestamp] WARNING: $1" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
# Log error message
# shellcheck disable=SC2329
log_error() {
echo -e "${YELLOW}${ICON_ERROR}${NC} $1" >&2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] ERROR: $1" >> "$LOG_FILE" 2> /dev/null || true
local timestamp
timestamp=$(get_timestamp)
echo "[$timestamp] ERROR: $1" >>"$LOG_FILE" 2>/dev/null || true
if [[ "${MO_DEBUG:-}" == "1" ]]; then
echo "[$timestamp] ERROR: $1" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "[$timestamp] ERROR: $1" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
# Debug logging (active when MO_DEBUG=1)
# shellcheck disable=SC2329
debug_log() {
if [[ "${MO_DEBUG:-}" == "1" ]]; then
echo -e "${GRAY}[DEBUG]${NC} $*" >&2
echo "[$(date '+%Y-%m-%d %H:%M:%S')] DEBUG: $*" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
local timestamp
timestamp=$(get_timestamp)
echo "[$timestamp] DEBUG: $*" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
@@ -139,12 +158,12 @@ log_operation() {
[[ -z "$path" ]] && return 0
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
timestamp=$(get_timestamp)
local log_line="[$timestamp] [$command] $action $path"
[[ -n "$detail" ]] && log_line+=" ($detail)"
echo "$log_line" >> "$OPERATIONS_LOG_FILE" 2> /dev/null || true
echo "$log_line" >>"$OPERATIONS_LOG_FILE" 2>/dev/null || true
}
# Log session start marker
@@ -154,16 +173,15 @@ log_operation_session_start() {
local command="${1:-mole}"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
timestamp=$(get_timestamp)
{
echo ""
echo "# ========== $command session started at $timestamp =========="
} >> "$OPERATIONS_LOG_FILE" 2> /dev/null || true
} >>"$OPERATIONS_LOG_FILE" 2>/dev/null || true
}
# Log session end with summary
# Usage: log_operation_session_end <command> <items_count> <total_size>
# shellcheck disable=SC2329
log_operation_session_end() {
oplog_enabled || return 0
@@ -171,18 +189,18 @@ log_operation_session_end() {
local items="${2:-0}"
local size="${3:-0}"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
timestamp=$(get_timestamp)
local size_human=""
if [[ "$size" =~ ^[0-9]+$ ]] && [[ "$size" -gt 0 ]]; then
size_human=$(bytes_to_human "$((size * 1024))" 2> /dev/null || echo "${size}KB")
size_human=$(bytes_to_human "$((size * 1024))" 2>/dev/null || echo "${size}KB")
else
size_human="0B"
fi
{
echo "# ========== $command session ended at $timestamp, $items items, $size_human =========="
} >> "$OPERATIONS_LOG_FILE" 2> /dev/null || true
} >>"$OPERATIONS_LOG_FILE" 2>/dev/null || true
}
# Enhanced debug logging for operations
@@ -200,7 +218,7 @@ debug_operation_start() {
echo ""
echo "=== $operation_name ==="
[[ -n "$operation_desc" ]] && echo "Description: $operation_desc"
} >> "$DEBUG_LOG_FILE" 2> /dev/null || true
} >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
@@ -214,7 +232,7 @@ debug_operation_detail() {
echo -e "${GRAY}[DEBUG] $detail_type: $detail_value${NC}" >&2
# Also log to file
echo "$detail_type: $detail_value" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "$detail_type: $detail_value" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
@@ -234,7 +252,7 @@ debug_file_action() {
echo -e "${GRAY}[DEBUG] $action: $msg${NC}" >&2
# Also log to file
echo "$action: $msg" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "$action: $msg" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
@@ -246,16 +264,16 @@ debug_risk_level() {
if [[ "${MO_DEBUG:-}" == "1" ]]; then
local color="$GRAY"
case "$risk_level" in
LOW) color="$GREEN" ;;
MEDIUM) color="$YELLOW" ;;
HIGH) color="$RED" ;;
LOW) color="$GREEN" ;;
MEDIUM) color="$YELLOW" ;;
HIGH) color="$RED" ;;
esac
# Output to stderr with color
echo -e "${GRAY}[DEBUG] Risk Level: ${color}${risk_level}${GRAY}, $reason${NC}" >&2
# Also log to file
echo "Risk Level: $risk_level, $reason" >> "$DEBUG_LOG_FILE" 2> /dev/null || true
echo "Risk Level: $risk_level, $reason" >>"$DEBUG_LOG_FILE" 2>/dev/null || true
fi
}
@@ -267,7 +285,7 @@ log_system_info() {
# Reset debug log file for this new session
ensure_user_file "$DEBUG_LOG_FILE"
if ! : > "$DEBUG_LOG_FILE" 2> /dev/null; then
if ! : >"$DEBUG_LOG_FILE" 2>/dev/null; then
echo -e "${YELLOW}${ICON_WARNING}${NC} Debug log not writable: $DEBUG_LOG_FILE" >&2
fi
@@ -280,19 +298,19 @@ log_system_info() {
echo "Hostname: $(hostname)"
echo "Architecture: $(uname -m)"
echo "Kernel: $(uname -r)"
if command -v sw_vers > /dev/null; then
if command -v sw_vers >/dev/null; then
echo "macOS: $(sw_vers -productVersion), $(sw_vers -buildVersion)"
fi
echo "Shell: ${SHELL:-unknown}, ${TERM:-unknown}"
# Check sudo status non-interactively
if sudo -n true 2> /dev/null; then
if sudo -n true 2>/dev/null; then
echo "Sudo Access: Active"
else
echo "Sudo Access: Required"
fi
echo "----------------------------------------------------------------------"
} >> "$DEBUG_LOG_FILE" 2> /dev/null || true
} >>"$DEBUG_LOG_FILE" 2>/dev/null || true
# Notification to stderr
echo -e "${GRAY}[DEBUG] Debug logging enabled. Session log: $DEBUG_LOG_FILE${NC}" >&2
@@ -304,7 +322,7 @@ log_system_info() {
# Run command silently (ignore errors)
run_silent() {
"$@" > /dev/null 2>&1 || true
"$@" >/dev/null 2>&1 || true
}
# Run command with error logging
@@ -312,12 +330,12 @@ run_logged() {
local cmd="$1"
# Log to main file, and also to debug file if enabled
if [[ "${MO_DEBUG:-}" == "1" ]]; then
if ! "$@" 2>&1 | tee -a "$LOG_FILE" | tee -a "$DEBUG_LOG_FILE" > /dev/null; then
if ! "$@" 2>&1 | tee -a "$LOG_FILE" | tee -a "$DEBUG_LOG_FILE" >/dev/null; then
log_warning "Command failed: $cmd"
return 1
fi
else
if ! "$@" 2>&1 | tee -a "$LOG_FILE" > /dev/null; then
if ! "$@" 2>&1 | tee -a "$LOG_FILE" >/dev/null; then
log_warning "Command failed: $cmd"
return 1
fi