Spaces:
Running
Running
File size: 6,741 Bytes
16fa939 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 | # 📋 客户端 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`
|