alert-manager.js 2.79 KB
/**
 * Created by zhangwenxue on 2019/07/23.
 */
'use strict';

const _ = require('lodash');
const ddAlert = require('./dingding-alert');
const MIN_SEND_INTERVAL = 10 * 1000;
const moment = require('moment');
const logger = global.yoho.logger;

/**
 * AlertManager
 * 统一调度dingtalk的告警
 *
 */
const AlertManager = {
  isAtAll: false,
  pendingAgents: {},

  /**
   * post agent信息到当前pending队列
   * @param {AlertAgent} agent
   */
  post(agent) {
    if (!this.pendingAgents[agent.name]) {
      this.pendingAgents[agent.name] = agent;
    }
    this.report();
  },

  /**
   * 向dingtalk robot发消息,使用_.throttle
   */
  report: _.throttle(() => AlertManager._report(), MIN_SEND_INTERVAL, {leading: true, trailing: true}),

  /**
   * 发消息到dingtalk robot
   */
  _report() {
    const now = Date.now();
    let ats = [];
    let text = [];

    text.push(`**本次告警**: ${moment(now).format('YYYY-MM-DD HH:mm:ss')}\n`);
    text.push(`**上次告警**: ${moment(this.lastSendTime).format('YYYY-MM-DD HH:mm:ss')}\n`);

    text = text.concat(Object.keys(this.pendingAgents).map(agentName => {
      const agent = this.pendingAgents[agentName];

      ats = ats.concat(agent.ats);
      return agent.getMarkdownReport();
    }));

    // 发送
    ddAlert(text.join('\n'), _.uniq(ats), this.isAtAll)
      .catch(e => {
        logger.warn('AlertManager send dingtalk alert:', e);
      });

    // 重置状态
    this.lastSendTime = now;
    this.pendingAgents = {};
  }
};

/**
 * AlertAgent
 * 模块告警Agent
 */
class AlertAgent {
  /**
   *
   * @param {string} name 模块名
   * @param {Object} subjectMap 模块告警信息类型,subjectId=>subject
   * @param {Array[string]} ats 需要@的手机号
   * @param {boolean} isAtAll 是否@全体人员, default=false
   */
  constructor(name, subjectMap, ats = [], isAtAll = false) {
    this.name = name;
    this.subjectMap = subjectMap;
    this.ats = ats;
    if (isAtAll) {
      AlertManager.isAtAll = true;
    }

    this.msgs = {};
  }

  /**
   * 发送告警信息
   * @param {string|number} subjectId 消息类型
   * @param {string|null} text 附加信息, 注: 发dingtalk时只发最后一条的text
   */
  send(subjectId, text = '') {
    let msg = _.get(this.msgs, subjectId, {text, count: 0});

    this.msgs[subjectId] = msg;
    msg.count++;
    AlertManager.post(this);
  }

  /**
   * 生成当前Agent的 markdown 格式报文
   */
  getMarkdownReport() {
    let report = [`## ${this.name} `];

    report.push(this.ats.map(at => `@${at}`).join(' '));

    const msgs = Object.keys(this.msgs).map(id => {
      const msg = this.msgs[id];

      return `- ${this.subjectMap[id]} ${msg.text} ${msg.count}次`;
    });

    report = report.concat(msgs);

    return report.join('\n');
  }
}

module.exports = AlertAgent;