在生產環境中,日誌管理是系統穩定性與可維護性的關鍵基石。一個配置不當的日誌系統可能導致磁碟空間爆滿、系統崩潰,甚至影響業務運作。本文將深入探討 Docker、PM2 以及 Go、Node.js、Python 等常見運行環境的日誌管理最佳實踐,幫助你避免「日誌爆炸」的災難。

為什麼日誌管理如此重要?

在生產環境中,日誌管理不當可能導致以下問題:

  • 磁碟空間耗盡: 未限制的日誌檔案會持續增長,最終佔滿磁碟空間
  • 系統效能下降: 過度的日誌寫入會影響 I/O 效能
  • 除錯困難: 日誌過多或過少都會影響問題排查效率
  • 合規風險: 敏感資訊洩漏或日誌保存期限不符合規範

[!IMPORTANT]
生產環境的日誌策略應該在可觀測性資源消耗之間取得平衡。過多的日誌會浪費資源,過少的日誌則無法有效除錯。

Docker 日誌管理

Docker 日誌驅動概述

Docker 支援多種日誌驅動 (Logging Driver),每種驅動適用於不同的場景:

驅動名稱 適用場景 優點 缺點
json-file 本地開發、小型部署 預設驅動、簡單易用 需手動管理日誌輪替
syslog 集中式日誌管理 與系統日誌整合 需額外配置 syslog 服務
journald systemd 系統 與 systemd 深度整合 僅限 Linux
fluentd 大規模日誌聚合 強大的日誌處理能力 需部署 Fluentd
awslogs AWS 環境 無縫整合 CloudWatch 僅限 AWS
gcplogs GCP 環境 無縫整合 Cloud Logging 僅限 GCP

配置 Docker Daemon 全域日誌設定

編輯 /etc/docker/daemon.json 設定全域日誌策略:

1
2
3
4
5
6
7
8
9
10
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3",
"compress": "true",
"labels": "production,backend",
"env": "APP_ENV,APP_VERSION"
}
}

參數說明:

  • max-size: 單一日誌檔案最大大小 (10m = 10MB)
  • max-file: 保留的日誌檔案數量
  • compress: 是否壓縮舊日誌檔案
  • labels: 在日誌中包含的容器標籤
  • env: 在日誌中包含的環境變數

修改後重啟 Docker 服務:

1
2
3
4
5
# macOS (Docker Desktop)
# 在 Docker Desktop 設定中修改,或重啟 Docker Desktop

# Linux
sudo systemctl restart docker

[!WARNING]
修改 daemon.json 會影響所有新建立的容器。現有容器需要重新建立才會套用新設定。

Docker Compose 中的日誌配置

docker-compose.yml 中為個別服務配置日誌:

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
version: '3.8'

services:
# 範例 1: 基本日誌配置
web:
image: nginx:alpine
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
compress: "true"

# 範例 2: 使用 syslog 驅動
api:
image: myapp:latest
logging:
driver: "syslog"
options:
syslog-address: "tcp://192.168.1.100:514"
tag: "api-service"
syslog-format: "rfc5424"

# 範例 3: 使用 Fluentd 驅動
backend:
image: backend:latest
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.backend"
fluentd-async-connect: "true"
fluentd-retry-wait: "1s"
fluentd-max-retries: "30"

# 範例 4: AWS CloudWatch Logs
worker:
image: worker:latest
logging:
driver: "awslogs"
options:
awslogs-region: "ap-northeast-1"
awslogs-group: "my-app-logs"
awslogs-stream: "worker-service"
awslogs-create-group: "true"

Docker 日誌查看與管理指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看容器日誌
docker logs <container_id>

# 即時追蹤日誌 (類似 tail -f)
docker logs -f <container_id>

# 查看最近 100 行日誌
docker logs --tail 100 <container_id>

# 查看特定時間範圍的日誌
docker logs --since 2026-01-25T10:00:00 <container_id>
docker logs --since 1h <container_id> # 最近 1 小時

