多设备 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 服务器等资源受限环境中部署。


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

相关推荐
Haoea!1 小时前
Docker + Harbor 私有镜像仓库搭建
运维·docker·容器
iambooo1 小时前
Docker 架构与核心原理深度解析:容器到底是怎么实现的?
运维·docker·容器
海兰2 小时前
Docker部署OpenClaw及常见问题解决(win11)
docker·容器·eureka
qq_316837753 小时前
使用 certbot docker镜像生成阿里云域名ssl证书
阿里云·docker·ssl
木雷坞3 小时前
使用Docker Compose部署PostgreSQL:从入门到实践
docker·postgresql·容器
bqliang3 小时前
Compose 实验性 Styles API
android·android jetpack
大尚来也3 小时前
PHP 入门指南:从零基础到掌握核心语法
android
summerkissyou19873 小时前
android -wifi/蓝牙-常见面试题
android·wifi·bluetooth
Tummer83633 小时前
从 Docker 到 Kubernetes:容器编排的工程化实践指南
docker·容器·kubernetes