日志管理 #

概述 #

日志是运维和故障排查的重要工具,Caddy 提供了灵活的日志配置,支持多种输出方式和格式。

text
┌─────────────────────────────────────────────────────────────┐
│                    Caddy 日志架构                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│    请求 → Caddy → 日志处理器                                 │
│                    ↓                                        │
│              ┌─────────────┐                                │
│              │  格式化器    │                                │
│              └──────┬──────┘                                │
│                     ↓                                       │
│              ┌─────────────┐                                │
│              │   输出器    │                                │
│              └──────┬──────┘                                │
│                     ↓                                       │
│    ┌─────────┬─────────┬─────────┬─────────┐               │
│    │  文件   │  控制台  │  Syslog  │  网络   │               │
│    └─────────┴─────────┴─────────┴─────────┘               │
│                                                             │
└─────────────────────────────────────────────────────────────┘

基本配置 #

最简单的日志配置 #

caddyfile
example.com {
    log
    respond "Hello"
}

文件日志 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
    }
    respond "Hello"
}

完整日志配置 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 10
            roll_keep_for 168h
        }
        format json
    }
    respond "Hello"
}

日志输出 #

文件输出 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
    }
}

文件轮转配置 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log {
            # 单个文件最大大小
            roll_size 100mb
            
            # 保留文件数量
            roll_keep 10
            
            # 保留时间
            roll_keep_for 168h  # 7 天
            
            # 文件名格式
            roll_name_format "access-2006-01-02T15-04-05.000.log"
        }
    }
}

控制台输出 #

caddyfile
example.com {
    log {
        output stdout
    }
}

标准错误输出 #

caddyfile
example.com {
    log {
        output stderr
    }
}

禁用日志 #

caddyfile
example.com {
    log off
}

多输出 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
        output stdout
    }
}

日志格式 #

JSON 格式 #

caddyfile
example.com {
    log {
        format json
    }
}

JSON 格式输出示例:

json
{
    "level": "info",
    "ts": 1704067200.123456,
    "logger": "http.log.access",
    "msg": "handled request",
    "request": {
        "remote_ip": "192.168.1.100",
        "remote_port": "54321",
        "proto": "HTTP/2.0",
        "method": "GET",
        "host": "example.com",
        "uri": "/api/users",
        "headers": {
            "User-Agent": ["curl/7.68.0"],
            "Accept": ["*/*"]
        }
    },
    "duration": 0.001234,
    "size": 1234,
    "status": 200
}

控制台格式 #

caddyfile
example.com {
    log {
        format console
    }
}

自定义格式 #

caddyfile
example.com {
    log {
        format console {
            # 时间格式
            time_format "2006-01-02 15:04:05"
            
            # 时间戳格式
            time_format "rfc3339"
        }
    }
}

自定义日志字段 #

caddyfile
example.com {
    log {
        format json {
            # 添加自定义字段
            time_format "rfc3339"
        }
    }
}

日志级别 #

全局日志级别 #

caddyfile
{
    # 全局日志级别
    log default {
        level DEBUG
    }
}

example.com {
    respond "Hello"
}

日志级别说明 #

级别 说明
DEBUG 调试信息
INFO 一般信息
WARN 警告信息
ERROR 错误信息
PANIC 严重错误
FATAL 致命错误

站点日志级别 #

caddyfile
example.com {
    log {
        level DEBUG
    }
}

日志字段 #

常用日志字段 #

caddyfile
example.com {
    log {
        format json
    }
    
    # 可用的日志字段:
    # {request}      - 完整请求信息
    # {duration}     - 请求处理时间
    # {size}         - 响应大小
    # {status}       - 响应状态码
    # {remote_host}  - 客户端 IP
    # {host}         - 请求主机
    # {uri}          - 请求 URI
    # {method}       - 请求方法
    # {proto}        - 协议版本
    # {headers}      - 请求头
}

