运维开发实践——基于Sentry搭建错误日志监控系统

错误日志监控也可称为业务逻辑监控, 旨在对业务系统运行过程中产生的错误日志进行收集归纳和监控告警。似乎有那么点曾相识?没错... 就是提到的“APM应用性能监控”。但它又与APM不同,APM系统主要注重应用层的行为分析,收集的更多是运营方向的数据。而sentry所做的是收集应用底层代码的崩溃信息,便于码侬们排查代码异常。简单来说它就是一个面向技术码侬的排障工具。

1. 场景描述

随着运维自动化流程的推进, 各类运维工具和系统也像雨后春笋般涌现. 目前我们自主开发的运维系统的数量已经接近两位数. 这些系统部署在多台机器上, 通常还配套一批后台运行的脚本. web端如果出现异常, 开发人员可以及时得到反馈进行修复. 而脚本因为没有交互, 可能会出现发生重大故障时才定位到问题的情况.

2. 既有方案

  1. 后端和脚本用python内置的日志模块记录程序中间状态, 同时也将两者的输出重定向到指定文件, 以获取未捕获的异常信息.

  2. 同台服务器上多个系统的日志集中存放到同个目录

  3. 使用rsync定时从多台服务器中拉取日志文件

  4. 对日志文件进行关键字匹配, 并将过滤结果通过邮件发送给运维开发人员

最终整合的通知邮件如图

运维开发实践——基于Sentry搭建错误日志监控系统

3. 存在的问题

上面的操作部分解决了脚本运行状态监控盲区的问题, 但还存在如下问题

  1. 无法第一时间感知错误

    脚本日志的拉取不是实时的, web端用户的反馈也往往存在滞后. 出现问题到解决问题的周期太长, 容易导致工作陷入被动.

  2. 错误信息的获取相对低效

    用户反馈和邮件告警包含的错误信息非常有限, 最终都不得不在大量的日志中上下翻看关联的信息. 可能还需要在测试环境下给代码埋点多获取一些中间变量数据, 给定位问题带来很多麻烦.

  3. 日志的处理方式不够灵活

    通常来说, 除了程序运行出错, 我们还关心其他异常情况, 比如数据污染, 非法请求, 第三方API调用异常等. 如果将此类等同错误记录下来, 很容易造成告警滥发, 而如果不处理此类异常, 久而久之可能导致严重的问题. 我们希望同样的日志内容可以根据场景不同灵活处理

  4. 监控覆盖面有限

    完整的监控应该涵盖脚本, 后端以及前端三个部分. 特别是我们新的运维系统实现了前后端分离之后, 很多前端的问题无法被统一记录下来.

鉴于此, 我们了解了一些日志收集和监控方案, 选择了sentry.

4. sentry初探

4.1 概览

sentry是一个现代化的错误日志记录和聚合平台。支持几乎所有主流开发语言和平台, 并提供了现代化UI, 如图

运维开发实践——基于Sentry搭建错误日志监控系统

与ELK, splunk不同, sentry专注于应用程序产生的错误日志的聚合和监控. 官方提供了多个语言的SDK.

运维开发实践——基于Sentry搭建错误日志监控系统

多达30种集成方式

运维开发实践——基于Sentry搭建错误日志监控系统

让开发者第一时间获悉错误信息, 并方便的整合进自己和团队的工作流中.

4.2 使用前后对比

为了直观的展示sentry的强大, 这里模拟一个常见的场景, 如有雷同, 纯属巧合.

4.2.1 场景一

接入sentry前

  • 用户A: 发布功能用不了

  • 开发者A: 哪个页面? 截个图

  • 用户A: (发截图)

  • 开发者A发现bug可以重现, 登录服务器查看错误日志, 确认程序逻辑无问题, 查看数据库数据, 发现有脏数据. 联系开发者B检查负责更新数据的python脚本C.py.

  • 开发者B登录服务器查看错误日志, 发现一个逻辑错误导致脚本罢工, 已持续了一个小时. 影响了数千条数据

接入后

  • 开发者A,B同时收到邮件告警, 一分钟前脚本C.py异常退出.

  • 开发者B进入sentry后台查看错误信息, 定位问题并将其修复, 再清理受影响的数十条数据.

  • 在此过程中没有用户受到影响, 无需开发者A介入

4.2.2 场景二

接入sentry前

  • 用户: 提交按钮点了没反应

  • 开发者: 哪个页面? 截个图

  • 用户: (发截图)

  • 开发者: 我这边没这个问题, 你打开开发者工具, 切到console面板, 截个图我看下

  • 用户: 怎么弄??

    ….省略100字

  • 开发者拿到相关数据, 确定是代码问题. 但是js文件经过了压缩, 无法定位到有问题的代码. 只能打开本地开发服务器调试.

接入后

  • 开发者收到邮件告警, 显示前端有错误日志

  • 开发者进入sentry后台查看错误信息, 比如用户浏览器版本, 产生错误的页面url, 代码调用过程和最终引发错误的代码, 确认问题所在.

  • 开发者: 两分钟前你提交的工单备注字段的校验有点问题, 你先把那一栏留空再提交, 稍后我会更新一个hotfix版本, 到时跟你说下.

  • 用户: 好的, 刚想问.