# 查看容器日誌檔案位置
docker inspect --format='{{.LogPath}}' <container_id>

# 清理特定容器的日誌 (需要停止容器)
truncate -s 0 $(docker inspect --format='{{.LogPath}}' <container_id>)

Docker 日誌輪替最佳實踐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# docker-compose.prod.yml - 生產環境配置
version: '3.8'

services:
app:
image: myapp:${VERSION}
logging:
driver: "json-file"
options:
max-size: "50m" # 單檔最大 50MB
max-file: "5" # 保留 5 個檔案 (總計 250MB)
compress: "true" # 壓縮舊日誌
labels: "env,service"
env: "APP_VERSION"
labels:
env: "production"
service: "api"
environment:
- APP_VERSION=1.2.3

PM2 日誌管理

PM2 日誌配置

PM2 是 Node.js 應用的流行進程管理器,提供強大的日誌管理功能。

基本配置檔案 (ecosystem.config.js)

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
module.exports = {
apps: [{
name: 'api-server',
script: './dist/server.js',
instances: 4,
exec_mode: 'cluster',

// 日誌配置
error_file: '/var/log/pm2/api-error.log',
out_file: '/var/log/pm2/api-out.log',
log_file: '/var/log/pm2/api-combined.log',

// 日誌日期格式
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',

// 合併日誌 (所有實例寫入同一個檔案)
merge_logs: true,

// 時間戳記
time: true,

// 環境變數
env: {
NODE_ENV: 'production',
LOG_LEVEL: 'info'
}
}]
};

PM2 日誌輪替配置

安裝 PM2 日誌輪替模組:

1
pm2 install pm2-logrotate

配置日誌輪替參數:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 設定最大日誌檔案大小 (預設 10MB)
pm2 set pm2-logrotate:max_size 50M

# 設定保留的日誌檔案數量 (預設 10)
pm2 set pm2-logrotate:retain 7

# 設定壓縮舊日誌
pm2 set pm2-logrotate:compress true

# 設定輪替間隔 (每天輪替)
pm2 set pm2-logrotate:rotateInterval '0 0 * * *'

# 設定日誌檔案日期格式
pm2 set pm2-logrotate:dateFormat 'YYYY-MM-DD_HH-mm-ss'

# 查看當前配置
pm2 conf pm2-logrotate

PM2 日誌管理指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看即時日誌
pm2 logs

# 查看特定應用的日誌
pm2 logs api-server

# 查看錯誤日誌
pm2 logs --err

# 查看輸出日誌
pm2 logs --out

# 查看最近 100 行日誌
pm2 logs --lines 100

# 清空所有日誌
pm2 flush

# 重新載入日誌 (不重啟應用)
pm2 reloadLogs

PM2 進階日誌配置

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
// ecosystem.config.js - 進階配置
module.exports = {
apps: [{
name: 'api-server',
script: './dist/server.js',
instances: 'max',
exec_mode: 'cluster',

// 分離錯誤與輸出日誌
error_file: '/var/log/pm2/api-error.log',
out_file: '/var/log/pm2/api-out.log',

// 停用合併日誌 (每個實例獨立日誌檔案)
merge_logs: false,

// 日誌格式化
log_type: 'json', // JSON 格式日誌,便於解析

// 環境變數
env_production: {
NODE_ENV: 'production',
LOG_LEVEL: 'warn', // 生產環境僅記錄警告以上等級
LOG_FORMAT: 'json'
},

env_development: {
NODE_ENV: 'development',
LOG_LEVEL: 'debug',
LOG_FORMAT: 'pretty'
}
}, {
name: 'worker',
script: './dist/worker.js',
instances: 2,

// 停用標準輸出日誌 (僅記錄錯誤)
out_file: '/dev/null',
error_file: '/var/log/pm2/worker-error.log',

// 自訂日誌路徑
log_date_format: 'YYYY-MM-DD HH:mm:ss.SSS'
}]
};

Go 應用日誌管理

使用標準函式庫 log/slog

