一、背景
公司客户端项目是基于Electron开发的,里面集成了一些第三方的插件和库,经常会遇到一些兼容性和性能问题,目前是通过用户反馈,运维去远程协助,处理不了的就会提bug给开发人员排查,部分问题测试又难以重现,所以处理起来很头疼。于是就想到要输出错误日志文件和错误告警上报,然后把错误信息发送到企业微信。方便开发定位和排查问题。
二、开通机器人权限
首先,我们要在企业微信群里创建一个机器人,叫日志监控机器人,专门用来进行 Sentry 的错误告警上报,如下图所示
因为Sentry调用webhook发送的数据报文格式和企业微信机器人要求的数据报文格式不一致,所以需要搭建Node服务,将Sentry的数据报文做下转换,并按照企业微信机器人的数据报文格式进行发送。
企业微信机器人要求的数据报文格式:
{
"msgtype": "text",
"text": {
"content": "广州今日天气:29度,大部分多云,降雨概率:60%",
"mentioned_list":["wangqing","@all"],
"mentioned_mobile_list":["13800001111","@all"]
}
}
Sentry发送的数据报文格式:
{
id: '7',
project: 'xx',
project_name: 'xx',
project_slug: 'xx',
logger: null,
level: 'error',
culprit: 'raven.scripts.runner in main',
message: 'This is an example Python exception',
url: 'http://sentry.xxxxxxx.com/organizations/sentry/issues/7/?referrer=webhooks_plugin',
triggering_rules: [],
event: {
event_id: 'f602ac321ee04bc28a20c9f4d446ef48',
level: 'error',
version: '5',
type: 'default',
logentry: {
formatted: 'This is an example Python exception',
message: null,
params: null
},
logger: '',
modules: { 'my.package': '1.0.0' },
platform: 'python',
timestamp: 1622734089.769465,
received: 1622734089.7702,
environment: 'prod',
user: {
id: '1',
email: 'sentry@example.com',
ip_address: '127.0.0.1',
username: 'sentry',
name: 'Sentry',
geo: [Object]
},
request: {
url: 'http://example.com/foo',
method: 'GET',
data: [Object],
query_string: [Array],
cookies: [Array],
headers: [Array],
env: [Object],
inferred_content_type: 'application/json',
fragment: null
},
...
}
}
三、搭建 Node 服务
基于egg.js快速搭建一个Node服务
npm init egg-message-notification --type=saimple
npm i
启动项目
npm run dev
open http://localhost:7001
看到下图说明启动成功了
路由配置如下
在app/controller/sentry.js文件中创建 SentryController,用来负责数据报文的转换,代码如下:
'use strict';
const Controller = require('egg').Controller;
const axios = require('axios');
const CircularJSON = require('circular-json');
/**
* 对当前时间进行格式化
*/
const fmtDateTime = () => {
const date = new Date();
const year = date.getFullYear();
let month = date.getMonth() + 1;
let hour = date.getHours();
let min = date.getMinutes();
month = month < 10 ? `0${month}` : month;
hour = hour < 10 ? `0${hour}` : hour;
min = min < 10 ? `0${min}` : min;
return `${year}-${month}-${date.getDate()} ${hour}:${min}`;
};
class SentryController extends Controller {
/**
* 接收Sentry发送过来的Webhook
*/
async recvSentryWebhook() {
const { ctx } = this;
const { request: { body } } = ctx;
const error = body.data && body.data.error || {};
ctx.logger.info(body);
const ROBOT_DATA = {
msgtype: 'markdown',
markdown: {
content: `<font color=\"warning\">${error.release || error.extra._productName}</font>发生错误:
> 错误原因: <font color=\"info\">${error.title}</font>
> 错误时间: <font color=\"info\">${fmtDateTime()}</font>
> 错误级别: <font color=\"${error.level === 'fatal' ? '#FF0000' : '#008000'}\">${error.level}</font>
> 错误链接: [查看日志](${error.web_url})`,
},
};
const result = await axios({
url: '企业微信机器人webhook地址',
method: 'POST',
headers: {
'content-type': 'application/json',
},
data: JSON.stringify(ROBOT_DATA),
});
ctx.body = {
status: 'success',
data: CircularJSON.stringify(result),
msg: '提醒成功',
};
}
}
module.exports = SentryController;
最后,别忘了配置csrf,在config/config.default.js添加如下代码
config.security = {
csrf: {
enable: false,
},
};
四、部署Node服务
我的做法是把代码提交到git仓库,然后再服务器clone下来,安装依赖,执行npm run start命名启动服务,看到下图就说明启动成功了