Files
adb_logger/log_avaair.sh
2025-12-04 13:50:26 +03:30

342 lines
8.0 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
#
# Advanced ADB Logcat Monitor for Android apps
#
# Author: Reza Esmaeili
# Description:
# Tail logcat for a specific Android app process (by package) with:
# - Device selection (auto or explicit)
# - Configurable filters / logcat args
# - Optional app restart
# - Safe cleanup on exit
# - Log files with timestamps in a dedicated directory
#
# Usage:
# ./adb-log-monitor.sh [options] [LOGCAT_FILTER...]
#
# Examples:
# ./adb-log-monitor.sh
# ./adb-log-monitor.sh -p ir.avaair.avaair -a avaair -f '*:E'
# ./adb-log-monitor.sh -d emulator-5554 --no-restart
#
# Options:
# -p PACKAGE Android package name (default: ir.avaair.avaair)
# -a APP_NAME Logical app name used for messages & filenames (default: derived from package)
# -d DEVICE Device ID (adb serial). If omitted, will prompt if multiple devices.
# -o DIR Logs output directory (default: logs)
# -f FILTER Logcat filter spec (default: *:V) can also pass as positional args
# --no-restart Do NOT auto-restart the app when process dies/startup fails
# --help Show this help and exit
#
# Exit codes:
# 0 Success / user interrupt
# 1 Usage / config error
# 2 adb / device error
# 3 (unused now, kept for compatibility if you want to reintroduce timeouts)
#
set -Euo pipefail
########################
# Default configuration
########################
DEFAULT_PACKAGE_NAME="ir.avaair.avaair"
DEFAULT_LOG_DIR="logs"
DEFAULT_FILTER="*:V"
PACKAGE_NAME="${PACKAGE_NAME:-$DEFAULT_PACKAGE_NAME}"
APP_NAME="${APP_NAME:-}"
DEVICE="${DEVICE:-}"
LOG_DIR="${LOG_DIR:-$DEFAULT_LOG_DIR}"
FILTER="${DEFAULT_FILTER}"
AUTO_RESTART=true
########################
# Pretty output helpers
########################
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
BLUE="\033[0;34m"
NC="\033[0m" # No Color
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
log_ok() { echo -e "${GREEN}[ OK ]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
log_error() { echo -e "${RED}[ERR ]${NC} $*" >&2; }
########################
# Usage
########################
usage() {
sed -n '1,80p' "$0" | sed 's/^# \{0,1\}//'
}
########################
# Parse CLI args
########################
ARGS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--no-restart)
AUTO_RESTART=false
shift
;;
--help|-h)
usage
exit 0
;;
-p)
[[ $# -lt 2 ]] && { log_error "-p requires PACKAGE"; exit 1; }
PACKAGE_NAME="$2"
shift 2
;;
-a)
[[ $# -lt 2 ]] && { log_error "-a requires APP_NAME"; exit 1; }
APP_NAME="$2"
shift 2
;;
-d)
[[ $# -lt 2 ]] && { log_error "-d requires DEVICE serial"; exit 1; }
DEVICE="$2"
shift 2
;;
-o)
[[ $# -lt 2 ]] && { log_error "-o requires DIR"; exit 1; }
LOG_DIR="$2"
shift 2
;;
-f)
[[ $# -lt 2 ]] && { log_error "-f requires FILTER"; exit 1; }
FILTER="$2"
shift 2
;;
--)
shift
break
;;
-*)
log_error "Unknown option: $1"
usage
exit 1
;;
*)
ARGS+=("$1")
shift
;;
esac
done
# If user passed extra args as filters, override FILTER
if [[ ${#ARGS[@]} -gt 0 ]]; then
FILTER="${ARGS[*]}"
fi
# Derive APP_NAME from PACKAGE_NAME if not set
if [[ -z "${APP_NAME}" ]]; then
APP_NAME="${PACKAGE_NAME##*.}"
fi
mkdir -p "${LOG_DIR}"
########################
# Cleanup
########################
LOGCAT_PID=""
cleanup() {
log_warn "Cleaning up..."
if [[ -n "${LOGCAT_PID}" ]]; then
kill "${LOGCAT_PID}" 2>/dev/null || true
fi
log_ok "Done."
}
trap cleanup EXIT
trap 'exit 0' SIGINT SIGTERM
########################
# Preconditions
########################
if ! command -v adb >/dev/null 2>&1; then
log_error "'adb' command not found. Install Android platform-tools and ensure it's in PATH."
exit 2
fi
########################
# Device selection
########################
select_device() {
local devices
mapfile -t devices < <(adb devices | awk 'NR>1 && $2=="device" {print $1}')
if adb devices | grep -q "unauthorized"; then
log_error "Device unauthorized. Accept the RSA prompt on your device."
exit 2
fi
if adb devices | grep -q "offline"; then
log_error "Device offline. Reconnect or restart adb server."
exit 2
fi
if [[ ${#devices[@]} -eq 0 ]]; then
log_error "No devices found. Connect a device and ensure it's authorized."
exit 2
fi
if [[ -n "${DEVICE}" ]]; then
if printf '%s\n' "${devices[@]}" | grep -qx "${DEVICE}"; then
log_ok "Using specified device: ${DEVICE}"
return
else
log_error "Specified device '${DEVICE}' is not connected."
exit 2
fi
fi
if [[ ${#devices[@]} -eq 1 ]]; then
DEVICE="${devices[0]}"
log_ok "Using single connected device: ${DEVICE}"
return
fi
log_warn "Multiple devices found:"
local i=1
for d in "${devices[@]}"; do
echo " [$i] $d"
((i++))
done
local choice
read -r -p "Select device number: " choice
if ! [[ "$choice" =~ ^[0-9]+$ ]] || (( choice < 1 || choice > ${#devices[@]} )); then
log_error "Invalid selection."
exit 1
fi
DEVICE="${devices[$((choice-1))]}"
log_ok "Selected device: ${DEVICE}"
}
########################
# PID helpers
########################
get_pid() {
local pid=""
# Try pidof first
if adb -s "${DEVICE}" shell "command -v pidof >/dev/null 2>&1" >/dev/null 2>&1; then
pid=$(adb -s "${DEVICE}" shell "pidof -s '${PACKAGE_NAME}' 2>/dev/null || true" | tr -d '\r' || true)
fi
# Fallback to ps if pidof gave nothing
if [[ -z "${pid}" ]]; then
if adb -s "${DEVICE}" shell "ps -A >/dev/null 2>&1" >/dev/null 2>&1; then
pid=$(adb -s "${DEVICE}" shell "ps -A | grep '${PACKAGE_NAME}' | awk '{print \$2}' | head -n 1" 2>/dev/null | tr -d '\r' || true)
else
pid=$(adb -s "${DEVICE}" shell "ps | grep '${PACKAGE_NAME}' | awk '{print \$2}' | head -n 1" 2>/dev/null | tr -d '\r' || true)
fi
fi
echo "${pid}"
return 0
}
########################
# App restart helper
########################
restart_app() {
if [[ "${AUTO_RESTART}" != true ]]; then
return
fi
log_warn "Restarting app ${PACKAGE_NAME}..."
adb -s "${DEVICE}" shell am force-stop "${PACKAGE_NAME}" >/dev/null 2>&1 || true
adb -s "${DEVICE}" shell monkey -p "${PACKAGE_NAME}" -c android.intent.category.LAUNCHER 1 >/dev/null 2>&1 || true
}
########################
# Start logcat
########################
start_logcat() {
local pid="$1"
local log_file="${LOG_DIR}/${APP_NAME}_$(date +'%Y-%m-%d_%H-%M-%S')_pid_${pid}.log"
log_ok "Logging PID ${pid} (${PACKAGE_NAME}) -> ${log_file}"
log_info "Device: ${DEVICE}"
log_info "Filter: ${FILTER}"
echo "------------------------------------------------------------"
(
adb -s "${DEVICE}" logcat --pid="${pid}" ${FILTER} | tee -a "${log_file}"
) &
LOGCAT_PID=$!
}
########################
# Main loop
########################
main() {
select_device
log_info "Monitoring app logs for package: ${PACKAGE_NAME} (name: ${APP_NAME})"
log_info "Log directory: ${LOG_DIR}"
log_info "Auto-restart: ${AUTO_RESTART}"
local last_pid=""
local consecutive_misses=0
while true; do
local pid
pid="$(get_pid)"
if [[ -z "${pid}" ]]; then
((consecutive_misses++))
if (( consecutive_misses == 1 )); then
log_warn "Process '${PACKAGE_NAME}' not running. Waiting for it to start..."
restart_app
else
log_info "Still waiting for process '${PACKAGE_NAME}' to start..."
fi
sleep 3
continue
fi
if (( consecutive_misses > 0 )); then
log_ok "Process '${PACKAGE_NAME}' started with PID ${pid}."
fi
consecutive_misses=0
if [[ "${pid}" != "${last_pid}" ]]; then
if [[ -n "${last_pid}" ]]; then
log_info "Process restarted (old PID: ${last_pid} -> new PID: ${pid})."
fi
if [[ -n "${LOGCAT_PID}" ]]; then
kill "${LOGCAT_PID}" 2>/dev/null || true
LOGCAT_PID=""
fi
last_pid="${pid}"
start_logcat "${pid}"
fi
sleep 5
done
}
main "$@"