Apollo 技术文档Apollo 技术文档
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
指南
  • 架构概述
  • BigWorld 架构深度解析
  • BigWorld 进程架构与玩家生命周期
  • AOI九宫格系统详解
  • AOI广播与消息去重
  • Base 模块
  • Core 模块
  • Runtime 模块
  • Data 模块
  • Network 模块
  • /modules/actor.html
  • Game 模块
  • BigWorld 模块
服务器应用
API 参考
QA
GitHub
  • MMORPG 架构 QA

Q92: 如何设计日志系统?

问题分析

本题考察对日志系统设计的理解:

  • 日志分类
  • 日志格式
  • 日志存储
  • 日志检索
  • KBEngine 日志

一、日志系统架构

1.1 系统组成

┌─────────────────────────────────────────────────────────────┐
│                    日志系统架构                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  应用层 (Application):                                    │
│  ├── 业务日志 (操作、交易、战斗)                             │
│  ├── 错误日志 (异常、失败)                                   │
│  ├── 性能日志 (耗时、瓶颈)                                   │
│  └── 审计日志 (权限、安全)                                   │
│                          │                                  │
│  ▼                                                          │
│  收集层 (Collection):                                      │
│  ├── 日志库 (libfmt, spdlog)                                 │
│  ├── 格式化 (结构化)                                         │
│  ├── 上下文信息 (线程、实体、玩家)                            │
│  └── 元数据 (时间戳、级别、来源)                              │
│                          │                                  │
│  ▼                                                          │
│  缓冲层 (Buffer):                                            │
│  ├── 异步缓冲                                               │
│  ├── 批量写入                                               │
│  └── 内存映射                                               │
│                          │                                  │
│  ▼                                                          │
│  存储层 (Storage):                                          │
│  ├── 本地文件 (按大小/时间轮转)                              │
│  ├── 日志聚合 (Logstash, Fluentd)                             │
│  ├── 搜索引擎 (Elasticsearch)                                │
│  └── 长期存储 (S3, OSS)                                     │
│                          │                                  │
│  ▼                                                          │
│  分析层 (Analysis):                                         │
│  ├── 检索 (Kibana)                                           │
│  ├── 可视化 (Grafana)                                         │
│  └── 告警 (ElastAlert)                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 日志级别

┌─────────────────────────────────────────────────────────────┐
│                    日志级别                                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  TRACE (追踪):  详细的执行流程                                │
│  ├── 用途: 开发调试                                         │
│  ├── 示例: 函数进入/退出、变量值                             │
│  └── 生产环境: 关闭                                          │
│                                                             │
│  DEBUG (调试): 调试信息                                        │
│  ├── 用途: 开发调试                                         │
│  ├── 示例: 状态变化、中间结果                                 │
│  └── 生产环境: 关闭                                          │
│                                                             │
│  INFO (信息): 一般信息                                        │
│  ├── 用途: 正常运行记录                                       │
│  ├── 示例: 玩家登录、系统启动                                 │
│  └── 生产环境: 开启                                          │
│                                                             │
│  WARN (警告): 警告信息                                        │
│  ├── 用途: 潜在问题                                           │
│  ├── 示例: 连接慢、重试                                       │
│  └── 生产环境: 开启                                          │
│                                                             │
│  ERROR (错误): 错误信息                                       │
│  ├── 用途: 错误事件                                         │
│  ├── 示例: 请求失败、异常捕获                                 │
│  └── 生产环境: 开启                                          │
│                                                             │
│  FATAL (致命): 致命错误                                       │
│  ├── 用途: 严重错误导致进程退出                                │
│  ├── 示例: 数据库连接失败、内存溢出                           │
│  └── 生产环境: 开启                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、KBEngine 日志系统

2.1 KBEngine 日志宏

# KBEngine 日志系统

"""
KBEngine 日志宏:

- DEBUG_MSG(): 调试信息
- INFO_MSG(): 信息日志
- WARNING_MSG(): 警告日志
- ERROR_MSG(): 错误日志
- CRITICAL_MSG(): 严重错误日志
"""

import KBEngine

class Account(KBEngine.Account):
    def __init__(self):
        KBEngine.Account.__init__(self)
        self.login_count = 0

    def onLogin(self, entityType):
        """登录回调"""
        # 记录日志
        INFO_MSG(f"Account {self.id} login as {entityType}")
        self.login_count += 1

        # 警告: 多次登录
        if self.login_count > 3:
            WARNING_MSG(f"Account {self.id} login {self.login_count} times")

    def onLogout(self):
        """登出回调"""
        INFO_MSG(f"Account {self.id} logout after {self.login_count} logins")