Go 1.21+ 引入的 log/slog 提供結構化日誌功能:

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
package main

import (
"log/slog"
"os"
)

func main() {
// 配置 JSON 格式日誌輸出
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo, // 設定日誌等級
AddSource: true, // 包含原始碼位置
}))

// 設定為全域 logger
slog.SetDefault(logger)

// 使用結構化日誌
slog.Info("server started",
"port", 8080,
"env", "production",
)

slog.Error("database connection failed",
"error", err,
"host", "localhost",
"port", 5432,
)
}

生產環境日誌配置

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
package logger

import (
"io"
"log/slog"
"os"
"path/filepath"
"time"

"gopkg.in/natefinch/lumberjack.v2"
)

// InitLogger 初始化生產環境日誌
func InitLogger(logDir string, level slog.Level) *slog.Logger {
// 確保日誌目錄存在
if err := os.MkdirAll(logDir, 0755); err != nil {
panic(err)
}

// 配置日誌輪替
logFile := &lumberjack.Logger{
Filename: filepath.Join(logDir, "app.log"),
MaxSize: 100, // MB
MaxBackups: 5, // 保留 5 個備份
MaxAge: 30, // 保留 30 天
Compress: true, // 壓縮舊日誌
LocalTime: true, // 使用本地時間
}

// 同時輸出到檔案和標準輸出
multiWriter := io.MultiWriter(os.Stdout, logFile)

// 建立 JSON handler
handler := slog.NewJSONHandler(multiWriter, &slog.HandlerOptions{
Level: level,
AddSource: true,
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// 自訂時間格式
if a.Key == slog.TimeKey {
return slog.String("timestamp",
a.Value.Time().Format(time.RFC3339))
}
return a
},
})

return slog.New(handler)
}

// 使用範例
func main() {
logger := InitLogger("/var/log/myapp", slog.LevelInfo)
slog.SetDefault(logger)

slog.Info("application started",
"version", "1.0.0",
"env", "production",
)
}

使用 Zap 高效能日誌庫

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
package main

import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)

func InitZapLogger() *zap.Logger {
// 日誌輪替配置
logWriter := &lumberjack.Logger{
Filename: "/var/log/myapp/app.log",
MaxSize: 100, // MB
MaxBackups: 5,
MaxAge: 30, // days
Compress: true,
}

// Encoder 配置
encoderConfig := zapcore.EncoderConfig{
TimeKey: "timestamp",
LevelKey: "level",
NameKey: "logger",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}

// 建立 Core
core := zapcore.NewCore(
zapcore.NewJSONEncoder(encoderConfig),
zapcore.AddSync(logWriter),
zapcore.InfoLevel,
)

// 建立 Logger
logger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))

return logger
}

func main() {
logger := InitZapLogger()
defer logger.Sync()

logger.Info("server started",
zap.Int("port", 8080),
zap.String("env", "production"),
)

logger.Error("failed to connect database",
zap.Error(err),
zap.String("host", "localhost"),
)
}

Node.js 應用日誌管理

使用 Winston 日誌庫

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
// logger.js
const winston = require('winston');
require('winston-daily-rotate-file');

// 日誌輪替配置
const dailyRotateFileTransport = new winston.transports.DailyRotateFile({
filename: '/var/log/myapp/app-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '100m',
maxFiles: '30d',
compress: true,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
});

// 錯誤日誌單獨輪替
const errorRotateFileTransport = new winston.transports.DailyRotateFile({
filename: '/var/log/myapp/error-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxSize: '100m',
maxFiles: '30d',
compress: true,
level: 'error',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
});

// 建立 logger
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json()
),
defaultMeta: {
service: 'api-server',
env: process.env.NODE_ENV
},
transports: [
dailyRotateFileTransport,
errorRotateFileTransport
]
});

// 開發環境額外輸出到 console
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
}));
}

module.exports = logger;

使用範例

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
// app.js
const logger = require('./logger');

// 基本日誌
logger.info('Server started', { port: 3000 });

