利用日志和跟踪理解系统行为
1. 日志数据收集与格式约定
为了有效理解系统行为,收集日志中的 IP 数据并生成用户常见地理区域的可视化图表是一种可行的方式。而要有效地存储和搜索日志数据,首先需要工程团队就日志格式达成一致。一致的格式有助于确保数据能够被高效地存储和处理。
1.1 多源数据收集
要实现系统的可观测性,需要从多个来源收集数据,不仅包括运行中的服务,还包括基础设施。定义通用格式可以让我们更轻松地分析数据,并使用现有工具进行数据搜索。可能收集和使用的数据类型如下:
- 应用程序日志
- 数据库日志
- 网络日志
- 底层操作系统收集的性能数据
对于部分组件,可能无法控制其日志格式,这时就需要处理其特殊性并进行转换。不过,我们可以先聚焦于能够控制的服务部分。确保整个工程团队遵循统一的格式和操作方式,从长远来看是有回报的,因为这会让数据收集变得更简单、更有效。接下来,我们先确定日志条目中应存储的内容,再探讨存储方式。
1.2 日志条目有用信息
为了让日志数据有助于理解系统行为,日志条目应包含以下关键信息:
1.2.1 时间戳
为了关联和正确排序数据,需要为日志条目添加时间戳。时间戳应尽可能详细和精确,例如使用四位数年份并采用最佳分辨率。每个服务最好以微秒为单位生成自己的时间戳,并且应包含时区信息,尽可能以 GMT/UTC 格式收集数据。这样可以避免不同服务因时区不同而导致的数据关联问题,在分析时能更轻松地按事件发生时间对数据进行排序。
1.2.2 标识符
在日志数据中应尽可能多地使用唯一标识符,如请求 ID、用户 ID 等。这些标识符在跨多个数据源引用数据时非常有价值,能够有效地对不同来源的数据进行分组。通常,系统中已经存在这些 ID 用于标识资源,并且它们可能已经在不同服务中传播,因此应充分利用它们。结合时间戳,唯一标识符能成为理解系统中事件流的强大工具。
1.2.3 来源
确定日志条目的来源有助于在需要时进行调试。常见的来源数据包括:
- 主机
- 类或模块
- 函数
- 文件名
在记录函数调用的执行时间时,收集的来源信息可以帮助推断性能,即使不是实时的,也能有效识别瓶颈和潜在的性能问题。
1.2.4 级别或类别
每个日志条目应包含一个类别,可以是日志的数据类型或日志级别。常见的日志级别有:ERROR、DEBUG、INFO、WARN。类别可以对数据进行分组,一些工具可以解析日志文件,搜索 ERROR 级别的消息并将其发送到错误报告系统,从而实现错误报告的自动化。
1.3 日志结构与可读性
日志条目应采用人类可读的格式,同时要便于机器解析。避免使用二进制编码或普通人类难以理解的编码方式,例如不要存储图像的二进制表示,而应使用其 ID、文件大小等相关数据。此外,应避免使用多行日志,因为在日志聚合工具中解析时可能会导致碎片化,容易丢失与特定日志条目相关的信息,如 ID、时间戳或来源。
在示例中,我们使用 JSON 对日志条目进行编码,这样既能提供人类可读和机器可解析的数据,又能自动包含前面提到的部分数据。同时,还可以使用 Logstash 格式化库,它有多种语言版本,能提供一致的数据格式。以下是一个使用 Logstash 库为 Python 收集的日志条目示例:
{
"source_host" : "e7003378928a",
"pathname" : "usrlocallibpython3.6site-packagesnamekorunners.py",
"relativeCreated" : 386.46125793457031,
"levelno" : 20,
"msecs" : 118.99447441101074,
"process" : 1,
"args" : [ "orders_service" ],
"name" : "nameko.runners",
"filename" : "runners.py",
"funcName" : "start",
"module" : "runners",
"lineno" : 64,
"@timestamp" : "2018-02-02T18:42:09.119Z",
"@version" : 1,
"message" : "starting services: orders_service",
"levelname" : "INFO",
"stack_info" : null,
"thread" : 140612517945416,
"processName" : "MainProcess",
"threadName" : "MainThread",
"msg" : "starting services: %s",
"created" : 1520275329.1189945
}
下面是一个 mermaid 流程图,展示了日志数据从生成到存储的基本流程:
graph LR
A[服务生成日志] --> B[遵循统一格式]
B --> C[使用 Logstash 格式化]
C --> D[存储日志数据]
2. 构建日志基础设施
2.1 ELK 和 Fluentd 解决方案
为了收集和聚合所有运行服务的日志,并提供搜索和关联功能,我们将构建一个基于 Elasticsearch、Logstash 和 Kibana(ELK)的日志基础设施,并使用 Fluentd 作为数据收集器。以下是这些技术的简要介绍:
-
Elasticsearch
:一个集中存储数据的搜索和分析引擎,对日志数据进行索引,支持高效的搜索和聚合操作。
-
Logstash
:服务器端处理管道,可从多个源摄取数据并进行转换,然后发送到 Elasticsearch。在本方案中,我们利用其格式化和数据收集功能,但使用 Fluentd 来发送数据。
-
Kibana
:用于可视化 Elasticsearch 数据的 UI 工具,可查询数据、探索关联并生成可视化图表。
-
Fluentd
:开源数据收集器,用于将服务中的日志推送到 Elasticsearch。它可以作为 Dockerfiles 的日志提供程序,在 Docker 组合文件中声明即可使用。
2.2 设置日志解决方案
我们通过 Docker 组合文件来设置日志解决方案,以下是添加到组合文件中的新组件:
version: '2.1'
services:
gateway:
container_name: simplebank-gateway
restart: always
build: ./gateway
ports:
- 5001:5000
volumes:
- ./gateway:/usr/src/app
links:
- "rabbitmq:simplebank-rabbitmq"
- "fluentd"
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: simplebank.gateway
kibana:
image: kibana
links:
- "elasticsearch"
ports:
- "5601:5601"
elasticsearch:
image: elasticsearch
expose:
- 9200
ports:
- "9200:9200"
fluentd:
build: ./fluentd
volumes:
- ./fluentd/conf:/fluentd/etc
links:
- "elasticsearch"
ports:
- "24224:24224"
- "24224:24224/udp"
2.3 Fluentd 配置
构建 Fluentd 时使用的 Dockerfile 如下:
FROM fluent/fluentd:v0.12-debian
RUN ["gem", "install", "fluent-plugin-elasticsearch",
"--no-rdoc", "--no-ri", "--version", "1.9.2"]
Fluentd 的配置文件如下:
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<match *.**>
@type copy
<store>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix fluentd
logstash_dateformat %Y%m%d
include_tag_key true
type_name access_log
tag_key @log_name
flush_interval 1s
</store>
<store>
@type stdout
</store>
</match>
2.4 配置日志收集
在服务中,可以通过环境变量控制日志级别,以便在开发和生产环境中使用不同的级别。以网关服务为例,其日志配置文件如下:
AMQP_URI: amqp://${RABBIT_USER:guest}:${RABBIT_PASSWORD:guest}@${RABBIT_HOST:
localhost}:${RABBIT_PORT:5672}/
WEB_SERVER_ADDRESS: '0.0.0.0:5000'
RPC_EXCHANGE: 'simplebank-rpc'
LOGGING:
version: 1
handlers:
console:
class: logging.StreamHandler
root:
level: ${LOG_LEVEL:INFO}
handlers: [console]
网关服务代码中启用日志的部分如下:
import datetime
import json
import logging
import uuid
from logstash_formatter import LogstashFormatterV1
from nameko.rpc import RpcProxy, rpc
from nameko.web.handlers import http
from statsd import StatsClient
from werkzeug.wrappers import Request, Response
class Gateway:
name = "gateway"
orders = RpcProxy("orders_service")
statsd = StatsClient('statsd', 8125,
prefix='simplebank-demo.gateway')
logger = logging.getLogger()
handler = logging.StreamHandler()
formatter = LogstashFormatterV1()
handler.setFormatter(formatter)
logger.addHandler(handler)
@http('POST', '/shares/sell')
@statsd.timer('sell_shares')
def sell_shares(self, request):
req_id = uuid.uuid4()
res = u"{}".format(req_id)
self.logger.debug(
"this is a debug message from gateway",
extra={"uuid": res})
self.logger.info("placing sell order", extra=
{"uuid": res})
self.__sell_shares(res)
return Response(json.dumps(
{"ok": "sell order {} placed".format(req_id)}),
mimetype='application/json')
@rpc
def __sell_shares(self, uuid):
self.logger.info("contacting orders service", extra={
"uuid": uuid})
res = u"{}".format(uuid)
return self.orders.sell_shares(res)
@http('GET', '/health')
@statsd.timer('health')
def health(self, _request):
return json.dumps({'ok': datetime.datetime.utcnow().__str__()})
2.5 启动服务与配置 Kibana
完成上述设置后,在根目录的控制台中执行以下命令启动所有服务、指标和日志基础设施:
docker-compose up --build --remove-orphans
启动后,还需要配置 Kibana 以使用 Fluentd 收集并存储在 Elasticsearch 中的日志。访问 Kibana 网络仪表板(http://localhost:5601),首次访问会重定向到管理页面,需要配置索引模式。在 Fluentd 配置中,我们设置了 logstash 前缀为 fluentd,因此在索引文本框中输入 fluentd-*,然后点击创建按钮,即可开始探索多个服务生成的日志数据。
通过以上步骤,我们构建了一个强大的日志基础设施,能够帮助我们更好地理解系统行为,进行问题排查和性能优化。
下面是一个 mermaid 流程图,展示了整个日志基础设施的工作流程:
graph LR
A[服务] --> B[STDOUT 输出日志]
B --> C[Fluentd 收集]
C --> D[Elasticsearch 存储与索引]
D --> E[Kibana 可视化与查询]
整个过程可以总结为以下步骤:
1. 服务生成日志并输出到 STDOUT。
2. Fluentd 收集日志并推送到 Elasticsearch。
3. Elasticsearch 对日志数据进行存储和索引。
4. Kibana 从 Elasticsearch 获取数据并进行可视化和查询。
通过这种方式,我们可以集中管理和分析所有服务的日志,提高系统的可观测性。
3. 日志数据的实际应用与优势
3.1 故障排查与问题定位
日志数据在故障排查中起着至关重要的作用。当系统出现问题时,我们可以通过查看日志中的时间戳、标识符、来源和日志级别等信息,快速定位问题所在。例如,如果某个服务出现错误,我们可以根据日志中的 ERROR 级别信息,结合唯一标识符,追踪该请求在各个服务中的处理流程,找出引发错误的具体函数或模块。
以下是一个简单的表格,展示了不同日志信息在故障排查中的作用:
| 日志信息 | 作用 |
| ---- | ---- |
| 时间戳 | 确定事件发生的顺序和时间,便于分析问题的先后关系 |
| 标识符 | 关联不同服务中的同一请求,追踪请求的完整流程 |
| 来源 | 定位问题发生的具体服务、模块或函数 |
| 日志级别 | 筛选重要信息,如 ERROR 级别的日志通常表示系统出现了严重问题 |
3.2 性能分析与优化
日志数据还可以用于性能分析和优化。通过记录函数的执行时间和资源使用情况,我们可以找出系统中的瓶颈和潜在的性能问题。例如,在日志中记录每个函数的执行时间,然后根据来源信息进行分组统计,就可以得到各个模块的性能指标。
以下是一个 mermaid 流程图,展示了使用日志数据进行性能分析的基本流程:
graph LR
A[收集日志数据] --> B[提取执行时间和来源信息]
B --> C[按来源分组统计]
C --> D[找出性能瓶颈]
D --> E[进行优化]
3.3 安全审计与合规性
日志数据对于安全审计和合规性检查也非常重要。通过记录用户的操作行为和系统的关键事件,我们可以监控系统的安全性,及时发现异常行为。例如,记录用户的登录时间、操作内容和 IP 地址等信息,可以帮助我们识别潜在的安全威胁。
4. 日志管理的最佳实践
4.1 日志级别管理
合理设置日志级别可以有效地控制日志数据的量,避免产生过多的无用信息。在开发环境中,可以将日志级别设置为 DEBUG,以便详细记录系统的运行状态;而在生产环境中,建议将日志级别设置为 INFO 或 WARN,只记录重要的信息。
4.2 日志保留策略
制定合理的日志保留策略可以确保日志数据的有效性和安全性。根据业务需求和法规要求,确定日志数据的保留时间,并定期清理过期的日志。例如,对于一些关键业务的日志,可以保留较长时间;而对于一些临时的调试日志,可以在问题解决后及时删除。
4.3 日志监控与告警
建立日志监控和告警机制可以及时发现系统中的问题。通过设置规则,监控日志中的特定信息,当出现异常情况时及时发出告警。例如,当某个服务的 ERROR 级别日志数量超过一定阈值时,发送邮件或短信通知相关人员。
5. 总结与展望
5.1 总结
通过构建基于 ELK 和 Fluentd 的日志基础设施,我们可以有效地收集、存储和分析系统的日志数据。日志数据不仅可以帮助我们理解系统行为,进行故障排查和性能优化,还可以用于安全审计和合规性检查。同时,遵循日志管理的最佳实践,可以提高日志数据的质量和可用性。
5.2 展望
随着系统的不断发展和复杂化,日志数据的规模和复杂度也会不断增加。未来,我们可以进一步探索日志数据的分析方法和技术,如使用机器学习算法进行异常检测和预测分析。此外,还可以结合其他数据源,如指标数据和跟踪数据,实现更全面的系统可观测性。
在实际应用中,我们可以根据具体的业务需求和系统特点,灵活调整日志基础设施的配置和日志管理策略,以满足不断变化的需求。通过持续优化和改进,我们可以更好地利用日志数据,提升系统的稳定性、性能和安全性。
以下是一个 mermaid 流程图,展示了未来日志管理的发展方向:
graph LR
A[现有日志基础设施] --> B[引入机器学习算法]
A --> C[结合其他数据源]
B --> D[异常检测与预测分析]
C --> E[实现全面可观测性]
D --> F[持续优化系统]
E --> F
通过不断探索和创新,我们可以将日志管理提升到一个新的水平,为系统的稳定运行和业务的发展提供有力支持。整个日志管理的过程可以总结为以下步骤:
1. 构建日志基础设施,确保日志数据的收集、存储和分析。
2. 应用日志数据进行故障排查、性能分析和安全审计。
3. 遵循日志管理的最佳实践,如日志级别管理、保留策略和监控告警。
4. 探索日志数据的分析方法和技术,结合其他数据源,实现更全面的系统可观测性。
5. 持续优化和改进日志管理策略,提升系统的稳定性、性能和安全性。
通过以上步骤,我们可以建立一个高效、可靠的日志管理体系,为系统的运行和发展提供有力保障。
超级会员免费看

被折叠的 条评论
为什么被折叠?



