310 lines
10 KiB
Bash
Executable File
310 lines
10 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# VibeTunnel Logging Utility
|
|
# Simplifies access to VibeTunnel logs using macOS unified logging system
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration
|
|
SUBSYSTEM="com.steipete.clawdis"
|
|
DEFAULT_LEVEL="info"
|
|
|
|
# Colors for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Function to handle sudo password errors
|
|
handle_sudo_error() {
|
|
echo -e "\n${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
|
echo -e "${YELLOW}⚠️ Password Required for Log Access${NC}"
|
|
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
|
echo -e "vtlog needs to use sudo to show complete log data (Apple hides sensitive info by default)."
|
|
echo -e "\nTo avoid password prompts, configure passwordless sudo for the log command:"
|
|
echo -e "See: ${BLUE}apple/docs/logging-private-fix.md${NC}\n"
|
|
echo -e "Quick fix:"
|
|
echo -e " 1. Run: ${GREEN}sudo visudo${NC}"
|
|
echo -e " 2. Add: ${GREEN}$(whoami) ALL=(ALL) NOPASSWD: /usr/bin/log${NC}"
|
|
echo -e " 3. Save and exit (:wq)\n"
|
|
echo -e "${RED}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
|
exit 1
|
|
}
|
|
|
|
# Default values
|
|
STREAM_MODE=false
|
|
TIME_RANGE="5m" # Default to last 5 minutes
|
|
CATEGORY=""
|
|
LOG_LEVEL="$DEFAULT_LEVEL"
|
|
SEARCH_TEXT=""
|
|
OUTPUT_FILE=""
|
|
ERRORS_ONLY=false
|
|
SERVER_ONLY=false
|
|
TAIL_LINES=50 # Default number of lines to show
|
|
SHOW_TAIL=true
|
|
SHOW_HELP=false
|
|
|
|
# Function to show usage
|
|
show_usage() {
|
|
cat << EOF
|
|
clawlog - Clawdis Logging Utility
|
|
|
|
USAGE:
|
|
vtlog [OPTIONS]
|
|
|
|
DESCRIPTION:
|
|
View Clawdis logs with full details (bypasses Apple's privacy redaction).
|
|
Requires sudo access configured for /usr/bin/log command.
|
|
|
|
LOG FLOW ARCHITECTURE:
|
|
Clawdis logs flow through the macOS unified log (subsystem: com.steipete.clawdis).
|
|
|
|
LOG CATEGORIES (examples):
|
|
• voicewake - Voice wake detection/test harness
|
|
• relay - Relay process manager
|
|
• xpc - XPC service calls
|
|
• notifications - Notification helper
|
|
• screenshot - Screenshotter
|
|
• shell - ShellExecutor
|
|
|
|
QUICK START:
|
|
vtlog -n 100 Show last 100 lines from all components
|
|
vtlog -f Follow logs in real-time
|
|
vtlog -e Show only errors
|
|
vtlog -c ServerManager Show logs from ServerManager only
|
|
|
|
OPTIONS:
|
|
-h, --help Show this help message
|
|
-f, --follow Stream logs continuously (like tail -f)
|
|
-n, --lines NUM Number of lines to show (default: 50)
|
|
-l, --last TIME Time range to search (default: 5m)
|
|
Examples: 5m, 1h, 2d, 1w
|
|
-c, --category CAT Filter by category (e.g., ServerManager, SessionService)
|
|
-e, --errors Show only error messages
|
|
-d, --debug Show debug level logs (more verbose)
|
|
-s, --search TEXT Search for specific text in log messages
|
|
-o, --output FILE Export logs to file
|
|
--server Show only server output logs
|
|
--all Show all logs without tail limit
|
|
--list-categories List all available log categories
|
|
--json Output in JSON format
|
|
|
|
EXAMPLES:
|
|
vtlog Show last 50 lines from past 5 minutes (default)
|
|
vtlog -f Stream logs continuously
|
|
vtlog -n 100 Show last 100 lines
|
|
vtlog -e Show only recent errors
|
|
vtlog -l 30m -n 200 Show last 200 lines from past 30 minutes
|
|
vtlog -c ServerManager Show recent ServerManager logs
|
|
vtlog -s "fail" Search for "fail" in recent logs
|
|
vtlog --server -e Show recent server errors
|
|
vtlog -f -d Stream debug logs continuously
|
|
|
|
CATEGORIES:
|
|
Common categories include:
|
|
- ServerManager - Server lifecycle and configuration
|
|
- SessionService - Terminal session management
|
|
- TerminalManager - Terminal spawning and control
|
|
- GitRepository - Git integration features
|
|
- ScreencapService - Screen capture functionality
|
|
- WebRTCManager - WebRTC connections
|
|
- UnixSocket - Unix socket communication
|
|
- WindowTracker - Window tracking and focus
|
|
- NgrokService - Ngrok tunnel management
|
|
- ServerOutput - Node.js server output
|
|
|
|
TIME FORMATS:
|
|
- 5m = 5 minutes - 1h = 1 hour
|
|
- 2d = 2 days - 1w = 1 week
|
|
|
|
EOF
|
|
}
|
|
|
|
# Function to list categories
|
|
list_categories() {
|
|
echo -e "${BLUE}Fetching VibeTunnel log categories from the last hour...${NC}\n"
|
|
|
|
# Get unique categories from recent logs
|
|
log show --predicate "subsystem == \"$SUBSYSTEM\"" --last 1h 2>/dev/null | \
|
|
grep -E "category: \"[^\"]+\"" | \
|
|
sed -E 's/.*category: "([^"]+)".*/\1/' | \
|
|
sort | uniq | \
|
|
while read -r cat; do
|
|
echo " • $cat"
|
|
done
|
|
|
|
echo -e "\n${YELLOW}Note: Only categories with recent activity are shown${NC}"
|
|
}
|
|
|
|
# Show help if no arguments provided
|
|
if [[ $# -eq 0 ]]; then
|
|
show_usage
|
|
exit 0
|
|
fi
|
|
|
|
# Parse command line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help)
|
|
show_usage
|
|
exit 0
|
|
;;
|
|
-f|--follow)
|
|
STREAM_MODE=true
|
|
SHOW_TAIL=false
|
|
shift
|
|
;;
|
|
-n|--lines)
|
|
TAIL_LINES="$2"
|
|
shift 2
|
|
;;
|
|
-l|--last)
|
|
TIME_RANGE="$2"
|
|
shift 2
|
|
;;
|
|
-c|--category)
|
|
CATEGORY="$2"
|
|
shift 2
|
|
;;
|
|
-e|--errors)
|
|
ERRORS_ONLY=true
|
|
shift
|
|
;;
|
|
-d|--debug)
|
|
LOG_LEVEL="debug"
|
|
shift
|
|
;;
|
|
-s|--search)
|
|
SEARCH_TEXT="$2"
|
|
shift 2
|
|
;;
|
|
-o|--output)
|
|
OUTPUT_FILE="$2"
|
|
shift 2
|
|
;;
|
|
--server)
|
|
SERVER_ONLY=true
|
|
CATEGORY="ServerOutput"
|
|
shift
|
|
;;
|
|
--list-categories)
|
|
list_categories
|
|
exit 0
|
|
;;
|
|
--json)
|
|
STYLE_ARGS="--style json"
|
|
shift
|
|
;;
|
|
--all)
|
|
SHOW_TAIL=false
|
|
shift
|
|
;;
|
|
*)
|
|
echo -e "${RED}Unknown option: $1${NC}"
|
|
echo "Use -h or --help for usage information"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Build the predicate
|
|
PREDICATE="subsystem == \"$SUBSYSTEM\""
|
|
|
|
# Add category filter if specified
|
|
if [[ -n "$CATEGORY" ]]; then
|
|
PREDICATE="$PREDICATE AND category == \"$CATEGORY\""
|
|
fi
|
|
|
|
# Add error filter if specified
|
|
if [[ "$ERRORS_ONLY" == true ]]; then
|
|
PREDICATE="$PREDICATE AND (eventType == \"error\" OR messageType == \"error\" OR eventMessage CONTAINS \"ERROR\" OR eventMessage CONTAINS \"[31m\")"
|
|
fi
|
|
|
|
# Add search filter if specified
|
|
if [[ -n "$SEARCH_TEXT" ]]; then
|
|
PREDICATE="$PREDICATE AND eventMessage CONTAINS[c] \"$SEARCH_TEXT\""
|
|
fi
|
|
|
|
# Build the command - always use sudo with --info to show private data
|
|
if [[ "$STREAM_MODE" == true ]]; then
|
|
# Streaming mode
|
|
CMD="sudo log stream --predicate '$PREDICATE' --level $LOG_LEVEL --info"
|
|
|
|
echo -e "${GREEN}Streaming VibeTunnel logs continuously...${NC}"
|
|
echo -e "${YELLOW}Press Ctrl+C to stop${NC}\n"
|
|
else
|
|
# Show mode
|
|
CMD="sudo log show --predicate '$PREDICATE'"
|
|
|
|
# Add log level for show command
|
|
if [[ "$LOG_LEVEL" == "debug" ]]; then
|
|
CMD="$CMD --debug"
|
|
else
|
|
CMD="$CMD --info"
|
|
fi
|
|
|
|
# Add time range
|
|
CMD="$CMD --last $TIME_RANGE"
|
|
|
|
if [[ "$SHOW_TAIL" == true ]]; then
|
|
echo -e "${GREEN}Showing last $TAIL_LINES log lines from the past $TIME_RANGE${NC}"
|
|
else
|
|
echo -e "${GREEN}Showing all logs from the past $TIME_RANGE${NC}"
|
|
fi
|
|
|
|
# Show applied filters
|
|
if [[ "$ERRORS_ONLY" == true ]]; then
|
|
echo -e "${RED}Filter: Errors only${NC}"
|
|
fi
|
|
if [[ -n "$CATEGORY" ]]; then
|
|
echo -e "${BLUE}Category: $CATEGORY${NC}"
|
|
fi
|
|
if [[ -n "$SEARCH_TEXT" ]]; then
|
|
echo -e "${YELLOW}Search: \"$SEARCH_TEXT\"${NC}"
|
|
fi
|
|
echo "" # Empty line for readability
|
|
fi
|
|
|
|
# Add style arguments if specified
|
|
if [[ -n "${STYLE_ARGS:-}" ]]; then
|
|
CMD="$CMD $STYLE_ARGS"
|
|
fi
|
|
|
|
# Execute the command
|
|
if [[ -n "$OUTPUT_FILE" ]]; then
|
|
# First check if sudo works without password for the log command
|
|
if sudo -n /usr/bin/log show --last 1s 2>&1 | grep -q "password"; then
|
|
handle_sudo_error
|
|
fi
|
|
|
|
echo -e "${BLUE}Exporting logs to: $OUTPUT_FILE${NC}\n"
|
|
if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then
|
|
eval "$CMD" 2>&1 | tail -n "$TAIL_LINES" > "$OUTPUT_FILE"
|
|
else
|
|
eval "$CMD" > "$OUTPUT_FILE" 2>&1
|
|
fi
|
|
|
|
# Check if file was created and has content
|
|
if [[ -s "$OUTPUT_FILE" ]]; then
|
|
LINE_COUNT=$(wc -l < "$OUTPUT_FILE" | tr -d ' ')
|
|
echo -e "${GREEN}✓ Exported $LINE_COUNT lines to $OUTPUT_FILE${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠ No logs found matching the criteria${NC}"
|
|
fi
|
|
else
|
|
# Run interactively
|
|
# First check if sudo works without password for the log command
|
|
if sudo -n /usr/bin/log show --last 1s 2>&1 | grep -q "password"; then
|
|
handle_sudo_error
|
|
fi
|
|
|
|
if [[ "$SHOW_TAIL" == true ]] && [[ "$STREAM_MODE" == false ]]; then
|
|
# Apply tail for non-streaming mode
|
|
eval "$CMD" 2>&1 | tail -n "$TAIL_LINES"
|
|
echo -e "\n${YELLOW}Showing last $TAIL_LINES lines. Use --all or -n to see more.${NC}"
|
|
else
|
|
eval "$CMD"
|
|
fi
|
|
fi
|