// 錯誤日誌
logger.error('Database connection failed', {
error: err.message,
stack: err.stack,
host: 'localhost',
port: 5432
});

// 警告日誌
logger.warn('High memory usage detected', {
usage: '85%',
threshold: '80%'
});

// 除錯日誌 (僅在 LOG_LEVEL=debug 時輸出)
logger.debug('Request received', {
method: 'POST',
path: '/api/users',
body: req.body
});

Python 應用日誌管理

使用標準函式庫 logging

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
# logger.py
import logging
import logging.handlers
import os
from pathlib import Path

def setup_logger(
name: str,
log_dir: str = '/var/log/myapp',
level: int = logging.INFO
) -> logging.Logger:
"""設定生產環境日誌"""

# 確保日誌目錄存在
Path(log_dir).mkdir(parents=True, exist_ok=True)

# 建立 logger
logger = logging.getLogger(name)
logger.setLevel(level)

# 避免重複添加 handler
if logger.handlers:
return logger

# 日誌格式
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - '
'%(filename)s:%(lineno)d - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)

# 輪替檔案 handler (按大小輪替)
file_handler = logging.handlers.RotatingFileHandler(
filename=os.path.join(log_dir, 'app.log'),
maxBytes=100 * 1024 * 1024, # 100MB
backupCount=5,
encoding='utf-8'
)
file_handler.setLevel(level)
file_handler.setFormatter(formatter)

# 錯誤日誌單獨檔案
error_handler = logging.handlers.RotatingFileHandler(
filename=os.path.join(log_dir, 'error.log'),
maxBytes=100 * 1024 * 1024,
backupCount=5,
encoding='utf-8'
)
error_handler.setLevel(logging.ERROR)
error_handler.setFormatter(formatter)

# Console handler (開發環境)
if os.getenv('ENV') != 'production':
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

logger.addHandler(file_handler)
logger.addHandler(error_handler)

return logger

使用時間輪替

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
import logging
import logging.handlers

def setup_daily_logger(name: str, log_dir: str = '/var/log/myapp'):
"""設定按日期輪替的日誌"""

logger = logging.getLogger(name)
logger.setLevel(logging.INFO)

# 按時間輪替 (每天午夜輪替)
time_handler = logging.handlers.TimedRotatingFileHandler(
filename=os.path.join(log_dir, 'app.log'),
when='midnight', # 每天午夜輪替
interval=1, # 間隔 1 天
backupCount=30, # 保留 30 天
encoding='utf-8',
utc=False # 使用本地時間
)

# 日誌檔名格式: app.log.2026-01-25
time_handler.suffix = '%Y-%m-%d'

formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s'
)
time_handler.setFormatter(formatter)

logger.addHandler(time_handler)
return logger

使用 structlog 結構化日誌

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
# logger.py
import structlog
import logging
import logging.handlers
from pathlib import Path

def setup_structlog(log_dir: str = '/var/log/myapp'):
"""設定結構化日誌"""

Path(log_dir).mkdir(parents=True, exist_ok=True)

# 配置標準 logging
logging.basicConfig(
format="%(message)s",
handlers=[
logging.handlers.RotatingFileHandler(
filename=f"{log_dir}/app.log",
maxBytes=100 * 1024 * 1024,
backupCount=5
)
],
level=logging.INFO,
)

# 配置 structlog
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.JSONRenderer()
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
cache_logger_on_first_use=True,
)

return structlog.get_logger()

# 使用範例
logger = setup_structlog()

logger.info("server_started", port=8000, env="production")
logger.error("database_error", error=str(e), host="localhost")

集中式日誌管理方案

ELK Stack (Elasticsearch + Logstash + Kibana)

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
# docker-compose.elk.yml
version: '3.8'

services:
elasticsearch:
image: elasticsearch:8.11.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms2g -Xmx2g"
- xpack.security.enabled=false
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
ports:
- "9200:9200"

logstash:
image: logstash:8.11.0
volumes:
- ./logstash/pipeline:/usr/share/logstash/pipeline
- ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
ports:
- "5000:5000"
- "5044:5044"
depends_on:
- elasticsearch