class Entity(KBEngine.Entity):
    def onAttack(self, attackerId):
        """被攻击"""
        INFO_MSG(f"Entity {self.id} attacked by {attackerId}")

        # 计算伤害
        damage = self.calculateDamage(attackerId)

        # 记录伤害
        DEBUG_MSG(f"Damage calculated: {damage}, HP: {self.hp}")

        if damage > self.hp:
            ERROR_MSG(f"Entity {self.id} died from {attackerId}")

2.2 KBEngine 日志配置

# KBEngine 日志配置

"""
日志配置文件: kbengine_defs.xml

<root>
    <logging>
        <!-- 日志级别: DEBUG, INFO, WARNING, ERROR, CRITICAL -->
        <defaultLevel>INFO</defaultLevel>

        <!-- 日志路径 -->
        <logPath>logs/</logPath>

        <!-- 单个日志文件大小限制 (MB) -->
        <logSize>100</logSize>

        <!-- 日志文件数量 -->
        <logCount>10</logCount>

        <!-- 组件日志 -->
        <components>
            <!-- 全局日志 -->
            <default>
                <level>INFO</level>
            </default>

            <!-- DBMgr 单独日志 -->
            <DBMgr>
                <level>DEBUG</level>
            </DBMgr>

            <!-- BaseApp 单独日志 -->
            <BaseApp>
                <level>INFO</level>
            </BaseApp>

            <!-- CellApp 单独日志 -->
            <CellApp>
                <level>INFO</level>
            </CellApp>
        </components>
    </logging>
</root>
"""

三、结构化日志

3.1 JSON 格式日志

# 结构化 JSON 日志

import json
import time
from datetime import datetime

class StructuredLogger:
    """结构化日志记录器"""

    def __init__(self, component):
        self.component = component

    def log(self, level, message, **context):
        """记录结构化日志"""
        log_entry = {
            "timestamp": datetime.utcnow().isoformat(),
            "level": level,
            "component": self.component,
            "message": message,
            "context": context
        }

        # 输出到文件
        self.write_log(log_entry)

    def write_log(self, log_entry):
        """写入日志文件"""
        log_file = f"logs/{self.component}.log"

        with open(log_file, 'a') as f:
            f.write(json.dumps(log_entry) + '\n')


# 使用示例
class GameLogger(StructuredLogger):
    """游戏日志记录器"""

    def __init__(self):
        super().__init__("game")

    def log_trade(self, player_a, player_b, items):
        """记录交易日志"""
        self.log("INFO", "Trade completed",
                 player_a=player_a,
                 player_b=player_b,
                 items=items,
                 trade_id=self.generate_trade_id())

    def log_combat(self, entity_id, damage, attacker_id):
        """记录战斗日志"""
        self.log("INFO", "Entity took damage",
                 entity_id=entity_id,
                 damage=damage,
                 attacker_id=attacker_id,
                 remaining_hp=self.get_entity_hp(entity_id))

    def log_chat(self, player_id, channel, message):
        """记录聊天日志"""
        self.log("INFO", "Chat message",
                 player_id=player_id,
                 channel=channel,
                 message=message,
                 timestamp=int(time.time()))

四、日志收集

4.1 Filebeat 收集

# filebeat 配置

filebeat.inputs:
  # KBEngine 日志文件
  - type: log
    enabled: true
    paths:
      - /path/to/kbengine/logs/*.log
    fields:
      service: kbengine
      env: production
    fields_under_root: true
    multiline:
      pattern: '^['
      negate: true
      match: after

    # 解析 JSON 日志
    json.keys_under_root: true

output.elasticsearch:
  hosts: ["localhost:9200"]
  indices:
    - "kbengine-%{+yyyy.MM.dd}"

五、日志检索

5.1 Kibana 查询

# KBEngine 日志查询示例

"""
Elasticsearch/Kibana 查询示例:

1. 查询所有错误日志:
   level: "ERROR"

2. 查询特定玩家的操作:
   context.player_id: "12345"

3. 查询特定时间范围:
   timestamp: [2024-01-01 TO 2024-01-31]

4. 查询包含特定关键词:
   message: *login*

5. 聚合查询:
   level: "ERROR" AND message: *database*
"""

六、最佳实践

6.1 日志设计建议

实践说明
结构化JSON 格式便于解析
上下文包含关键信息
分级记录合理设置日志级别
定期轮转避免单个文件过大
异步写入不阻塞主线程

七、总结

日志系统核心

日志系统 = 分级分类 + 结构化格式 + 异步写入 + 集中存储
- 合理的日志级别
- JSON 结构化
- 文件轮转
- Elasticsearch 存储

参考资料

  • KBEngine Logging
  • ELK Stack
  • Python Logging
在 GitHub 上编辑此页
最后更新: 3/20/26, 6:06 AM
贡献者: cuihairu