多设备 Android Logcat 自动采集方案:基于 Docker + Shell 实现日志按天切割与自动清理

前言

在基于 Docker 运行多个 Android 模拟器实例的云手机/自动化测试场景中,日志管理是一个绕不开的运维难题。每台设备都在源源不断地产生 logcat 日志,如果不加以管理,要么日志丢失、要么磁盘被撑爆。

本文介绍一套完整的多设备 Logcat 自动采集方案,具备以下能力:

  • ✅ 支持同时采集多个 Android 容器的 logcat
  • ✅ 日志按天自动切割,文件命名清晰
  • ✅ 自动保留最近 3 天日志,超期自动清理
  • ✅ 历史日志 gzip 压缩,节省磁盘空间
  • ✅ 容器重启后自动重连,采集不中断
  • ✅ systemd 托管,主机重启自动恢复

环境说明

本文方案基于以下典型部署结构:

复制代码
宿主机(Ubuntu)
├── phone0   ← android:14_2.5.0 容器
├── phone1   ← android:14_2.6.0 容器
├── phone2   ← android:14_2.6.0 容器
├── phone3   ← android:14_2.6.0 容器
└── ...

每个 Android 容器通过 Docker 运行,执行 logcat 的方式为:

bash 复制代码
sudo docker exec phone0 logcat

⚠️ 注意:后台运行时不能加 -it 参数,否则会报错 the input device is not a TTY,应直接使用 docker exec <容器名> logcat


方案架构

复制代码
/opt/logcat-collector/collect.sh   ← 核心采集脚本
         │
         ├── 为每个设备 fork 一个后台子进程
         │         │
         │         ├── 检测容器是否运行
         │         ├── docker exec <name> logcat >> 当天日志文件
         │         └── 异常退出后自动重试
         │
         └── 主进程每小时执行:
                   ├── 清理 3 天前的旧日志
                   └── gzip 压缩昨天的日志

/etc/systemd/system/logcat-collector.service  ← systemd 服务
/var/log/android_logcat/                       ← 日志存储目录

实现步骤

第一步:创建采集脚本

bash 复制代码
sudo mkdir -p /opt/logcat-collector

sudo tee /opt/logcat-collector/collect.sh << 'EOF'
#!/bin/bash
# ============================================
# Android Logcat 自动采集脚本(docker exec 方式)
# 支持多设备、按天切割、保留最近3天
# ============================================

LOG_BASE="/var/log/android_logcat"
RETENTION_DAYS=3
PIDFILE_DIR="/tmp/logcat_pids"

# 设备列表:和容器名保持一致
DEVICES=(
  "phone0"
  "phone1"
  "phone2"
  "phone3"
)

mkdir -p "$LOG_BASE" "$PIDFILE_DIR"

# ---- 清理过期日志 ----
cleanup_old_logs() {
  find "$LOG_BASE" -name "*.log" -mtime +${RETENTION_DAYS} -delete
  find "$LOG_BASE" -name "*.log.gz" -mtime +${RETENTION_DAYS} -delete
  echo "[$(date '+%F %T')] 已清理 ${RETENTION_DAYS} 天前的旧日志"
}

# ---- 单设备采集(后台循环) ----
collect_device() {
  local name="$1"
  local log_dir="${LOG_BASE}/${name}"
  mkdir -p "$log_dir"

  echo "[$(date '+%F %T')] [$name] 启动采集" | tee -a "${log_dir}/collector.log"

  while true; do
    # 检查容器是否正在运行
    if ! docker ps --format '{{.Names}}' | grep -q "^${name}$"; then
      echo "[$(date '+%F %T')] [$name] 容器未运行,等待..." >> "${log_dir}/collector.log"
      sleep 10
      continue
    fi

    local today
    today=$(date '+%Y-%m-%d')
    local log_file="${log_dir}/${today}.log"

    echo "[$(date '+%F %T')] [$name] 开始写入 → $log_file" >> "${log_dir}/collector.log"

    # 核心命令:docker exec 执行 logcat(不加 -it,适合后台运行)
    docker exec "${name}" logcat -v threadtime "*:V" \
      >> "$log_file" 2>> "${log_dir}/collector.log"

    echo "[$(date '+%F %T')] [$name] logcat 中断,5秒后重试..." >> "${log_dir}/collector.log"
    sleep 5
  done
}

