ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Zulip 日志与错误报告体系:从后端异常邮件到前端 Blueslip 的完整实践

Zulip 日志与错误报告体系:从后端异常邮件到前端 Blueslip 的完整实践 Zulip 日志与错误报告体系从后端异常邮件到前端 Blueslip 的完整实践【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip本指南基于 Zulip 的 logging 子系统文档系统讲解这个大型开源团队聊天项目如何构建零已知 500 错误、零已知前端 JS 异常的错误报告框架。你将掌握 Zulip 后端基于 Django 的邮件/Sentry 异常上报机制、/var/log/zulip/下各日志文件的语义与格式、log-search日志检索工具以及前端自研的blueslip错误收集库与性能上报链路的完整实战方案。Zulip 将可靠的错误报告视为项目成功的关键没有它就只能依赖用户反馈 bug 来完善产品。因此项目提供了两套互补方案——默认开箱即用的邮件报告系统适合中小型部署以及可选的Sentry 集成适合 zulip.com 这类大型部署用于聚合异常、追踪影响用户与 realm。后端错误报告后端错误报告直接构建在 Django 框架提供的错误处理基础设施之上对应源码位于 zerver/lib/logging_util.py 与 zerver/filters.py。Django 提供的四块核心能力异常邮件通知通过django.utils.log.AdminEmailHandler任何 500 错误都会自动发送邮件给服务器管理员。Zulip 认为任何 Zulip 服务器的 500 错误通常都值得管理员调查并向上游报告因此该邮件系统默认开启由 zproject/computed_settings.py 中的ERROR_REPORTING决定是否把mail_admins加入默认 handler 列表。错误限速_RateLimitFilter为避免宕机时瞬间发出成百上千封邮件zerver/lib/logging_util.py 中的_RateLimitFilter会对同类错误去重限流。它的关键实现细节包括默认限速窗口由settings.{类名大写}_LIMIT控制默认600 秒同一 traceback按traceback.format_exception输出做 SHA1 哈希在一个窗口内只上报一次正常情况使用Django 共享缓存memcached做全局去重所有 Django 进程共享并通过线程局部变量handling_exception检测递归异常当共享缓存本身不可用时例如数据库/缓存整体宕机自动降级为进程内内存缓存去重last_error时间戳保证关键路径不会因日志系统自身故障而崩溃。ZulipLimiter用于控制台/文件日志和EmailLimiter用于邮件都是它的子类分别独立限流。敏感信息过滤zerver/filters.py 中的ZulipExceptionReporterFilter继承 Django 的SafeExceptionReporterFilter在异常报告中清洗两类敏感数据设置项通过正则API|TOKEN|KEY|SECRET|PASS|SIGNATURE|HTTP_COOKIE|_SALT忽略大小写遮蔽环境密钥且在生产环境下get_safe_settings()直接返回空字典完全不在异常报告中包含任何 Django 设置POST 参数把content、password、key、api-key、api_key、subject、stream、subscriptions、to、csrfmiddlewaretoken、realm_counts、installation_counts等字段统一替换为**********防止消息内容与凭据泄漏到错误报告中。JsonableError中间件zerver/middleware.py 与 zerver/middleware.py 等位置处理JsonableError定义于 zerver/lib/exceptions.py——这是 Zulip 的标准机制让 API 代码能够返回 JSON 格式的 HTTP 错误响应例如CsrfFailureError、ProxyMisconfigurationError等异常子类都继承自它。后端 Sentry 错误日志可选Zulip 的可选后端 Sentry 集成会聚合错误展示受影响的是哪些用户和 realm、异常发生前的所有日志、异常每个栈帧中的局部变量、以及触发请求的完整请求头。启用步骤源自 docs/subsystems/logging.md在 Sentry 组织中创建一个 platform 为Django的项目将 Sentry DSN 写入/etc/zulip/settings.py自托管部署的配置模板见 zproject/prod_settings_template.py## Controls the DSN used to report errors to Sentry.io SENTRY_DSN https://bbbbbb.ingest.sentry.io/1234以zulip用户身份重启 Zulip/home/zulip/deployments/current/scripts/restart-server如需在每次部署后标记 Sentry 发布版本可参考 Sentry 部署钩子。源码级补充真正的初始化逻辑在 zproject/sentry.py 的setup_sentry()中按进程类型自动选择集成Tornado 进程启用TornadoIntegration()并禁用DjangoIntegration()其余进程启用DjangoIntegration()同时始终加载RedisIntegration()before_sendadd_context为事件附加realm标签与用户role并主动删除username与email这类 PII只保留用户 ID、realm、角色与 IPsend_default_piiTrue与清洗逻辑配合实现隐私与可追踪性的平衡release默认取ZULIP_VERSION若部署目录存在sentry-release文件则读其内容覆盖traces_sampler支持对队列 worker 单独设采样率SENTRY_TRACE_WORKER_RATE普通请求走SENTRY_TRACE_RATEshutdown_timeout10保证停机时异常也能发送默认 2 秒忽略django.security.SuspiciousOperation、DisallowedHost等用户输入类非 bug安全日志避免噪音。DSN 的默认值来自 zproject/default_settings.pySENTRY_DSN、SENTRY_FRONTEND_DSN均可通过 zulip.conf 的[sentry] project_dsn/frontend_project_dsn配置项注入SENTRY_FRONTEND_SAMPLE_RATE默认为1.0。后端日志Backend loggingDjango 日志系统基于标准 Python logging 基础设施Zulip 的完整 LOGGING 字典配置位于 zproject/computed_settings.py。整体流向如下logging.exception与logging.error→ 邮件发送给服务器维护者logging.warning→ 写入/var/log/zulip/errors.logerrors_filehandler 级别为 WARNING更低级别 → 主服务器日志以及各进程专属日志如django.log对应主 Django 进程events_*对应队列 worker。zproject/computed_settings.py 中集中定义了全部日志路径server.log、errors.log、manage.log、workers.log、slow_queries.log、send_email.log、ldap.log、digest.log、analytics.log、webhooks_errors.log、auth.log、scim.log、registration.log等均落在/var/log/zulip/下开发环境则自动重定向到仓库内var/log/目录。所有文件 handler 均使用logging.handlers.WatchedFileHandler支持日志轮转。开发者还可注释掉django.dblogger 的 DEBUG 配置以在控制台打印全部数据库查询。后端日志格式主服务器日志生产环境/var/log/zulip/server.log开发环境就是你运行run-dev的终端为每个后端请求记录一行并包含 warnings、errors 与 Python 异常的完整 traceback。开发时应时常留意run-dev控制台以发现刚引入的 bug生产环境排查错误首选errors.log主日志太冗长而排查性能问题则主日志极具价值。典型输出来自原文档2016-05-20 14:50:22.056 INFO [zr] 127.0.0.1 GET 302 528ms (db: 1ms/1q) (start: 123ms) / (unauthzulip via ?) [20/May/2016 14:50:22]GET / HTTP/1.0 302 0 2016-05-20 14:50:22.272 INFO [zr] 127.0.0.1 GET 200 124ms (db: 3ms/2q) /login/ (unauthzulip via ?) 2016-05-20 14:50:26.538 INFO [zr] 127.0.0.1 POST 200 12ms (db: 1ms/2q) (start: 53ms) /api/v1/events/internal [1463769771:0/0] (8zulip via internal) 2016-05-20 14:50:26.959 INFO [zr] 127.0.0.1 GET 200 588ms (db: 26ms/21q) / [1463769771:0] (8zulip via website)每行字段依次为时间戳 → 日志级别 → 日志器名Zulip 请求日志统一缩写为zr见 zerver/lib/logging_util.py 的logger_nicknames映射→IP 地址 → HTTP 方法 → HTTP 状态码 → 处理耗时→可选的性能细节如数据库时间/查询数、memcached 时间/查询数、Django 进程启动时间、Markdown 处理时间等在圆括号中→端点/URL对应 zproject/urls.py 中的路由→email via client展示涉及的用户账户若已登录与客户端类型web、Android 等。日志格式由 zerver/lib/logging_util.py 的ZulipFormatter实现通过LOGGING_SHOW_PID开关可显示 PIDLOGGING_SHOW_MODULE默认 False见 zproject/default_settings.py可显示调用模块级别名会被缩写DEBUG→DEBG、WARNING→WARN、CRITICAL→CRITTornado 多分片部署下还会追加分片端口号到日志器名中。此外 zerver/lib/logging_util.py 的ZulipWebhookFormatter专用于 webhook 日志逐项输出 user、client、url、content_type、自定义X-头与缩进格式化后的 payload。性能数据对排查性能问题尤为关键一眼即可判断慢请求源于数据库、Markdown 处理器、memcached 还是其他 Python 代码。但需注意数据库时间只统计连接与接收响应所耗时间当响应数据量大时把数据库结果封装成 Django 对象的 Python 处理开销并不计入这些数字。搜索后端日志log-searchZulip 自带 scripts/log-search 工具可基于多种维度快速检索主server.log也支持搜索信息类似的 NGINX 访问日志。静态资源、用户头像等不重要的请求默认被过滤输出展示时间戳、请求耗时、客户端 IP、响应码、请求方法、主机名与路径被过滤掉的属性不再显示保持简洁zulipexample-prod:~/deployments/current$ ./scripts/log-search realm-name 22:30:36.593 1ms 2606:2800:220:1:248:1893:25c8:1946 302 GET / 22:30:42.508 366ms 2606:2800:220:1:248:1893:25c8:1946 200 GET /login/ 23:18:30.977 1ms 93.184.216.34 302 GET / 23:18:31.286 132ms 93.184.216.34 200 GET /login/ 23:18:51.520 149ms 93.184.216.34 200 GET /login/ 23:19:02.929 1300ms 93.184.216.34 302 POST /accounts/password/reset/ 23:19:08.911 26ms 93.184.216.34 302 GET /accounts/password/reset/OA/b56jfp-bd80ee99b98e703456b3bdcd91892be2/ 23:19:20.796 12ms 93.184.216.34 200 GET /accounts/login/ 23:19:29.323 295ms 8 93.184.216.34 302 POST /accounts/login/ 23:20:04.980 110ms 8 93.184.216.34 200 DELETE /json/users/me/subscriptions zulipexample-prod:~/deployments/current$ ./scripts/log-search 2606:2800:220:1:248:1893:25c8:1946 22:30:36.593 1ms 302 GET https://realm-one.example-prod.example.com/ 22:30:42.508 366ms 200 GET https://realm-one.example-prod.example.com/login/第二个例子按客户端 IP过滤且因为限制了 IP输出中不再重复展示 IP。完整文档见./scripts/log-search --help。源码级补充scripts/log-search 的过滤项非常强大——任意数量的过滤词按 AND 组合可传 IPIPv4/IPv6、hostname、用户 ID纯数字、HTTP 方法、路径/开头、状态码如502、5xx或日期时间前缀authed/unauthed可筛选登录状态。默认排除/static/、/user_uploads/、/user_avatars/、事件拉取/json/events、typing、presence、消息接口与 Sentry 上报路径可用-s/-u/-a/-e/-t/-p/-m/-r分别放行-O则只保留显式放行的路径。工具内置了 Python 日志PYTHON_LOG_LINE_RE与 NGINX 日志NGINX_LOG_LINE_RE两套正则解析器-N/--nginx切换数据源-S/--stats还能输出耗时统计p50/p75/p90/p95/p99 分位数与最大耗时-T/--timeline显示请求间的间隔/重叠以便定位响应停顿并支持-n/--log-files、-A/--all-logs、-H/--min-hours控制日志轮转文件的检索范围回滚天数上限由 zulip.conf 的application_server.access_log_retention_days配置默认 14 天。Blueslip 前端错误报告blueslip命名源自 MIT 用于报告设施问题的表格是 Zulip 自研的浏览器端日志与错误报告库实现于 web/src/blueslip.ts。生产环境用它通过 Sentry若配置报告并聚合错误开发环境则会在消息视图区上方弹出高可见的覆盖层让测试新功能时的异常难以被忽略。开发模式下Blueslip 监听window的error事件几乎能捕获浏览器中发生的任何错误同时对unhandledrejection未处理的 Promise 拒绝也做了覆盖web/src/blueslip.ts它还在开头把Error.stackTraceLimit提高到 100000保留更深的调用栈它也提供手动触发方法供 Zulip JS 代码报告警告与断言失败显式的blueslip.error调用在配置 Sentry 后会发送给 Sentry内部通过Sentry.captureException上报见 web/src/blueslip.tsBlueslip 会在会话期间保存收到的全部通知日志便于观察异常链式触发的情况。可从浏览器控制台打印该日志blueslip require(./src/blueslip); blueslip.get_log();其内部_memory_log有 1000 条上限超出后自动丢弃最旧记录防止内存无界增长web/src/blueslip.ts。Blueslip 的错误级别throw new Error(…)致命错误难以从中恢复。项目尽量少用因为它会杀死当前 JS 线程而非把控制权交还给调用方blueslip.error确定由 bug 引起、足以值得上报的事件但可以妥善处理而不造成明显用户问题例如处理 presence 更新时的异常blueslip.warn有问题但不足以在生产环境向 Sentry 记录 error 的事件开发环境会在 JS 控制台高亮生产环境作为breadcrumb面包屑日志出现在 Sentry 中blueslip.log与blueslip.info开发环境输出到 JS 控制台生产环境进入 Sentry 面包屑。适合记录有助于判断出错时浏览器状态的数据例如用户当时是否处于某个 narrowblueslip.debug类似blueslip.log但开发环境不打印到 JS 控制台。Sentry JavaScript 错误日志可选可选的 JS Sentry 集成会聚合错误展示受影响的用户与 realm、异常前的所有日志、以及错误前发生的 DOM 交互。启用步骤在 Sentry 组织中创建 platform 为JavaScript的项目将 DSN 写入/etc/zulip/settings.py## Controls the DSN used to report JavaScript errors to Sentry.io SENTRY_FRONTEND_DSN https://bbbbbb.ingest.sentry.io/1234若希望对部分错误采样可把SENTRY_FRONTEND_SAMPLE_RATE从默认的1.0调低以zulip用户身份重启/home/zulip/deployments/current/scripts/restart-server前端 Sentry 的加载由 zproject/urls.py 在settings.SENTRY_FRONTEND_DSN配置时启用DSN 默认同样可通过 zulip.conf 的[sentry] frontend_project_dsn注入zproject/default_settings.py。前端性能报告为了便于调试对延迟极其敏感的消息发送链路上的性能问题每当发送消息时 Zulip 都会记录并上报以下数据用户触发消息的时间即起始时间send_message响应从服务器返回的时间浏览器通过get_events协议收到消息的时间后两者相互竞争存在竞态消息是否被本地回显locally echoed若被回显回显内容与服务器渲染内容是否存在差异——该数据可用于统计本地回显系统的有效性。相关代码全部位于zerver/views/report.py服务端接收上报与 web/src/sent_messages.ts前端记录发送时间并调用report_error上报其中携带locally_echoed标记等元数据见 web/src/sent_messages.ts。类似的报告也用于切换视图narrow耗时动作发起的时间更新后的消息流对用户可见的时间切换视图后浏览器再次进入空闲的时间用于捕获产生大量延迟工作的场景。这些性能指标与 Blueslip 的异常收集、Sentry 的聚合分析共同构成 Zulip 完整的前端可观测性闭环让零 JS 异常这一项目目标有了一套可量化、可追踪的实现路径。【免费下载链接】zulipZulip server and web application. Open-source team chat that helps teams stay productive and focused.项目地址: https://gitcode.com/GitHub_Trending/zu/zulip创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表