5. sentry的配置

sentry官方提供了详细的部署文档, 网上也可以搜索到中文的安装教程, 安装过程不赘述. 想要尝鲜的小伙伴也可以直接使用sentry官方提供的saas, 免费版支持每天5000个event. 地址是 https://sentry.io

5.1 概念

使用sentry, 需要弄清楚几个概念:

  • event

    直译是"事件", 是可操作数据的基本单位. 每一次日志输出就产生一个event. event并不一定就是错误, 如果日志记录级别设置很低, 那么后台会产生很多的event, 所以正确的设置日志级别很重要

  • issue

    直译是"工单"或者"问题", 是同一类event的聚合. 某一个错误可能因为重复执行而被记录多出, 在sentry会自动聚合到一起, 方便处理. 通常我们操作的对象就是issue

    运维开发实践——基于Sentry搭建错误日志监控系统

  • DSN

    DSN即客户端密钥, 用来进行客户端和服务器的通信. DSN是一个url, 包含一个公钥一个私钥, 项目标记和服务器地址, 比如https://1703147af2094458bevb1bfadcfa1c2:7e00a1d4cbd745c0b780451c3586d7f4@sentry.io/1545. 这类DSN是私密的, 还有一类是非私密的, 在sentry后台中显示为DSN(public), 给前端项目使用.

  • Raven

    整个错误日志监控系统包括客户端和服务端, Sentry是服务端的名称, 客户端名称是Raver, 需要两者配合才能工作.

5.2 配置

sentry服务端的配置主要是名称, 告警规则等, 至于被监控项目是前端还是后端区别不大.

5.2.1 创建项目

  1. 进入sentry系统后台, 点击右上角新建项目

    运维开发实践——基于Sentry搭建错误日志监控系统

  2. 命名为[项目名][前|后端], 比如"蓝海前端".

  3. 在配置应用框架页面, 点击可以查看各个语言或框架的接入文档(可以忽略这一步)

  4. 点击左上角项目名称, 进入项目首页, 可以看到页面显示"Waiting for events…"

5.2.2 获取和测试DSN

  1. 在"项目设置"页, 在左侧列表中点击"客户端密钥", 进入页面

  2. 拷贝DSN, 后端的是DSN, 前端是DSN(public)

  3. 以python为例, 执行pip install raven安装客户端后, 执行raven test DSN, 如果一切顺利, 可以在sentry后台项目首页看到新增了一条测试消息

    运维开发实践——基于Sentry搭建错误日志监控系统

5.2.3 配置警报

  1. 在"项目设置"页面, 在左侧列表中点击"警报", 进入警报配置页

  2. 点击规则标签页, 可以看到已有一个规则, 当事件首次发生时告警

  3. 根据需要修改规则

告警规则的配置相当灵活, 且可以对多个条件进行与或判断

运维开发实践——基于Sentry搭建错误日志监控系统

5.2.4 集成告警

  1. 在"项目设置"页面, 在左侧列表中点击"所有集成"

  2. 勾选需要接入的类型,比如Mail

    运维开发实践——基于Sentry搭建错误日志监控系统

邮件服务器的配置请参考官方文档, 自己搭建的sentry服务器如果发现集成类型很少, 可以安装官方或第三方插件进行扩展

在服务端配置结束后, 可以开始配置客户端.

6. 后端的接入

因为我们的系统主要用python开发, 在此以python为例.

python接入sentry十分简单. 官方提供了十几种python环境(框架)下使用sentry的例子, 比如在celery中

from raven import Clientfrom raven.contrib.celery import register_signal, register_logger_signalclient = Client(DSN)# register a custom filter to filter out duplicate logsregister_logger_signal(client)# The register_logger_signal function can also take an optional argument# `loglevel` which is the level used for the handler created.# Defaults to `logging.ERROR`register_logger_signal(client, loglevel=logging.INFO)# hook into the Celery error handlerregister_signal(client)# The register_signal function can also take an optional argument# `ignore_expected` which causes exception classes specified in Task.throws# to be ignoredregister_signal(client, ignore_expected=True)

个人推荐借鉴logging使用的例子, 原因是通常开发者会根据logging模块定制自己的日志配置, 不直接使用框架内的日志模块. 如果你在应用程序中只用了logging模块, 那么接入sentry对已存在的代码来说是透明的, 无需多加修改.

用logging模块接入sentry只需两步:

6.1 安装客户端

pip install raven

6.2 初始化配置

在应用程序的入口文件(tornado中的app.py等)中, 或者自定义的日志模块中, 插入如下代码

from raven.handlers.logging import SentryHandlerfrom raven.conf import setup_logginghandler = SentryHandler(DSN)handler.setLevel(logging.ERROR)setup_logging(handler)

完成了这两步操作之后, 就可以像之前那样使用logging模块

import logginglogger = logging.getLogger(__name__)logger.info('This is a test message')

当上面的代码被执行时, 除了原有的打log操作之外, raven还会向sentry服务器发送日志内容, 并向标准输出添加

Sending message of length xxx to https://xxxx