# ---- 停止所有采集进程 ----
stop_all() {
  echo "[$(date '+%F %T')] 收到停止信号,终止所有采集..."
  for f in "$PIDFILE_DIR"/*.pid; do
    [ -f "$f" ] || continue
    pid=$(cat "$f")
    kill -- -"$pid" 2>/dev/null || kill "$pid" 2>/dev/null
    rm -f "$f"
  done
  exit 0
}

trap stop_all SIGTERM SIGINT

# ---- 主逻辑 ----
cleanup_old_logs

for name in "${DEVICES[@]}"; do
  collect_device "$name" &
  child_pid=$!
  echo "$child_pid" > "${PIDFILE_DIR}/${name}.pid"
  echo "[$(date '+%F %T')] [$name] 采集进程 PID=$child_pid"
done

# 每小时清理 + 压缩昨天日志
while true; do
  sleep 3600
  cleanup_old_logs

  yesterday=$(date -d "yesterday" '+%Y-%m-%d')
  for name in "${DEVICES[@]}"; do
    f="${LOG_BASE}/${name}/${yesterday}.log"
    if [ -f "$f" ] && [ ! -f "${f}.gz" ]; then
      gzip -9 "$f" && echo "[$(date '+%F %T')] [$name] 已压缩 ${yesterday}.log"
    fi
  done
done
EOF

sudo chmod +x /opt/logcat-collector/collect.sh

第二步:配置 systemd 服务

将采集脚本托管到 systemd,实现开机自启和异常自动重启:

bash 复制代码
sudo tee /etc/systemd/system/logcat-collector.service << 'EOF'
[Unit]
Description=Android Logcat Multi-Device Collector
After=docker.service
Requires=docker.service

[Service]
Type=simple
ExecStart=/opt/logcat-collector/collect.sh
ExecStop=/bin/kill -SIGTERM $MAINPID
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=journal
SyslogIdentifier=logcat-collector
User=root

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable logcat-collector
sudo systemctl start logcat-collector

第三步:验证运行状态

bash 复制代码
# 查看服务状态
sudo systemctl status logcat-collector

# 实时查看采集器日志
sudo journalctl -u logcat-collector -f

# 查看某设备今天的 logcat 输出
tail -f /var/log/android_logcat/phone0/$(date '+%Y-%m-%d').log

# 查看全部日志文件结构
find /var/log/android_logcat/ -type f | sort

日志目录结构

正常运行后,日志目录结构如下:

复制代码
/var/log/android_logcat/
├── phone0/
│   ├── collector.log          ← 采集器自身运行日志
│   ├── 2025-02-22.log.gz     ← 已压缩的历史日志
│   ├── 2025-02-23.log.gz
│   └── 2025-02-24.log        ← 今天实时写入中
├── phone1/
│   └── ...
├── phone2/
│   └── ...
└── phone3/
    └── ...

常见问题

Q1:logcat 命令一直没有输出?

先手动测试命令是否可用:

bash 复制代码
sudo docker exec phone0 logcat -v threadtime "*:V" | head -20

如果有输出说明命令正常,脚本即可正常工作。

Q2:如何新增一台设备?

编辑 /opt/logcat-collector/collect.sh,在 DEVICES 数组中追加容器名:

bash 复制代码
DEVICES=(
  "phone0"
  "phone1"
  "phone2"
  "phone3"
  "phone4"   # 新增
)

然后重启服务:

bash 复制代码
sudo systemctl restart logcat-collector

Q3:如何修改日志保留天数?

修改脚本顶部的变量:

bash 复制代码
RETENTION_DAYS=7   # 改为保留7天

重启服务生效。

Q4:磁盘空间担心不够怎么办?

logcat 日志量取决于应用行为,可以通过过滤级别减少日志量:

bash 复制代码
# 只采集 Warning 及以上级别
docker exec "${name}" logcat -v threadtime "*:W"

# 只采集特定 Tag
docker exec "${name}" logcat -v threadtime MyApp:D "*:S"

方案对比

方案 优点 缺点
手动执行 logcat 简单直接 不持久、重启丢失
ADB 方式采集 标准接口 需要端口映射、ADB 连接管理复杂
docker exec 方式(本文) 无需额外端口、直接访问容器 需要宿主机有 docker 权限
容器内自采集 与宿主机解耦 需修改容器镜像

总结

本方案核心思路:

  1. docker exec <容器名> logcat 替代 ADB,省去端口映射和连接管理的复杂性
  2. 每个设备独立一个后台子进程,互不干扰,任一设备故障不影响其他设备
  3. 按天切割日志文件,便于查找和归档
  4. 通过 find -mtime 实现自动清理,gzip 压缩节省空间
  5. systemd 托管保证服务可靠运行

整套方案纯 Shell 实现,无第三方依赖,适合在嵌入式 Linux、ARM 服务器等资源受限环境中部署。


如果本文对你有帮助,欢迎点赞收藏~有问题欢迎评论区交流。

相关推荐
阿拉斯攀登13 小时前
从入门到实战:CMake 与 Android JNI/NDK 开发全解析
android·linux·c++·yolo·cmake
冬奇Lab13 小时前
相机录像流程:MediaRecorder与Camera2的协作之道
android·音视频开发·源码阅读
苏渡苇14 小时前
Docker 网络完全指南
网络·docker·容器·docker容器·容器通信
麦客奥德彪14 小时前
Jetpack Compose 常用开发总结
android
风向决定发型丶14 小时前
K8S CPU绑核详解
云原生·容器·kubernetes
麦客奥德彪14 小时前
Jetpack Compose Modifier 完全指南
android
斯普信云原生组16 小时前
Docker 开源软件应急处理方案及操作手册——镜像管理与构建故障
docker·容器·eureka
Mac的实验室16 小时前
(2026年最新)解决谷歌账号注册设备扫码短信发送失败无法验证难题(100%通过无需扫码验证)
android·google·程序员
半条咸鱼17 小时前
如何通过 ADB 连接安卓设备(USB + 无线 TCP/IP)
android
huwuhang17 小时前
斐讯盒子N1_YYFROM固件_webview119更新版附安卓专用遥控器刷机固工具USB_Burning_Tool
android