kibana:
image: kibana:8.11.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
depends_on:
- elasticsearch

volumes:
elasticsearch_data:

Logstash 配置範例:

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
# logstash/pipeline/logstash.conf
input {
# 接收 Docker 日誌
tcp {
port => 5000
codec => json
}

# 接收 Filebeat 日誌
beats {
port => 5044
}
}

filter {
# 解析 JSON 日誌
if [message] =~ /^\{.*\}$/ {
json {
source => "message"
}
}

# 添加時間戳記
date {
match => [ "timestamp", "ISO8601" ]
target => "@timestamp"
}

# 移除不需要的欄位
mutate {
remove_field => [ "host", "agent" ]
}
}

output {
elasticsearch {
hosts => ["elasticsearch:9200"]
index => "app-logs-%{+YYYY.MM.dd}"
}

# 除錯用
stdout {
codec => rubydebug
}
}

Loki + Promtail + Grafana

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
# docker-compose.loki.yml
version: '3.8'

services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- ./loki/config.yml:/etc/loki/config.yml
- loki_data:/loki
command: -config.file=/etc/loki/config.yml

promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail/config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki

grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
depends_on:
- loki

volumes:
loki_data:
grafana_data:

日誌管理最佳實踐總結

1. 日誌等級策略

環境 建議等級 說明
開發 DEBUG 詳細除錯資訊
測試 INFO 一般資訊與警告
預發布 WARN 警告與錯誤
生產 ERROR 僅記錄錯誤

2. 日誌輪替建議

1
2
3
4
5
6
7
8
9
# 按大小輪替
- 單檔大小: 50-100MB
- 保留檔案數: 5-10 個
- 壓縮舊日誌: 是

# 按時間輪替
- 輪替週期: 每天
- 保留天數: 30-90 天
- 壓縮舊日誌: 是

3. 敏感資訊處理

[!CAUTION]
絕對不要在日誌中記錄以下資訊:

  • 密碼、API 金鑰、Token
  • 信用卡號、身分證字號
  • 個人隱私資料 (Email、電話等)
1
2
3
4
5
// ❌ 錯誤做法
logger.info('User login', { password: user.password });

// ✅ 正確做法
logger.info('User login', { userId: user.id, username: user.name });

4. 效能考量

  • 使用非同步日誌寫入
  • 避免在高頻路徑記錄過多日誌
  • 使用結構化日誌格式 (JSON)
  • 考慮使用日誌取樣 (高流量場景)

5. 監控與告警

1
2
3
4
5
6
7
8
9
10
# 建議監控指標
- 日誌檔案大小
- 磁碟使用率
- 日誌寫入速率
- 錯誤日誌數量

# 告警閾值
- 磁碟使用率 > 80%
- 錯誤日誌激增 (5 分鐘內 > 100 筆)
- 日誌寫入失敗

快速檢查清單

在部署生產環境前,請確認以下項目:

  • 已設定日誌輪替 (按大小或時間)
  • 已限制日誌檔案總大小
  • 已配置適當的日誌等級
  • 已移除敏感資訊
  • 已設定日誌監控與告警
  • 已測試日誌輪替是否正常運作
  • 已配置集中式日誌管理 (可選)
  • 已設定日誌備份策略

結語

日誌管理是生產環境運維的基礎設施,一個良好的日誌策略能夠:

  • 預防災難: 避免磁碟空間耗盡導致的系統崩潰
  • 快速除錯: 在問題發生時快速定位根因
  • 效能優化: 透過日誌分析發現效能瓶頸
  • 合規審計: 滿足法規要求的日誌保存與追蹤

記住:日誌不是越多越好,而是要恰到好處。在可觀測性與資源消耗之間找到平衡,才是生產環境日誌管理的精髓。

[!TIP]
定期檢視日誌配置,隨著業務成長調整策略。建議每季度審查一次日誌管理策略,確保其符合當前需求。