# AetherGateLogServer **Repository Path**: haitangyunchi/AetherGateLogServer ## Basic Information - **Project Name**: AetherGateLogServer - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-08 - **Last Updated**: 2026-08-26 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AetherGate 日志服务器 说明文档 > 版本:v1.1 > 更新日期:2026-08-04 > 技术栈:PHP 8.0+ / MySQL 5.7+ / Nginx --- ## 一、项目概述 AetherGate 日志服务器是一个独立的 PHP + MySQL 后台系统,用于接收、存储、分析 AetherGate 客户端上报的运行时事件。客户端通过 HTTP POST 将日志推送到服务器,后台提供可视化的统计、查询、错误分析、性能监控能力。 ### 设计目标 - **轻量**:纯 PHP 实现,无需 Node/Python/Java 运行时,宝塔一键部署 - **自托管**:数据完全在自有服务器,不依赖第三方服务 - **可观测**:覆盖运行时心跳、功能调用、异常、性能四类事件 - **易运维**:内置安装向导、定时清理、调试日志查看 ### 架构图 ``` AetherGate 客户端 (.NET) │ │ HTTP POST /api/upload (Header: X-API-Key) ▼ Nginx (宝塔) ── 伪静态 ──> PHP-FPM │ ▼ index.php (路由分发) │ ├── ApiController (API 接收) │ └── LogModel::add() ──> MySQL app_logs 表 │ ├── AuthController (登录鉴权) │ └── AdminController (后台管理) ├── 仪表盘 / 日志管理 / 设备列表 ├── 错误分析 / 功能统计 / 性能监控 └── 系统设置 / 调试日志 宝塔计划任务(每天 03:00) └── php Tasks/cleanup_logs.php ──> 清理过期日志 + OPTIMIZE TABLE ``` --- ## 二、功能特性 ### 客户端可上报的四类事件 - **Runtime**:运行时心跳(Start 启动、Running 每 5 分钟一次) - **Feature**:功能调用埋点(MBTI 计算、截图、USM 转换、更新检查等) - **Error**:异常上报(全局未捕获异常 + 业务 catch 主动上报) - **Performance**:性能监控(关键操作耗时,通过 `TrackPerformanceScope`) ### 后台页面 - **欢迎页**:未登录访问 `/` 显示项目介绍页(炫酷 UI) - **登录页**:管理员登录入口 - **仪表盘**:今日/总量统计、在线设备、版本分布、7 天趋势图、事件分类饼图、最近错误 - **日志管理**:按应用/设备/分类/日期筛选,分页浏览原始日志,批量删除 - **设备列表**:所有上报过日志的设备汇总 - **错误分析**:错误类型排行、错误详情、按错误名聚合统计 - **功能统计**:功能调用排行、按天统计、Top 10 功能 - **性能监控**:操作平均/最大/最小耗时排行 - **调试日志**:查看服务器 `storage/api_debug.log`,排查客户端上报问题 - **系统设置**:API Key 管理、数据清理、修改密码、客户端接入示例 ### 安全机制 - API Key 鉴权(64 位十六进制,每次客户端上报必须携带) - 后台登录 Session 鉴权(2 小时过期) - Nginx 禁止访问 `/config` `/storage` `/Tasks` `/Models` `/Controllers` 和所有 `.sql` 文件 - 自定义模态框替代原生 `confirm()`,避免 XSS --- ## 三、目录结构 ``` api-log-server/ ├── index.php # 入口文件,路由分发 ├── .htaccess # Apache 重写规则(兼容非 Nginx 环境) ├── nginx.conf # Nginx 伪静态规则(宝塔粘贴) │ ├── config/ │ ├── config.php # 数据库配置、应用配置、getDB() │ └── auth.php # 登录鉴权、API Key 验证 │ ├── Controllers/ │ ├── ApiController.php # API 接收(/api/upload, /api/stats) │ ├── AuthController.php # 登录登出 │ └── AdminController.php # 后台管理所有页面 │ ├── Models/ │ ├── LogModel.php # 日志 CRUD、统计、清理 │ └── UserModel.php # 管理员用户、API Key 管理 │ ├── app/Views/ │ ├── welcome.php # 欢迎页(未登录展示) │ ├── login.php # 登录页 │ ├── dashboard.php # 仪表盘 │ ├── logs.php # 日志管理 │ ├── devices.php # 设备列表 │ ├── errors.php # 错误分析 │ ├── features.php # 功能统计 │ ├── performance.php # 性能监控 │ ├── debug_log.php # 调试日志查看 │ ├── settings.php # 系统设置 │ └── layouts/ │ ├── header.php # 顶部导航 + 侧边栏 │ └── footer.php # 底部 + 自定义模态框组件 │ ├── install/ │ ├── install.php # 安装向导(创建数据库表、生成管理员、写 install.lock) │ ├── index.php # 安装目录入口 │ ├── database.sql # 完整建表脚本 │ ├── seed_feature_data.sql # 示例数据(安装时可勾选导入,用于演示后台效果) │ └── upgrade_v1.1.sql # v1.0 → v1.1 升级脚本 │ ├── Tasks/ │ ├── cleanup_logs.php # 定时清理脚本(宝塔计划任务调用) │ └── .htaccess # Apache 屏蔽 │ └── storage/ # 运行时目录(必须可写) ├── api_debug.log # API 调试日志 ├── cleanup.log # 清理脚本运行日志(宝塔重定向生成) └── README.md ``` --- ## 四、部署指南(宝塔面板) ### 4.1 环境准备 - 宝塔面板 ≥ 7.0 - PHP ≥ 8.0(需启用 `pdo_mysql`、`curl`、`openssl` 扩展) - MySQL ≥ 5.7 或 MariaDB ≥ 10.3 - Nginx(推荐)或 Apache ### 4.2 部署步骤 **第一步:上传代码** 将整个 `api-log-server` 目录上传到服务器,推荐路径: ``` /www/wwwroot/api_log_server ``` **第二步:宝塔创建站点** - 域名:`api-log-server.aethergate.cn`(或你自己的域名) - 根目录:`/www/wwwroot/api_log_server` - PHP 版本:8.0+ - 数据库:UTF8MB4 **第三步:配置伪静态** 宝塔站点设置 → 伪静态 → 选 `nginx` → 粘贴 `nginx.conf` 全部内容保存。 **第四步:设置目录权限** `storage` 目录必须可写: ```bash chmod 755 /www/wwwroot/api_log_server/storage chown -R www:www /www/wwwroot/api_log_server/storage ``` **第五步:创建数据库** 宝塔 → 数据库 → 添加数据库: - 数据库名:`api_log_server` - 用户名:`api_log_server` - 密码:自动生成并记录 - 字符集:`utf8mb4` **第六步:访问安装向导** 浏览器访问 `https://api-log-server.aethergate.cn/install`,按向导填写: - 数据库主机:`127.0.0.1` - 数据库名 / 用户名 / 密码:上一步记录的值 - 管理员用户名:自定义(如 `admin`) - 管理员密码:至少 8 位 安装完成后会生成 `/install.lock` 文件,之后访问 `/install` 会自动跳转。 **第七步:配置 HTTPS** 宝塔站点设置 → SSL → Let's Encrypt 免费证书 → 申请并强制 HTTPS。 ### 4.3 验证部署 访问首页,应看到欢迎页(未登录)。点击右上角「管理员登录」,用安装时设置的账号登录,进入仪表盘。 --- ## 五、数据库设计 ### 5.1 核心表 **admin_users**(管理员用户) - `id` 主键 - `username` 用户名(唯一) - `password` 密码哈希(`password_hash` 生成) - `api_key` API 密钥(64 位十六进制,唯一) - `last_login` 最后登录时间 - `created_at` 创建时间 **app_logs**(应用日志主表) - `id` 主键 - `log_time` 客户端日志时间(客户端生成) - `app_name` 应用名(如 `AetherGate.Debug`、`AetherGate`) - `app_version` 应用版本 - `architecture` 系统架构(如 `x64`、`arm64`) - `system_version` 系统版本 - `device_id` 设备唯一标识(GUID) - `session_id` 会话标识 - `event_category` 事件分类(Runtime / Feature / Error / Performance) - `event_name` 事件名(如 `Start`、`GenshinMbtiCalculate`) - `event_param` 扩展参数(JSON,存错误堆栈、五维分数等) - `created_at` 入库时间(服务器生成) ### 5.2 索引 - `idx_device_id`:按设备查询 - `idx_log_time`:按时间清理 - `idx_app_name`:按应用筛选 - `idx_event_category`:按分类统计 - `idx_event_name`:按事件名聚合 - `idx_created_at`:按入库时间排序 - `idx_category_name`:联合索引,加速"某分类下某事件"查询 ### 5.3 视图 - `vw_devices`:设备汇总(首次/最后上报时间、日志数、分类数) - `vw_weekly_stats`:最近 30 天按天统计(日志数、设备数、错误数、功能数) ### 5.4 升级脚本 - `install/upgrade_v1.1.sql`:v1.0 → v1.1,扩展 `event_name` 字段长度,新增索引和视图 执行方法:宝塔 phpMyAdmin 选择数据库,粘贴 SQL 运行。 --- ## 六、客户端接入 ### 6.1 配置项 在客户端 [LogUploadClient.cs](file:///f:/developers/AetherGate/src/AetherGate/Features/Update/LogUploadClient.cs) 中配置: ```csharp // 服务器地址(末尾不要带斜杠) public static Uri? DefaultBaseAddress { get; set; } = new("https://api-log-server.aethergate.cn"); // API Key(与服务器后台「系统设置」中显示的一致) public static string ApiKey { get; set; } = "你的64位APIKey"; ``` ### 6.2 上报方式 客户端通过 `LogUploadClient` 类上报,主要方法: - `UploadLogAsync(LogUploadRequestBase request)`:基础上报,每 5 分钟自动调用 - `TrackEvent(category, name, param)`:通用事件上报 - `TrackFeature(name, param)`:功能调用埋点 - `TrackError(ex, context, extraParam)`:异常上报 - `TrackPerformance(name, durationMs, param)`:性能上报 - `TrackPerformanceScope(name, param)`:返回 `IDisposable`,`using` 块结束时自动记录耗时 ### 6.3 上报数据格式 `POST /api/upload`,请求头: ``` Content-Type: application/json X-API-Key: 你的64位APIKey ``` 请求体: ```json { "time": "2026-08-04T20:07:31.123+08:00", "app_name": "AetherGate.Debug", "app_version": "1.0.0.0", "architecture": "x64", "system_version": "Microsoft Windows NT 10.0.22631.0", "device_id": "6abf36f0-08fd-fc02-c7ed-d176e3335b28", "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "event_category": "Feature", "event_name": "GenshinMbtiCalculate", "event_param": { "uid": "268775833", "persona_index": 12, "dim1_score": 65, "dim2_score": 88 } } ``` ### 6.4 响应格式 ```json { "code": 0, "msg": "success" } ``` 错误响应: - `400`:JSON 格式错误或缺少必要字段(`device_id` / `event_category` / `event_name`) - `403`:API Key 无效 - `500`:数据库写入失败 - `503`:系统未安装 --- ## 七、API 接口文档 ### 7.1 上报日志 - **路径**:`POST /api/upload` - **请求头**:`X-API-Key: ` - **请求体**:见 6.3 - **响应**:`{ "code": 0, "msg": "success" }` ### 7.2 获取统计数据 - **路径**:`GET /api/stats` - **请求头**:`X-API-Key: ` - **响应**:仪表盘所需的全部统计数据 ```json { "code": 0, "data": { "today_logs": 1234, "total_logs": 56789, "online_devices": 3, "total_devices": 12, "today_starts": 5, "version_distribution": [...], "weekly_trend": [...], "category_summary": [...], "today_errors": 2, "today_features": 45, "recent_errors": [...] } } ``` ### 7.3 在线设备判定规则 仪表盘「在线设备数」= 最近 7 分钟内有 `Runtime/Running` 心跳的去重设备数。客户端每 5 分钟上报一次 Running,所以 7 分钟窗口能覆盖一次完整心跳。 --- ## 八、事件埋点规范 ### 8.1 事件命名约定 采用 `PascalCase` 命名,动词或名词开头,语义清晰: - `Start` / `Running`:运行时心跳(保留名,勿他用) - `GenshinMbtiCalculate` / `StarRailMbtiCalculate`:MBTI 计算 - `ScreenshotCapture` / `ScreenshotCaptureSuccess`:截图 - `USMConvert` / `USMConvertSuccess` / `USMConvertCancelled`:USM 转换 - `CheckUpdate` / `UpdateAvailable` / `NoUpdateAvailable`:更新检查 - `RefreshAllGameRoles` / `RefreshAllGameRolesSuccess`:游戏记录同步 - `GachaLogSync` / `GachaLogSyncSuccess`:抽卡日志同步 - `GameLaunch` / `GameLaunchSuccess`:游戏启动 ### 8.2 成功/失败事件对 功能调用建议成对埋点: - 开始:`XxxAction`(无后缀,记录开始) - 成功:`XxxActionSuccess`(带 `Success` 后缀,携带结果参数) - 取消:`XxxActionCancelled`(用户主动取消) - 失败:通过 `TrackError` 上报,`context` 参数填 `XxxAction`,自动归入 `Error` 分类 ### 8.3 event_param 设计建议 - 所有参数使用 `camelCase` - 携带足够的上下文便于排查:`uid`、`gameBiz`、`version`、`count` 等 - 错误事件固定字段:`type`(异常类型)、`message`、`stack`、`source` - 性能事件固定字段:`duration_ms`(耗时毫秒) - 避免上报敏感信息:cookie、密码、token、完整 cookie 字符串 --- ## 九、定时清理配置(宝塔计划任务) 日志服务器不会自动清理过期日志,必须通过宝塔计划任务每天定时执行清理脚本。下面给出从零配置的完整步骤。 ### 9.1 前置条件检查 在配置计划任务之前,请先确认: 1. **PHP CLI 路径**:宝塔默认 PHP 命令行路径通常是 `/www/server/php/80/bin/php`(PHP 8.0)。可在宝塔「软件商店 → 已安装 → PHP 8.0 → 设置 → 安装扩展」确认 `pdo_mysql`、`curl` 已启用。 验证命令(SSH 登录后执行): ```bash php -v php -m | grep -E "pdo_mysql|curl" ``` 2. **清理脚本已上传**:确认 `/www/wwwroot/api_log_server/Tasks/cleanup_logs.php` 文件存在。 ```bash ls -l /www/wwwroot/api_log_server/Tasks/cleanup_logs.php ``` 3. **storage 目录可写**:清理脚本会把运行日志写入 `storage/cleanup.log`,必须可写。 ```bash chmod 755 /www/wwwroot/api_log_server/storage chown www:www /www/wwwroot/api_log_server/storage ``` 4. **数据库配置正确**:`config/config.php` 中的数据库连接信息必须能正常连接。 ```bash cd /www/wwwroot/api_log_server php -r "require 'config/config.php'; \$pdo=getDB(); echo \$pdo ? 'DB OK' : 'DB FAIL';" ``` ### 9.2 首次手动测试(强烈建议执行) 在配置自动任务之前,先用 `--dry-run` 预览一次,确认脚本能正常运行、能读到数据库、清理范围符合预期。 **SSH 登录服务器执行**: ```bash cd /www/wwwroot/api_log_server php Tasks/cleanup_logs.php --dry-run --verbose ``` 预期输出示例: ``` [2026-08-04 14:30:00] [INFO] ========== 清理任务开始 ========== [2026-08-04 14:30:00] [INFO] 脚本路径: /www/wwwroot/api_log_server/Tasks/cleanup_logs.php [2026-08-04 14:30:00] [INFO] 项目根目录: /www/wwwroot/api_log_server [2026-08-04 14:30:00] [INFO] 保留天数: 30 天 [2026-08-04 14:30:00] [INFO] 清理截止时间 (log_time < ): 2026-07-05 14:30:00 [2026-08-04 14:30:00] [INFO] 执行模式: 预览(DRY-RUN,不删数据) [2026-08-04 14:30:00] [INFO] 数据库连接成功 [2026-08-04 14:30:00] [INFO] ------ 清理范围预览 ------ [2026-08-04 14:30:00] [INFO] 影响总记录数: 12,543 条 [2026-08-04 14:30:00] [INFO] 影响设备数: 8 [2026-08-04 14:30:00] [INFO] 时间范围: 2026-06-15 08:00:00 ~ 2026-07-04 23:59:00 [2026-08-04 14:30:00] [INFO] 按分类分布: [2026-08-04 14:30:00] [INFO] - Runtime 10,234 条 [2026-08-04 14:30:00] [INFO] - Feature 1,802 条 [2026-08-04 14:30:00] [INFO] - Error 42 条 [2026-08-04 14:30:00] [INFO] - Performance 465 条 [2026-08-04 14:30:00] [INFO] 清理前全表记录数: 56,789 条 [2026-08-04 14:30:00] [INFO] ========== 清理任务结束 ========== ``` 看到 `数据库连接成功` + 影响记录数合理后,再进行下一步。 ### 9.3 宝塔配置计划任务(详细图文步骤) **第一步:进入计划任务页面** 登录宝塔面板 → 左侧菜单点击「**计划任务**」→ 右上角点击「**添加任务**」。 **第二步:填写任务信息** 按以下配置填写: - **任务类型**:选择 `Shell 脚本` - **任务名称**:`日志服务器-清理30天前过期日志`(自定义,建议带项目名便于识别) - **执行周期**:选择 `每天`,时间设为 `03 时 00 分`(凌晨业务低峰期,减少对写入的影响) - **脚本内容**:粘贴下方完整脚本 **第三步:粘贴脚本内容** ```bash #!/bin/bash # ================================================================ # AetherGate 日志服务器 - 过期日志定时清理任务 # 执行时间: 每天 03:00 # 功能: 清理 30 天前的日志, 释放磁盘空间 # ================================================================ # 站点根目录(按实际部署路径修改) SITE_DIR="/www/wwwroot/api_log_server" # PHP CLI 路径(宝塔默认, 如非 8.0 版本请改为实际路径) PHP_BIN="/www/server/php/80/bin/php" # 清理脚本路径 CLEANUP_SCRIPT="${SITE_DIR}/Tasks/cleanup_logs.php" # 运行日志路径 LOG_FILE="${SITE_DIR}/storage/cleanup.log" # 检查脚本是否存在 if [ ! -f "$CLEANUP_SCRIPT" ]; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] 清理脚本不存在: $CLEANUP_SCRIPT" >> "$LOG_FILE" exit 1 fi # 检查 PHP 是否可用 if [ ! -x "$PHP_BIN" ]; then echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] PHP 不可用: $PHP_BIN" >> "$LOG_FILE" exit 1 fi # 切换到站点目录并执行清理 cd "$SITE_DIR" || exit 1 # 执行清理(保留天数由 config.php 中 log_retention_days 控制, 默认 30 天) # 如需指定保留天数, 在脚本后追加 --days=N, 如: --days=7 "$PHP_BIN" "$CLEANUP_SCRIPT" >> "$LOG_FILE" 2>&1 # 退出码: 0 成功, 非 0 失败(宝塔会在任务历史中显示退出码) exit $? ``` **第四步:保存任务** 点击页面底部「**添加任务**」按钮保存。 **第五步:手动执行一次验证** 在计划任务列表中找到刚创建的任务,点击右侧「**执行**」按钮手动运行一次。然后点击「**日志**」按钮查看输出,或 SSH 查看运行日志: ```bash tail -50 /www/wwwroot/api_log_server/storage/cleanup.log ``` 看到 `========== 清理任务结束 ==========` 且无 `[ERROR]` 即配置成功。 ### 9.4 脚本参数详解 清理脚本 [Tasks/cleanup_logs.php](file:///f:/developers/AetherGate/api-log-server/Tasks/cleanup_logs.php) 支持以下命令行参数: - `--days=N`:临时指定保留 N 天(优先级高于 `config.php` 配置)。例如 `--days=7` 表示只保留最近 7 天。 - `--dry-run`:预览模式,只统计影响范围不实际删除。首次配置或调整保留天数后建议先用此参数预览。 - `--verbose`:输出详细信息,包括按分类/应用的删除分布、影响设备数、时间范围。 **参数组合示例**: ```bash # 标准运行(每天 03:00 自动执行,使用 config.php 中的保留天数) php Tasks/cleanup_logs.php # 预览保留 7 天的影响范围 php Tasks/cleanup_logs.php --days=7 --dry-run --verbose # 正式清理保留 90 天(不改 config.php,仅本次生效) php Tasks/cleanup_logs.php --days=90 --verbose # 只预览不删(验证脚本能否正常连接数据库) php Tasks/cleanup_logs.php --dry-run ``` ### 9.5 清理脚本核心特性 - **保留天数可配置**:优先级为 `--days=N` 参数 > `config.php` 中 `log_retention_days` > 默认 30 - **安全预览**:`--dry-run` 参数只统计不删除,用于先看影响范围 - **详细统计**:`--verbose` 参数输出按分类/应用的分布 - **分批删除**:每批 5000 条 + 50ms 让行,避免锁影响实时写入 - **空间回收**:删除后自动 `OPTIMIZE TABLE app_logs` 回收磁盘空间 - **路径无关**:基于 `dirname(__DIR__)` 推导根目录,不受工作目录影响 - **超时保护**:`set_time_limit(0)` + `memory_limit=256M`,支持大表清理 - **退出码**:成功返回 0,失败返回 1,宝塔任务历史会记录 - **文件日志自动清理**:同时清理 `storage/api_debug.log` 和 `storage/cleanup.log`,超过阈值时截断保留最新 N 行(详见 9.11 节) ### 9.6 修改保留天数 **方式一:修改 config.php(推荐,永久生效)** 编辑 [config/config.php](file:///f:/developers/AetherGate/api-log-server/config/config.php) 第 22 行: ```php $app_config = [ 'app_name' => 'AetherGate Log Server', 'app_version' => '1.0.0', 'timezone' => 'Asia/Shanghai', 'log_retention_days' => 30, // ← 改为你想保留的天数,如 7、90、180 'max_logs_per_page' => 50, ]; ``` 修改后无需重启,下次计划任务执行时自动生效。 **方式二:命令行临时覆盖(不改配置文件)** ```bash # 临时保留 90 天(仅本次执行生效,下次计划任务仍用 config.php 的值) php Tasks/cleanup_logs.php --days=90 ``` **方式三:在宝塔脚本中固定参数(永久覆盖 config.php)** 修改 9.3 节脚本内容中的最后一行: ```bash # 原来 "$PHP_BIN" "$CLEANUP_SCRIPT" >> "$LOG_FILE" 2>&1 # 改为(追加 --days=90) "$PHP_BIN" "$CLEANUP_SCRIPT" --days=90 >> "$LOG_FILE" 2>&1 ``` ### 9.7 监控清理任务运行状态 **方式一:宝塔任务历史** 宝塔面板 → 计划任务 → 找到任务 → 点击「**日志**」按钮,可查看每次执行的输出和退出码。 **方式二:SSH 查看运行日志** ```bash # 查看最近 100 行 tail -100 /www/wwwroot/api_log_server/storage/cleanup.log # 实时监控(执行计划任务时观察) tail -f /www/wwwroot/api_log_server/storage/cleanup.log # 查看所有历史清理记录 cat /www/wwwroot/api_log_server/storage/cleanup.log ``` **方式三:检查任务是否按时执行** ```bash # 查看清理日志的最后修改时间(应为今天凌晨 03:00 后) ls -l /www/wwwroot/api_log_server/storage/cleanup.log # 统计已执行的清理次数 grep -c "清理任务开始" /www/wwwroot/api_log_server/storage/cleanup.log ``` ### 9.8 清理任务失败排查 如果发现任务未按时执行或执行失败,按以下步骤排查: **问题 1:宝塔任务历史显示「执行失败」或退出码非 0** ```bash # 查看详细错误 tail -50 /www/wwwroot/api_log_server/storage/cleanup.log ``` 常见原因: - `PHP 不可用`:PHP 路径错误,执行 `which php` 查看实际路径并修改脚本中的 `PHP_BIN` - `清理脚本不存在`:路径错误或文件未上传,确认 `Tasks/cleanup_logs.php` 存在 - `数据库连接失败`:检查 `config/config.php` 中的数据库配置 **问题 2:任务历史显示成功,但日志数据未减少** ```bash # 手动执行一次 verbose 模式查看详情 cd /www/wwwroot/api_log_server php Tasks/cleanup_logs.php --dry-run --verbose ``` 可能原因: - 没有超过保留期的数据(`影响总记录数: 0 条`),属正常现象 - `log_retention_days` 配置过大,所有数据都在保留期内 **问题 3:storage/cleanup.log 文件不存在或为空** ```bash # 检查目录权限 ls -ld /www/wwwroot/api_log_server/storage # 手动创建并授权 touch /www/wwwroot/api_log_server/storage/cleanup.log chmod 644 /www/wwwroot/api_log_server/storage/cleanup.log chown www:www /www/wwwroot/api_log_server/storage/cleanup.log ``` **问题 4:OPTIMIZE TABLE 耗时过长锁表** 数据量非常大时 `OPTIMIZE TABLE` 可能锁表较久。临时解决方案: ```bash # 跳过 OPTIMIZE(修改脚本,在 cleanup_logs.php 中注释掉 OPTIMIZE 那几行) # 或者在业务低峰期手动执行 mysql -u用户名 -p 数据库名 -e "OPTIMIZE TABLE app_logs;" ``` ### 9.9 清理任务运行日志示例 正常执行的完整日志格式: ``` [2026-08-04 03:00:00] [INFO] ========== 清理任务开始 ========== [2026-08-04 03:00:00] [INFO] 脚本路径: /www/wwwroot/api_log_server/Tasks/cleanup_logs.php [2026-08-04 03:00:00] [INFO] 项目根目录: /www/wwwroot/api_log_server [2026-08-04 03:00:00] [INFO] 保留天数: 30 天 [2026-08-04 03:00:00] [INFO] 清理截止时间 (log_time < ): 2026-07-05 03:00:00 [2026-08-04 03:00:00] [INFO] 执行模式: 正式执行 [2026-08-04 03:00:00] [INFO] 数据库连接成功 [2026-08-04 03:00:00] [INFO] ------ 清理范围预览 ------ [2026-08-04 03:00:00] [INFO] 影响总记录数: 12,543 条 [2026-08-04 03:00:00] [INFO] 清理前全表记录数: 56,789 条 [2026-08-04 03:00:01] [INFO] 删除完成: 实际删除 12,543 条 / 共 3 批次,耗时 0.847 秒 [2026-08-04 03:00:02] [INFO] 清理后全表记录数: 44,246 条 [2026-08-04 03:00:02] [INFO] 本次净减少: 12,543 条 [2026-08-04 03:00:02] [INFO] 执行 OPTIMIZE TABLE 回收空间... [2026-08-04 03:00:08] [INFO] OPTIMIZE TABLE 完成 [2026-08-04 03:00:08] [INFO] ========== 清理任务结束 ========== ``` 无数据可清理时的日志: ``` [2026-08-05 03:00:00] [INFO] ========== 清理任务开始 ========== [2026-08-05 03:00:00] [INFO] 保留天数: 30 天 [2026-08-05 03:00:00] [INFO] 清理截止时间 (log_time < ): 2026-07-06 03:00:00 [2026-08-05 03:00:00] [INFO] 执行模式: 正式执行 [2026-08-05 03:00:00] [INFO] 数据库连接成功 [2026-08-05 03:00:00] [INFO] ------ 清理范围预览 ------ [2026-08-05 03:00:00] [INFO] 影响总记录数: 0 条 [2026-08-05 03:00:00] [INFO] 清理前全表记录数: 44,246 条 [2026-08-05 03:00:00] [INFO] 没有需要清理的过期日志,跳过 [2026-08-05 03:00:00] [INFO] 清理后全表记录数: 44,246 条 [2026-08-05 03:00:00] [INFO] 本次净减少: 0 条 [2026-08-05 03:00:00] [INFO] ========== 清理任务结束 ========== ``` ### 9.10 多站点复用方案 如果你有多个日志服务器站点(如测试环境和生产环境),可以为每个站点创建独立的计划任务,只需修改脚本中的 `SITE_DIR` 变量: ```bash # 测试环境任务 SITE_DIR="/www/wwwroot/api_log_server_test" PHP_BIN="/www/server/php/80/bin/php" cd "$SITE_DIR" && "$PHP_BIN" "Tasks/cleanup_logs.php" >> "storage/cleanup.log" 2>&1 # 生产环境任务(另一个计划任务) SITE_DIR="/www/wwwroot/api_log_server" PHP_BIN="/www/server/php/80/bin/php" cd "$SITE_DIR" && "$PHP_BIN" "Tasks/cleanup_logs.php" >> "storage/cleanup.log" 2>&1 ``` 建议两个任务的执行时间错开(如测试环境 02:00,生产环境 03:00),避免同时执行占用过多数据库资源。 ### 9.11 文件日志自动清理 除了清理数据库 `app_logs` 表的过期记录,`cleanup_logs.php` 还会自动清理服务器上的两个文件日志,避免无限增长。 #### 清理范围 - **`storage/api_debug.log`**:API 调试日志,每次客户端上报时写入一行(约 100-200 字节) - **`storage/cleanup.log`**:清理脚本自身的运行日志 #### 清理策略 采用「超阈值才截断」策略,避免每次执行都重写文件: - 文件大小未超过阈值 → 跳过,不处理 - 文件大小超过阈值 → 保留最新 N 行,删除其余历史内容 - 阈值设为 0 → 完全禁用该文件的自动清理 #### 配置项 在 [config/config.php](file:///f:/developers/AetherGate/api-log-server/config/config.php#L25-L29) 中配置: ```php 'app_config' => [ // ... 其他配置 ... // 文件日志自动清理 'api_debug_max_size_mb' => 50, // api_debug.log 超过 50MB 时截断 'api_debug_keep_lines' => 1000, // 截断时保留最新 1000 行 'cleanup_log_max_size_mb' => 10, // cleanup.log 超过 10MB 时截断 'cleanup_log_keep_lines' => 500, // 截断时保留最新 500 行 ], ``` #### 两种文件的清理方式不同 **`api_debug.log`(原子替换)**: - 用临时文件 + `rename()` 原子替换 - 适用于:PHP 每次写入时 `file_put_contents` 打开关闭,没有长持 fd - 优点:原子操作,不会出现半截断文件 **`cleanup.log`(直接覆盖)**: - 用 `file_put_contents($file, $newContent)` 直接覆盖原文件 - 原因:宝塔 shell 的 `>> storage/cleanup.log 2>&1` 重定向在脚本启动时就打开了文件 fd,如果用 `rename()` 替换文件,原 inode 变成「已删除但仍打开」,本次脚本后续的输出会写到匿名 inode(丢失),新文件收不到 - 直接覆盖方式:`fopen('w')` 会截断原文件但保留 inode,shell 的 fd 仍然有效,本次输出会接着新内容追加 - 代价:非原子操作,极端情况下(截断瞬间断电)可能丢失部分内容,但 cleanup.log 不是关键数据,可接受 #### 运行日志示例 文件日志清理的输出会追加到 `cleanup.log`: ``` [2026-08-04 03:00:08] [INFO] ------ 文件日志清理 ------ [2026-08-04 03:00:08] [INFO] api_debug.log 当前 52.34 MB 超过阈值 50 MB,准备截断保留最新 1000 行 [2026-08-04 03:00:08] [INFO] api_debug.log 已截断: 52.34 MB → 0.18 MB (保留最新 1000 行 / 原始 285431 行) [2026-08-04 03:00:08] [INFO] cleanup.log 当前 0.02 MB 未超阈值 (10 MB),跳过 [2026-08-04 03:00:08] [INFO] ========== 清理任务结束 ========== ``` #### 关闭文件日志清理 如果不想自动清理文件日志(例如想完整保留所有调试记录),把对应阈值设为 0: ```php 'app_config' => [ 'api_debug_max_size_mb' => 0, // 禁用 api_debug.log 自动清理 'cleanup_log_max_size_mb' => 0, // 禁用 cleanup.log 自动清理 ], ``` #### 容量估算 按每日 1 万条客户端上报计算: - `api_debug.log`:约 2MB/天 → 50MB 阈值约 25 天触发一次 - `cleanup.log`:每天约 30 行 × 100 字节 = 3KB/天 → 10MB 阈值约 10 年才触发(实际几乎不会执行) 所以默认配置下,`api_debug.log` 每月清理 1-2 次,`cleanup.log` 基本不会触发清理。 --- ## 十、后台管理功能详解 ### 10.1 仪表盘(/admin/dashboard) 展示内容: - **统计卡片**:今日日志数、总日志数、在线设备、总设备、今日启动、今日错误、今日功能调用 - **版本分布**:饼图,按 `app_version` 聚合设备数 - **7 天趋势图**:折线图,按天显示日志数/错误数/功能数/设备数 - **事件分类饼图**:Runtime / Feature / Error / Performance 占比 - **最近错误**:最新 5 条 Error 事件预览 ### 10.2 日志管理(/admin/logs) - **筛选**:应用名、设备 ID、事件分类、起止日期 - **分页**:默认每页 50 条,可通过 URL 参数 `page_size` 调整 - **批量删除**:复选框选中后点「删除选中」 - **清理过期**:一键清理 30 天前日志 - **参数解析**:`event_param` 字段自动 JSON 美化显示 ### 10.3 错误分析(/admin/errors) - **错误类型排行**:按 `event_name` 聚合,显示出现次数、影响设备数、最后发生时间 - **错误详情列表**:分页显示完整错误日志,包含堆栈、版本、设备 - **时间范围**:默认统计最近 30 天 ### 10.4 功能统计(/admin/features) - **功能调用排行**:按 `event_name` 聚合,显示调用次数、使用设备数、最后使用时间 - **时间范围切换**:支持 7 天 / 30 天 / 90 天 - **进度条可视化**:直观显示各功能使用频率 ### 10.5 性能监控(/admin/performance) - **操作耗时排行**:按平均耗时降序 - **统计字段**:调用次数、平均耗时、最大耗时、最小耗时 - **数据来源**:`event_param` 中的 `duration_ms` 字段 ### 10.6 调试日志(/admin/debug-log) - 查看 `storage/api_debug.log` 最新 1000 行 - 按内容自动着色:红色(错误/失败)、绿色(成功)、黄色(警告)、蓝色(信息)、灰色(调试) - 支持清空日志内容、删除日志文件 ### 10.7 系统设置(/admin/settings) - **API Key 管理**:查看当前 Key、重新生成(所有客户端需同步更新) - **数据清理**:按天数清理过期日志(7/30/90/180/365 天) - **清除示例数据**:清除 `seed_feature_data.sql` 插入的测试数据 - **修改密码**:管理员密码修改 - **客户端接入示例**:自动展示当前 API Key 的 C# 接入代码 ### 10.8 自定义模态框 所有危险操作(删除、清理、重新生成 Key)使用统一的模态框组件,替代原生 `confirm()`: - 半透明遮罩 + 毛玻璃效果 - 弹出动画(cubic-bezier 缓动) - 图标 + 标题 + 详情说明 + 取消/确认按钮 - 类型区分:`danger`(红色确认)、`warning`(黄色确认) --- ## 十一、运维与故障排查 ### 11.1 客户端日志不上报 **排查步骤**: 1. **查看服务器调试日志**:后台「调试日志」页面,或直接看 `storage/api_debug.log` 2. **检查 API Key**:客户端配置的 Key 必须与服务器后台「系统设置」显示的完全一致(64 位) 3. **检查服务器地址**:客户端 `DefaultBaseAddress` 必须是可访问的 HTTPS 地址 4. **检查 install.lock**:服务器根目录必须有 `/install.lock` 文件,否则返回 503 5. **DNS 解析**:客户端机器必须能解析服务器域名(`nslookup api-log-server.aethergate.cn`) **常见错误**: - `不知道这样的主机`:DNS 解析失败,检查域名解析或换用 IP - `403 Invalid API Key`:Key 不匹配,重新复制 - `400 Missing required fields`:请求体缺少 `device_id` / `event_category` / `event_name` - `503 系统未安装`:服务器没有 `install.lock`,访问 `/install` 完成安装 ### 11.2 调试日志查看 服务器 `storage/api_debug.log` 记录每次 API 请求的完整链路: ``` [2026-08-04 18:07:02] 收到上传请求: POST [2026-08-04 18:07:02] API Key: 67efba45... (长度: 64) [2026-08-04 18:07:02] API Key 验证通过 [2026-08-04 18:07:02] 请求体长度: 340 [2026-08-04 18:07:02] 解析数据: device_id=6abf36f0-..., category=Runtime, name=Start [2026-08-04 18:07:02] 日志保存成功 ``` 后台「调试日志」页面自动着色显示最新 1000 行。 ### 11.3 数据库维护 **手动清理**: ```bash # SSH 登录后 mysql -u用户名 -p 数据库名 > DELETE FROM app_logs WHERE log_time < DATE_SUB(NOW(), INTERVAL 30 DAY); > OPTIMIZE TABLE app_logs; ``` **查看表大小**: ```sql SELECT table_name, ROUND(data_length/1024/1024, 2) AS data_mb, ROUND(index_length/1024/1024, 2) AS index_mb, table_rows FROM information_schema.tables WHERE table_schema = 'api_log_server'; ``` **备份**: 宝塔 → 数据库 → 备份 → 设置计划任务,每天自动备份到 `/www/backup/database/`。 ### 11.4 清理任务运行日志 查看 `storage/cleanup.log`: ``` [2026-08-04 03:00:00] [INFO] ========== 清理任务开始 ========== [2026-08-04 03:00:00] [INFO] 保留天数: 30 天 [2026-08-04 03:00:00] [INFO] 清理截止时间 (log_time < ): 2026-07-05 03:00:00 [2026-08-04 03:00:00] [INFO] 影响总记录数: 12,543 条 [2026-08-04 03:00:00] [INFO] 清理前全表记录数: 56,789 条 [2026-08-04 03:00:01] [INFO] 删除完成: 实际删除 12,543 条 / 共 3 批次,耗时 0.847 秒 [2026-08-04 03:00:02] [INFO] 清理后全表记录数: 44,246 条 [2026-08-04 03:00:02] [INFO] 本次净减少: 12,543 条 [2026-08-04 03:00:05] [INFO] 执行 OPTIMIZE TABLE 回收空间... [2026-08-04 03:00:08] [INFO] OPTIMIZE TABLE 完成 [2026-08-04 03:00:08] [INFO] ========== 清理任务结束 ========== ``` --- ## 十二、安全注意事项 ### 12.1 必做项 - **修改默认管理员密码**:安装后立即在「系统设置」修改密码 - **启用 HTTPS**:避免 API Key 明文传输 - **限制数据库外网访问**:MySQL 只监听 127.0.0.1 - **定期备份**:宝塔计划任务每天备份数据库 - **防火墙**:只开放 80/443 端口,SSH 用密钥登录 ### 12.2 API Key 安全 - API Key 等同于管理员权限,不要泄露 - 怀疑泄露时立即在「系统设置」重新生成 - 客户端配置文件不要提交到公开 Git 仓库 ### 12.3 敏感数据 客户端上报时禁止包含: - Cookie 字符串 - 用户密码 - 游戏 token - 完整账号信息 如需关联用户,使用 `device_id`(GUID)或脱敏后的 `uid`。 --- ## 十三、示例数据管理 ### 13.1 安装时自动导入 安装向导第 3 步「创建管理员账户」时,表单底部有一个「📦 导入示例数据」复选框(默认勾选)。勾选后,安装脚本会自动执行 [install/seed_feature_data.sql](file:///f:/developers/AetherGate/api-log-server/install/seed_feature_data.sql),插入 30+ 条 Feature 类别示例日志,用于演示后台功能统计、错误分析、性能监控页面效果。 安装完成页会显示绿色提示框告知导入状态,并在 `install.lock` 中记录 `sample_data_imported` 字段。 ### 13.2 手动导入示例数据 如安装时未勾选导入,或想重新插入示例数据,可手动执行: - **文件位置**:`install/seed_feature_data.sql` - **执行方法**:宝塔 phpMyAdmin 选择数据库 → 粘贴 SQL 文件内容 → 执行 或命令行: ```bash mysql -u用户名 -p 数据库名 < /www/wwwroot/api_log_server/install/seed_feature_data.sql ``` ### 13.3 清除示例数据 **方式一**:后台「系统设置」→「数据清理」→「清除示例数据」按钮。 **方式二**:直接执行 SQL: ```sql DELETE FROM app_logs WHERE app_name = 'AetherGate.Debug'; ``` **识别规则**:示例数据使用 `app_name = 'AetherGate.Debug'` 标识,清除时一键删除所有该标识的日志记录(含在线/离线设备),无需等待过期。注意:若有真实 Debug 客户端在用 `AetherGate.Debug` 上报日志,将被一并清除。 --- ## 十四、版本历史 ### v1.3(2026-08-04) - 定时清理脚本新增文件日志自动清理功能 - `api_debug.log` 超过 50MB 时自动截断保留最新 1000 行(原子替换) - `cleanup.log` 超过 10MB 时自动截断保留最新 500 行(直接覆盖,兼容 shell 重定向 fd) - `config.php` 新增 4 个文件日志清理配置项 - 阈值设为 0 可禁用对应文件的自动清理 ### v1.2(2026-08-04) - 安装向导新增「导入示例数据」复选框,安装时可勾选自动导入示例数据 - 示例数据 SQL 文件移至 `install/seed_feature_data.sql`,与建表脚本统一管理 - 安装完成页显示示例数据导入状态 - `install.lock` 记录 `sample_data_imported` 状态字段 ### v1.1(2026-08-04) - 新增错误分析页面(错误排行 + 详情) - 新增功能使用统计页面(调用排行 + 时间范围) - 新增性能监控页面(耗时统计) - 仪表盘增强:7 天趋势图、事件分类饼图、最近错误预览 - 新增自定义模态框组件替代 `confirm()` - 新增示例数据清除功能 - 新增定时清理脚本 `Tasks/cleanup_logs.php` - Nginx 规则增强:屏蔽 `/Tasks` `/Models` `/Controllers` 和 `.sql` 文件 ### v1.0(2026-08-04) - 初始版本 - 完成核心功能:日志接收、后台管理、登录鉴权、安装向导 - 包含仪表盘、日志管理、设备列表、调试日志、系统设置 - 欢迎页 + 登录页 UI --- ## 十五、FAQ ### Q1:日志服务器有什么用? AetherGate 客户端是桌面应用,开发者无法直接看到用户的运行情况。日志服务器收集客户端上报的事件,帮助开发者: - 了解功能使用情况(哪些功能用得多、哪些没人用) - 监控异常(用户遇到的错误类型、堆栈、影响范围) - 分析性能(哪些操作慢、平均耗时多少) - 统计版本分布(推动用户升级) ### Q2:断网时日志会丢失吗? 会。当前实现没有离线缓存机制,断网期间的日志直接丢弃。联网后客户端每 5 分钟自动上报一次心跳,不影响后续日志。如需离线容错,可在客户端增加 SQLite 本地缓存 + 网络恢复重传机制。 ### Q3:服务器硬盘空间会满吗? 不会。通过宝塔计划任务每天清理 30 天前的日志(可配置保留天数),单台设备每月上报量约 50MB,30 天保留期最多积累 1.5GB,对服务器硬盘几乎无压力。 ### Q4:API Key 忘了怎么办? 登录后台「系统设置」页面,页面上会显示当前 API Key。如果连后台密码也忘了,直接访问 `/install` 重新安装(会清空数据,谨慎操作)。 ### Q5:如何对接其他客户端(非 AetherGate)? 日志服务器协议通用,任何能发 HTTP POST 的客户端都能对接: 1. 在后台「系统设置」获取 API Key 2. 客户端构造 JSON 请求体(参考 6.3 节格式) 3. 设置请求头 `X-API-Key` 4. POST 到 `https://你的域名/api/upload` 后台「系统设置」页面底部有 C# 接入示例代码。 ### Q6:多个客户端可以共用一个服务器吗? 可以。不同客户端用不同的 `app_name` 区分(如 `AetherGate`、`AetherGate.Debug`、`AnotherApp`),后台支持按应用名筛选。API Key 可以共用,也可以为每个客户端生成独立的 Key(当前版本共用一个)。 ### Q7:如何停止日志上报? 在客户端将 `LogUploadClient.DefaultBaseAddress` 设为 `null`,所有上报方法会立即返回,不发任何请求。 --- ## 十六、相关文件索引 - 客户端日志上传服务:[LogUploadService.cs](file:///f:/developers/AetherGate/src/AetherGate/Features/Update/LogUploadService.cs) - 客户端日志上传客户端:[LogUploadClient.cs](file:///f:/developers/AetherGate/src/AetherGate/Features/Update/LogUploadClient.cs) - 服务器入口:[index.php](file:///f:/developers/AetherGate/api-log-server/index.php) - 服务器配置:[config.php](file:///f:/developers/AetherGate/api-log-server/config/config.php) / [auth.php](file:///f:/developers/AetherGate/api-log-server/config/auth.php) - 数据库建表:[database.sql](file:///f:/developers/AetherGate/api-log-server/install/database.sql) - 定时清理脚本:[cleanup_logs.php](file:///f:/developers/AetherGate/api-log-server/Tasks/cleanup_logs.php) - Nginx 规则:[nginx.conf](file:///f:/developers/AetherGate/api-log-server/nginx.conf)