如果希望向sentry发送更多上下文信息, 可以带上extra参数

logger.error('This is a test message', extra={'stack': True})

最终显示在后台的日志信息如图

运维开发实践——基于Sentry搭建错误日志监控系统

包含了日志级别, python环境信息, SDK信息, 栈调用, 前后n次日志输出, 相关的其他事件等等, 如果是未捕获的异常或带上extra参数, 还会显示中间变量的值, 很方便的定位到出错的位置和数据, 无需再去代码埋点.

7. 前端的接入

前端的接入相对来说复杂一些

第一, 需要对sentry服务器的域名进行解析. 内部的系统, 后端监控可以给线上机器添加hosts指定sentry服务器的IP. 而前端, 因为错误日志是从用户浏览器发出的, 需要用户能自动解析sentry服务器的域名

第二, 如果前端项目用到了打包工具, 而通常打包工具会对代码进行压缩甚至混淆, 就会出现sentry收集到的日志无法准确定位问题代码的情况 所幸, sentry支持导入sourcemap自动解析和还原代码, 让开发者在后台能看到development环境一样详细的栈调用. (当然如果没有用打包工具可以忽略这一步)

前端的接入这里以reactjs为例

7.1 安装依赖

npm i raven-js --save

7.2嵌入raven

在index.js文件(入口文件)中,

import Raven from 'raven-js';# 在适当的地方加入, 尽可能让它早执行Raven.config(DSN(public)).install();

其他前端框架的接入请参考官方文档 https://docs.sentry.io/clients/java

7.3导入sourcemap

提前生成好sourcemap文件, 实测source-map级别可以完美工作, cheap-source-map能定位到, 但显示不友好. 当然, 最推荐的是cheap-module-source-map

# 安装npm i -g sentry-cli-binary# 登录sentrysentry-cli --url SENTRY_URL login# SENTRY_URL指自建服务或官方saas地址, 执行命令后会访问API TOKEN创建页面, 生成一个TOKEN, 拷贝进来, 成功后TOKEN会被保留到系统用户某个配置目录下, 后续的请求都会重复使用这个TOKEN# 创建一个releasesentry-cli releases -o sentry -p 7d04f2c51f32 new test01 --finalize# 这里的sentry 和7d04f2c51f32 是指 组织名称和项目名称, 均指*简称*, 与sentry页面上默认显示的不同, 需要到配置页面查看# 上传dist目录下的文件sentry-cli releases -o sentry -p 7d04f2c51f32 files test01 upload-sourcemaps dist# 删除旧的release下的所有文件sentry-cli releases -o sentry -p 7d04f2c51f32 files test01 delete --all# 当然这个命令是不想要release上的文件的时候执行的

注意, 生成的map文件与上传的相对路径需要一致. 比如, dist目录是打包后的文件存放目录, map文件为sourcemap/[file].map, 则sentry-cli上传目录应该是dist, 这样map文件才会显示在sentry后台的~/sourcemap/目录下.

这样的webpack配置

devtool = 'source-map';output.path: 'dist';output.sourceMapFilename = 'sourcemap/[file].map';

则对应这样的命令

sentry-cli releases -o sentry -p 7d04f2c51f32 files test02 upload-sourcemaps dist

另外, sentry-cli提供了一个参数--url-prefix, 可以为上传的map文件添加前缀, 默认是~/, 有兴趣的同学可以试试看

再补充一点, sentry需要根据js文件的sourceMappingURL来解析map文件路径, 所以sourcemap级别不能用hide-source-map或者类似的.

代码上传完毕后, 在版本->工件页面可以看到该release上的文件, 如图

运维开发实践——基于Sentry搭建错误日志监控系统

最终错误日志效果如图

运维开发实践——基于Sentry搭建错误日志监控系统

8. sentry管理后台的使用

篇幅所限, sentry后台的使用简要讲几点

第一,自定义过滤

sentry提供了丰富的过滤选项, 默认过滤条件是"Unresolved Issues", 用户也可以自己组合过滤条件, 并保存成个人或团队的默认选项

运维开发实践——基于Sentry搭建错误日志监控系统

第二,页面实时更新

上图中间的按钮可以开启或关闭issues页面的实时刷新,

第三,统计和概览

运维开发实践——基于Sentry搭建错误日志监控系统

上图是系统管理员页面, 可以看到系统调用, 等待中的任务队列等的情况, 在个人帐号首页也能看到项目的统计信息.

9. 需要注意的点

  1. 用sentry做错误日志监控不能取代原有的日志存储方案, 只是在日志收集和监控方面做了扩展. 使用sentry应着重利用其实时性和快捷性, 做到快速响应. sentry会清除较旧的日志内容, 这与ELK之类的日志处理系统也有差别.

  2. sentry能否用得好还取决于打log的开发者的功力. 如果原始日志记录缺少关键信息或无效信息过多, 再强大的日志分析系统也无能为力. 因此在引入sentry做日志监控的同时, 也要同步加强开发团队打log的意识, 规范日志级别, 格式和内容

上一篇:sql查找某一列中某一数值出现次数大于3的记录的前3条


下一篇:node 日志 log4js 错误日志记录