Spaces:
Running
Running
| # 📋 客户端 IP 地址日志指南 | |
| ## ✅ 已完成的功能 | |
| 现在 Flask 应用已经配置为在日志中记录客户端的真实 IP 地址。 | |
| ## 🔍 日志格式 | |
| ### Flask 应用日志 | |
| ``` | |
| [2025-10-16 18:58:23] INFO in app: [203.0.113.1] GET /api/health | UA: Mozilla/5.0 (Windows NT 10.0) | |
| ^^^^^^^^^^^^ | |
| 客户端真实 IP | |
| ``` | |
| **日志包含:** | |
| - 时间戳 | |
| - 日志级别 | |
| - 客户端 IP 地址 | |
| - HTTP 方法和路径 | |
| - User-Agent(浏览器信息) | |
| ### Gunicorn 访问日志 | |
| ``` | |
| [16/Oct/2025:18:58:23 +0000] Client: 203.0.113.1 (Remote: 172.17.0.1) GET /api/health Status: 200 Size: 123 bytes Time: 5234 μs UA: "Mozilla/5.0..." | |
| ^^^^^^^^^^^^ | |
| 真实客户端 IP | |
| ``` | |
| **日志包含:** | |
| - 时间戳 | |
| - 客户端真实 IP(从 X-Forwarded-For 获取) | |
| - 远程地址(可能是代理服务器) | |
| - 请求信息 | |
| - 响应状态码和大小 | |
| - 处理时间 | |
| - User-Agent | |
| ## 🎯 IP 获取逻辑 | |
| 应用会按以下优先级检查 HTTP 头部来获取真实 IP: | |
| 1. **X-Forwarded-For** - 最常见的代理头部 | |
| 2. **X-Real-IP** - Nginx 等使用 | |
| 3. **CF-Connecting-IP** - Cloudflare 使用 | |
| 4. **True-Client-IP** - 某些 CDN 使用 | |
| 5. **X-Client-IP** - 其他代理使用 | |
| 6. **request.remote_addr** - 直连时的 IP | |
| ### 代码实现 | |
| ```python | |
| def get_client_ip(): | |
| """获取客户端真实 IP 地址""" | |
| headers_to_check = [ | |
| 'X-Forwarded-For', | |
| 'X-Real-IP', | |
| 'CF-Connecting-IP', | |
| 'True-Client-IP', | |
| 'X-Client-IP', | |
| ] | |
| for header in headers_to_check: | |
| ip = request.headers.get(header) | |
| if ip: | |
| # X-Forwarded-For 可能包含多个 IP,取第一个 | |
| return ip.split(',')[0].strip() | |
| return request.remote_addr or 'Unknown' | |
| ``` | |
| ## 📁 日志文件位置 | |
| ### 本地开发环境 | |
| - **控制台输出**: 实时显示 | |
| - **文件日志**: `logs/app.log`(自动滚动,保留10个备份) | |
| ### Hugging Face Spaces | |
| - **控制台输出**: Space 页面的 "Logs" 标签 | |
| - **文件日志**: 不持久化(容器重启后丢失) | |
| ## 🔍 查看日志 | |
| ### 查看实时日志(本地) | |
| ```bash | |
| # 启动应用(控制台输出) | |
| python3.12 app.py | |
| # 或使用 Gunicorn | |
| gunicorn -c gunicorn_config.py app:app | |
| # 实时监控日志文件 | |
| tail -f logs/app.log | |
| ``` | |
| ### 查看历史日志 | |
| ```bash | |
| # 查看最近 100 条日志 | |
| tail -n 100 logs/app.log | |
| # 搜索特定 IP 的访问记录 | |
| grep "203.0.113.1" logs/app.log | |
| # 统计各 IP 的访问次数 | |
| grep -oP '\[\K[0-9.]+(?=\])' logs/app.log | sort | uniq -c | sort -rn | |
| ``` | |
| ### Hugging Face Spaces | |
| 1. 进入你的 Space 页面 | |
| 2. 点击 "Logs" 标签 | |
| 3. 查看实时日志输出 | |
| 4. 可以搜索特定 IP 或时间段 | |
| ## 📊 日志分析示例 | |
| ### 统计最活跃的 IP 地址 | |
| ```bash | |
| # 提取所有 IP 并统计 | |
| grep -oP '\[\K[0-9.]+(?=\])' logs/app.log | sort | uniq -c | sort -rn | head -10 | |
| ``` | |
| 输出示例: | |
| ``` | |
| 245 203.0.113.1 | |
| 156 198.51.100.1 | |
| 89 192.0.2.1 | |
| 45 203.0.113.50 | |
| ``` | |
| ### 查看特定 IP 的访问历史 | |
| ```bash | |
| grep "\[203.0.113.1\]" logs/app.log | |
| ``` | |
| 输出示例: | |
| ``` | |
| [2025-10-16 18:58:23] INFO in app: [203.0.113.1] GET /api/health | UA: Mozilla/5.0 | |
| [2025-10-16 18:58:24] INFO in app: [203.0.113.1] GET /api/book/info | UA: Mozilla/5.0 | |
| [2025-10-16 18:58:25] INFO in app: [203.0.113.1] POST /api/search | UA: Mozilla/5.0 | |
| ``` | |
| ### 分析访问时间分布 | |
| ```bash | |
| # 按小时统计访问量 | |
| grep -oP '\[.*?\s+\K\d{2}' logs/app.log | sort | uniq -c | |
| ``` | |
| ### 统计不同浏览器的使用情况 | |
| ```bash | |
| grep "UA:" logs/app.log | grep -oP 'UA: \K[^/]+' | sort | uniq -c | sort -rn | |
| ``` | |
| ## 🧪 测试 IP 记录 | |
| ### 运行测试脚本 | |
| ```bash | |
| # 确保应用正在运行 | |
| python3.12 app.py | |
| # 在另一个终端运行测试 | |
| python3.12 test_ip_logging.py | |
| ``` | |
| ### 手动测试 | |
| ```bash | |
| # 普通请求 | |
| curl http://localhost:7860/api/health | |
| # 模拟代理请求(带 X-Forwarded-For) | |
| curl -H "X-Forwarded-For: 203.0.113.1" http://localhost:7860/api/health | |
| # 查看日志中记录的 IP | |
| tail logs/app.log | |
| ``` | |
| ## 🔒 隐私和安全 | |
| ### IP 地址处理 | |
| - IP 地址仅用于日志记录和调试 | |
| - 不会永久存储在数据库中 | |
| - 日志文件会自动滚动(最多保留10个备份) | |
| ### 隐私保护建议 | |
| 如果需要遵守 GDPR 等隐私法规: | |
| 1. **匿名化 IP**: 可以只记录 IP 的前缀 | |
| ```python | |
| # 例如:203.0.113.1 -> 203.0.113.0 | |
| ip_parts = ip.split('.') | |
| anonymized_ip = '.'.join(ip_parts[:3]) + '.0' | |
| ``` | |
| 2. **定期清理日志**: 设置日志保留期限 | |
| ```python | |
| # 在 gunicorn_config.py 中 | |
| max_log_age_days = 30 # 保留30天 | |
| ``` | |
| 3. **限制日志访问**: 确保只有授权人员可以访问日志 | |
| ## 📈 日志性能影响 | |
| ### 性能考虑 | |
| - IP 获取:几乎无性能影响(只是读取 HTTP 头部) | |
| - 日志写入:异步处理,不影响请求响应时间 | |
| - 文件大小:每个日志文件最大 10MB,自动滚动 | |
| ### 优化建议 | |
| 如果日志量很大: | |
| 1. **减少日志级别**(只记录重要信息) | |
| ```python | |
| app.logger.setLevel(logging.WARNING) # 只记录警告和错误 | |
| ``` | |
| 2. **只记录 API 请求**(跳过静态资源) | |
| ```python | |
| # 已在 before_request 中实现 | |
| if request.path.startswith('/api/'): | |
| app.logger.info(...) | |
| ``` | |
| 3. **使用日志聚合服务**(如 ELK、Datadog) | |
| ## 🛠️ 自定义日志格式 | |
| ### 修改 Flask 日志格式 | |
| 编辑 `app.py` 中的 `setup_logging()` 函数: | |
| ```python | |
| file_handler.setFormatter(logging.Formatter( | |
| '[%(asctime)s] [%(levelname)s] [IP:%(client_ip)s] %(message)s' | |
| )) | |
| ``` | |
| ### 修改 Gunicorn 日志格式 | |
| 编辑 `gunicorn_config.py` 中的 `access_log_format`: | |
| ```python | |
| access_log_format = ( | |
| '%(h)s - %({X-Forwarded-For}i)s ' | |
| '[%(t)s] "%(r)s" %(s)s %(b)s ' | |
| '"%(f)s" "%(a)s"' | |
| ) | |
| ``` | |
| ## 📚 相关文档 | |
| - [Flask 日志文档](https://flask.palletsprojects.com/en/latest/logging/) | |
| - [Gunicorn 配置文档](https://docs.gunicorn.org/en/stable/settings.html) | |
| - [HTTP 头部参考](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) | |
| ## 🎉 总结 | |
| 现在您的 Flask 应用已经可以: | |
| ✅ 记录客户端真实 IP 地址 | |
| ✅ 支持多种代理环境(Hugging Face Spaces、Cloudflare 等) | |
| ✅ 提供详细的访问日志 | |
| ✅ 支持日志分析和统计 | |
| 查看日志的快速命令: | |
| ```bash | |
| # 实时监控 | |
| tail -f logs/app.log | |
| # 搜索特定 IP | |
| grep "203.0.113.1" logs/app.log | |
| # 统计访问量 | |
| grep -c "GET /api/" logs/app.log | |
| ``` | |
| --- | |
| **最后更新**: 2025-10-16 | |
| **相关文件**: `app.py`, `gunicorn_config.py`, `test_ip_logging.py` | |