# 摄影作品集网站 - 运维监控方案 ## 🎯 方案概述 这是一个**简单实用的日志管理方案**,专注于日志收集和问题修复。日志查看功能集成到管理后台,提供友好的Web界面。 ### 设计原则 - **集成化**: 日志查看功能集成到管理后台 - **用户友好**: 提供美观易用的Web界面 - **问题导向**: 专注于快速定位和修复问题 - **低维护成本**: 几乎零维护的方案 - **渐进式**: 后续可以根据需要扩展 ## 📋 核心组件 ### 日志管理架构 ``` ┌─────────────────────────────────────────────────────────────┐ │ 后端应用日志 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 错误日志 │ │ 访问日志 │ │ 业务日志 │ │ │ │ (JSON格式) │ │ (HTTP日志) │ │ (操作日志) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ 管理后台日志模块 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 日志查看器 │ │ 实时监控 │ │ 统计分析 │ │ │ │ (Web界面) │ │ (自动刷新) │ │ (图表展示) │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ ├─────────────────────────────────────────────────────────────┤ │ API接口层 │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ 日志查询API │ │ 统计API │ │ 搜索API │ │ │ │(/api/logs) │ │(/api/stats) │ │(/api/search)│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## 🔧 技术选择 ### 日志方案 - **日志存储**: 本地文件 (JSON格式) - **日志轮转**: lumberjack.v2 - **日志查看**: 管理后台Web界面 - **Trace ID**: 集成OpenTracing (已在架构文档中添加) - **权限控制**: 基于管理后台的用户权限 ### 集成方式 - **前端**: 管理后台的日志管理模块 - **后端**: Gin路由提供日志查询API - **认证**: 复用管理后台的登录认证 - **权限**: 仅管理员可访问日志功能 ## 📝 日志配置 ### 1. 应用日志配置 #### 日志配置文件 (config.yaml) ```yaml # config/config.yaml logger: level: "info" format: "json" output: "file" filename: "/app/logs/app.log" max_size: 100 # MB max_age: 7 # days compress: true # 可选:如果需要链路追踪 tracing: enabled: true service_name: "photography-backend" jaeger: endpoint: "http://localhost:14268/api/traces" sampling_rate: 1.0 ``` ### 2. 日志格式标准化 #### 统一的日志格式 ```json { "timestamp": "2024-01-15T10:30:00Z", "level": "info", "message": "Photo created successfully", "service": "photography-backend", "trace_id": "abc123def456", "request_id": "req-789", "user_id": "user-123", "operation": "create_photo", "photo_id": 1001, "duration": 0.5, "error": null } ``` ### 3. 日志分类 #### 三种核心日志类型 ```bash # 日志目录结构 logs/ ├── app.log # 应用日志 (所有级别) ├── error.log # 错误日志 (ERROR级别) └── access.log # HTTP访问日志 ``` #### 日志级别使用 ```go // 日志级别使用指南 logger.Info("正常业务操作") // 记录重要的业务操作 logger.Warn("需要关注的情况") // 记录警告信息 logger.Error("错误情况") // 记录错误信息 logger.Debug("调试信息") // 开发调试用 ``` ## 🚀 集成到管理后台 ### 1. 后端集成步骤 #### 在main.go中注册日志路由 ```go // cmd/server/main.go func main() { // ... 其他初始化代码 // 创建Gin引擎 r := gin.Default() // 注册API路由 apiGroup := r.Group("/api") { // 其他API路由... } // 注册管理后台路由 adminGroup := r.Group("/admin/api") { // 注册日志管理路由 admin.RegisterLogRoutes(adminGroup, "logs/app.log") // 其他管理后台路由... } r.Run(":8080") } ``` #### 日志处理器实现 ```go // internal/api/handlers/admin/logs_handler.go package admin import ( "bufio" "encoding/json" "net/http" "os" "strconv" "strings" "github.com/gin-gonic/gin" "photography-backend/pkg/middleware" ) // LogEntry 日志条目 type LogEntry struct { Timestamp string `json:"timestamp"` Level string `json:"level"` Message string `json:"message"` TraceID string `json:"trace_id,omitempty"` UserID string `json:"user_id,omitempty"` Operation string `json:"operation,omitempty"` } // LogHandler 日志处理器 type LogHandler struct { logFile string } // GetLogs 获取日志列表 func (h *LogHandler) GetLogs(c *gin.Context) { // 获取参数 levelFilter := c.Query("level") searchFilter := c.Query("search") traceID := c.Query("trace_id") lines := 100 if l := c.Query("lines"); l != "" { if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 1000 { lines = parsed } } // 读取日志文件 logs, err := h.readLogs(lines, levelFilter, searchFilter, traceID) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "读取日志失败", "details": err.Error(), }) return } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "logs": logs, "total": len(logs), }, }) } // GetLogStats 获取日志统计 func (h *LogHandler) GetLogStats(c *gin.Context) { stats := map[string]int{ "total": 0, "error": 0, "warn": 0, "info": 0, "debug": 0, } file, err := os.Open(h.logFile) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "error": "读取日志文件失败", "details": err.Error(), }) return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if line == "" { continue } stats["total"]++ // 解析日志级别 var entry LogEntry if err := json.Unmarshal([]byte(line), &entry); err == nil { if count, exists := stats[entry.Level]; exists { stats[entry.Level] = count + 1 } } } c.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": stats, }) } // RegisterLogRoutes 注册日志相关路由 func RegisterLogRoutes(r *gin.RouterGroup, logFile string) { logHandler := NewLogHandler(logFile) // 需要管理员权限的路由组 adminGroup := r.Group("") adminGroup.Use(middleware.RequireAuth()) // 需要登录 adminGroup.Use(middleware.RequireAdmin()) // 需要管理员权限 { adminGroup.GET("/logs", logHandler.GetLogs) adminGroup.GET("/logs/stats", logHandler.GetLogStats) } } ``` ### 2. 前端集成步骤 #### 管理后台日志查看组件 ```javascript // admin/src/pages/Logs/LogViewer.jsx import React, { useState, useEffect } from 'react'; import { Card, Table, Select, Input, Button, Tag, Space, Statistic, Row, Col, message } from 'antd'; const LogViewer = () => { const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(false); const [filters, setFilters] = useState({ level: '', search: '', trace_id: '', lines: 100, }); const [stats, setStats] = useState({}); const [autoRefresh, setAutoRefresh] = useState(false); // 日志级别配置 const levelConfig = { error: { color: 'red', icon: '❌' }, warn: { color: 'orange', icon: '⚠️' }, info: { color: 'blue', icon: 'ℹ️' }, debug: { color: 'default', icon: '🐛' }, }; // 获取日志数据 const fetchLogs = async () => { setLoading(true); try { const response = await adminApi.get('/logs', { params: filters }); setLogs(response.data.data.logs || []); } catch (error) { message.error('获取日志失败'); } finally { setLoading(false); } }; // 获取统计数据 const fetchStats = async () => { try { const response = await adminApi.get('/logs/stats'); setStats(response.data.data || {}); } catch (error) { console.error('获取统计数据失败', error); } }; useEffect(() => { fetchLogs(); fetchStats(); }, [filters]); // 自动刷新 useEffect(() => { let interval; if (autoRefresh) { interval = setInterval(() => { fetchLogs(); fetchStats(); }, 5000); } return () => interval && clearInterval(interval); }, [autoRefresh, filters]); // 表格列配置 const columns = [ { title: '时间', dataIndex: 'timestamp', key: 'timestamp', width: 180, render: (timestamp) => new Date(timestamp).toLocaleString(), }, { title: '级别', dataIndex: 'level', key: 'level', width: 80, render: (level) => { const config = levelConfig[level] || levelConfig.info; return ( {config.icon} {level.toUpperCase()} ); }, }, { title: 'Trace ID', dataIndex: 'trace_id', key: 'trace_id', width: 120, render: (traceId) => traceId ? ( ) : '-', }, { title: '消息', dataIndex: 'message', key: 'message', ellipsis: true, render: (message, record) => { // 高亮搜索关键词 if (filters.search && message.toLowerCase().includes(filters.search.toLowerCase())) { const regex = new RegExp(`(${filters.search})`, 'gi'); const parts = message.split(regex); return parts.map((part, index) => part.toLowerCase() === filters.search.toLowerCase() ? {part} : part ); } return message; }, }, ]; return (
{/* 统计卡片 */} {/* 过滤控件 */} setFilters(prev => ({ ...prev, search: e.target.value }))} /> setFilters(prev => ({ ...prev, trace_id: e.target.value }))} /> {/* 日志表格 */} `${record.timestamp}-${index}`} pagination={{ showSizeChanger: false, showQuickJumper: true, showTotal: (total) => `共 ${total} 条日志`, }} scroll={{ x: 800 }} /> ); }; export default LogViewer; ``` ### 3. 配置更新 #### 更新应用配置文件 ```yaml # config/config.yaml app: name: "photography-backend" version: "1.0.0" environment: "production" logger: level: "info" format: "json" output: "file" filename: "logs/app.log" max_size: 100 max_age: 7 compress: true tracing: enabled: true service_name: "photography-backend" sampling_rate: 1.0 admin: log_access: true # 启用日志访问功能 log_retention_days: 30 # 日志保留天数 ``` ### 4. 一键部署脚本 #### 部署管理后台日志功能 ```bash #!/bin/bash # deploy-admin-logs.sh echo "🚀 部署管理后台日志功能..." # 创建必要目录 mkdir -p logs mkdir -p admin/src/pages/Logs # 确保日志文件存在 touch logs/app.log chmod 664 logs/app.log # 创建日志轮转配置 cat > /etc/logrotate.d/photography-backend << 'EOF' /path/to/photography/logs/*.log { daily rotate 30 compress delaycompress missingok notifempty create 664 app app postrotate systemctl reload photography-backend endscript } EOF # 设置权限 chown -R app:app logs/ chmod 755 logs/ echo "✅ 管理后台日志功能部署完成!" echo "" echo "📋 访问方式:" echo " 1. 登录管理后台: http://localhost:8080/admin" echo " 2. 进入日志管理页面" echo " 3. 使用管理员账号访问" echo "" echo "🔧 API地址:" echo " 日志列表: GET /admin/api/logs" echo " 日志统计: GET /admin/api/logs/stats" ``` ## 🔧 故障排查流程 ### 1. 问题诊断步骤 ```bash # 快速诊断脚本 #!/bin/bash # scripts/quick-diagnosis.sh echo "🔍 快速问题诊断" # 1. 检查服务状态 echo "1. 检查服务状态..." docker-compose ps # 2. 检查最近错误 echo "2. 最近10条错误日志..." tail -n 1000 logs/app.log | grep -i error | tail -10 | jq -r '.timestamp + " " + .message' # 3. 检查磁盘空间 echo "3. 检查磁盘空间..." df -h # 4. 检查日志文件大小 echo "4. 检查日志文件大小..." ls -lh logs/ # 5. 检查内存使用 echo "5. 检查内存使用..." docker stats --no-stream photography-backend echo "✅ 诊断完成" ``` ### 2. 常见问题解决 #### 问题1:无法找到错误原因 ```bash # 1. 获取完整的错误上下文 grep -B 5 -A 5 "error_message" logs/app.log # 2. 按时间范围查找 grep "2024-01-15T10:" logs/app.log | grep -i error ``` #### 问题2:需要追踪特定用户的操作 ```bash # 按用户ID查找 grep "user-123" logs/app.log | jq -r '.timestamp + " " + .message' # 按操作类型查找 grep "create_photo" logs/app.log | jq -r '.timestamp + " " + .message' ``` #### 问题3:日志文件太大 ```bash # 手动轮转日志 mv logs/app.log logs/app.log.old sudo systemctl restart photography-backend # 或者使用logrotate sudo logrotate -f /etc/logrotate.d/photography-backend ``` ## 🎯 总结 这个集成到管理后台的日志方案提供了: ### ✨ 核心特性 - **🏢 集成化管理** - 完全集成到管理后台,统一的用户体验 - **🔐 权限控制** - 基于管理后台的认证和授权体系 - **📊 专业界面** - 使用React组件,美观且专业 - **🔍 强大搜索** - 支持关键词、级别、Trace ID多维度过滤 - **⏰ 实时监控** - 自动刷新功能,实时观察系统状态 - **📈 统计分析** - 直观的统计卡片,快速了解系统健康状况 ### 🚀 部署简单 1. 运行部署脚本: `./deploy-admin-logs.sh` 2. 访问管理后台: `http://localhost:8080/admin` 3. 登录管理员账号,进入日志管理页面 ### 💡 使用场景 - **错误排查**: 管理员快速定位和分析错误日志 - **性能监控**: 通过Trace ID追踪完整请求链路 - **运营监控**: 实时观察系统运行状态 - **历史分析**: 搜索和分析历史日志数据 - **团队协作**: 多个管理员可以同时查看和分析日志 ### 🔧 技术优势 - **集成性**: 完全集成到现有管理后台 - **安全性**: 基于管理后台的权限控制 - **专业性**: 使用成熟的UI组件库 - **可扩展**: 易于添加新的日志分析功能 - **用户友好**: 直观的界面,无需学习成本 ### 📋 API接口 - `GET /admin/api/logs` - 获取日志列表 - `GET /admin/api/logs/stats` - 获取日志统计 这就是最适合生产环境的日志管理方案 - **专业、安全、易用**!