自定义日志消息 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
        format json
    }
    
    # 在响应中记录日志
    handle /api/* {
        log msg "API request received"
        reverse_proxy localhost:3000
    }
}

条件日志 #

基于路径的日志 #

caddyfile
example.com {
    # API 路径记录详细日志
    handle /api/* {
        log {
            output file /var/log/caddy/api.log
            format json
        }
        reverse_proxy localhost:3000
    }
    
    # 静态文件简单日志
    handle {
        log {
            output file /var/log/caddy/static.log
            format console
        }
        file_server
    }
}

基于状态码的日志 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log
    }
    
    # 错误日志单独记录
    handle_errors {
        log {
            output file /var/log/caddy/error.log
            level ERROR
        }
        respond "{http.error.status_code} {http.error.status_text}"
    }
}

排除健康检查 #

caddyfile
example.com {
    # 排除健康检查日志
    @health path /health
    log @health off
    
    log {
        output file /var/log/caddy/access.log
    }
    
    respond "Hello"
}

全局日志配置 #

全局日志 #

caddyfile
{
    log {
        output file /var/log/caddy/global.log
        format json
    }
}

example.com {
    respond "Hello"
}

多日志器 #

caddyfile
{
    log main {
        output file /var/log/caddy/main.log
    }
    
    log debug {
        output file /var/log/caddy/debug.log
        level DEBUG
    }
}

example.com {
    log main
    respond "Hello"
}

日志分析 #

使用 jq 分析 JSON 日志 #

bash
# 统计状态码分布
cat /var/log/caddy/access.log | jq -r '.status' | sort | uniq -c | sort -rn

# 统计请求最多的路径
cat /var/log/caddy/access.log | jq -r '.request.uri' | sort | uniq -c | sort -rn | head -20

# 统计慢请求(>1s)
cat /var/log/caddy/access.log | jq 'select(.duration > 1)'

# 统计错误请求
cat /var/log/caddy/access.log | jq 'select(.status >= 400)'

# 统计 IP 访问量
cat /var/log/caddy/access.log | jq -r '.request.remote_ip' | sort | uniq -c | sort -rn | head -20

# 统计 User-Agent
cat /var/log/caddy/access.log | jq -r '.request.headers.User-Agent[0]' | sort | uniq -c | sort -rn | head -10

使用 grep 分析文本日志 #

bash
# 查找错误
grep -i error /var/log/caddy/access.log

# 查找特定 IP
grep "192.168.1.100" /var/log/caddy/access.log

# 查找特定路径
grep "/api/users" /var/log/caddy/access.log

# 统计状态码
grep -o '"status":[0-9]*' /var/log/caddy/access.log | cut -d: -f2 | sort | uniq -c

实时监控日志 #

bash
# 实时查看日志
tail -f /var/log/caddy/access.log

# 实时查看并过滤
tail -f /var/log/caddy/access.log | grep --line-buffered "error"

# 实时查看 JSON 日志
tail -f /var/log/caddy/access.log | jq

# 实时统计
tail -f /var/log/caddy/access.log | jq -r '.status' | awk '{count[$1]++} END {for (s in count) print s, count[s]}'

日志轮转 #

使用 logrotate #

bash
# /etc/logrotate.d/caddy
/var/log/caddy/*.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0644 caddy caddy
    postrotate
        systemctl reload caddy
    endscript
}

Caddy 内置轮转 #

caddyfile
example.com {
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 14
            roll_keep_for 336h
        }
    }
}

完整示例 #

生产环境日志配置 #

caddyfile
{
    log {
        output file /var/log/caddy/caddy.log {
            roll_size 50mb
            roll_keep 10
        }
        format json
        level INFO
    }
}

example.com {
    # 访问日志
    log {
        output file /var/log/caddy/access.log {
            roll_size 100mb
            roll_keep 30
            roll_keep_for 720h
        }
        format json
    }
    
    # API 日志
    handle /api/* {
        log {
            output file /var/log/caddy/api.log {
                roll_size 50mb
                roll_keep 14
            }
            format json
        }
        reverse_proxy localhost:3000
    }
    
    # 静态文件
    handle {
        root * /var/www/html
        file_server
    }
    
    # 错误日志
    handle_errors {
        log {
            output file /var/log/caddy/error.log
            level ERROR
        }
        respond "{http.error.status_code} {http.error.status_text}"
    }
}

多站点日志配置 #

caddyfile
# 共享日志配置
(logging) {
    log {
        output file /var/log/caddy/{host}.log {
            roll_size 50mb
            roll_keep 14
        }
        format json
    }
}

site1.example.com {
    import logging
    root * /var/www/site1
    file_server
}

site2.example.com {
    import logging
    reverse_proxy localhost:3000
}

api.example.com {
    log {
        output file /var/log/caddy/api.log {
            roll_size 100mb
            roll_keep 30
        }
        format json
    }
    reverse_proxy localhost:3001
}

开发环境日志配置 #

caddyfile
{
    debug
}

localhost {
    log {
        output stdout
        format console {
            time_format "15:04:05"
        }
        level DEBUG
    }
    
    respond "Development server"
}

日志最佳实践 #

1. 使用 JSON 格式 #

caddyfile
log {
    format json  # 便于解析和分析
}

2. 合理设置轮转 #

caddyfile
output file /var/log/caddy/access.log {
    roll_size 100mb
    roll_keep 14
}

3. 分离不同类型的日志 #

caddyfile
# 访问日志
log {
    output file /var/log/caddy/access.log
}

# 错误日志
handle_errors {
    log {
        output file /var/log/caddy/error.log
        level ERROR
    }
}

4. 排除不必要的日志 #

caddyfile
# 排除健康检查
@health path /health
log @health off

5. 设置合适的日志级别 #

caddyfile
# 生产环境
log {
    level INFO
}

# 开发环境
log {
    level DEBUG
}

下一步 #

现在你已经掌握了日志管理配置,接下来学习 URL 重写 了解如何处理 URL 路由!

最后更新:2026-03-28