Work Records

日々の作業記録です。ソフトウェアエンジニアリング全般から、趣味の話まで。

slackに飛んでくるアラートの統計を取る

slackに色々なアラートを飛ばしている

slackに色々なアラート通知を飛ばしている人は多いと思います。

例えばDatadogと連携すればこのような感じで。
f:id:kenjiszk:20171220025043p:plain



アラートの統計情報を取りたい

そこでこう言った需要が出て来ます。

  • なんか最近アラート多くないか?半年前と比べてどうなってんだろ。
  • ちょっと無駄なアラートが増えて来たかもしれない。
  • この半年でアラートの件数を半分に減らそう!

アラートの件数の推移を取りたくなってくるわけです。

slackの機能

当初、slackにそう言った機能くらいあるだろ、とたかをくくっていたわけですが、
slackには特定のchannelの特定のbotが発言した回数の統計情報、くらい細かいものは見せてくれませんでした。。。
まあ当たり前か。。。

APIを使って取得してみる

ただし、便利なAPIはたくさん用意してくれているので、サクッとやりたいことは実現できそうです。
api.slack.com

サンプルスクリプト

ということで、サクッとGoでslackのapiを叩いて、mysqlにデータを保存するスクリプトを作りました。
主要部分だけ抜粋。

slackのchannels.historyからデータを取得
/* パラメータのセット、slackのtokenは環境変数から、idはデータを取得したいchannelのchannel id*/
values := url.Values{}
values.Add("token", os.Getenv("SLACK_ACCESS_TOKEN"))
values.Add("channel", id)

resp, err := http.Get("https://slack.com/api/channels.history" + "?" + values.Encode())
if err != nil {
        return err
}
defer resp.Body.Close()

/* レスポンスをよみこんでjsonに変換 */
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
        return err
}
jsonStr := string(body)
jsonBytes := ([]byte)(jsonStr)
rInfo := new(RespInfo)
if err := json.Unmarshal(jsonBytes, rInfo); err != nil {
        return err
}

必要なデータだけ受け取るので、構造体は以下のように定義

type RespInfo struct {
        Ok       bool      `json:"ok"`
        HasMore  bool      `json:"has_more"`
        Messages []Message `json:"messages"`
}

type Message struct {
        BotID string `json:"bot_id"`
        TS    string `json:"ts"`
}
MySQLに突っ込む

rInfo.Messagesに取得して来たメッセージが入っているので必要なものをMySQLに突っ込む
テーブル定義はこのように。

CREATE DATABASE slack_stats;
USE slack_stats;
CREATE TABLE `alerts` (
  `timestamp` varchar(256) DEFAULT NULL,
  `bot_id` varchar(256) DEFAULT NULL,
  `slack_channel` varchar(256) DEFAULT NULL,
  UNIQUE KEY `i1` (`timestamp`,`bot_id`,`slack_channel`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

突っ込む側の処理は以下。
apiから取れるslack idはただの文字列なので人が読めるような文字列に変換する処理を入れてあります。

dbStr := fmt.Sprintf("root:%s@tcp(%s:3306)/slack_stats", os.Getenv("MYSQL_PASSWORD"), os.Getenv("MYSQL_HOST"))
db, err := sql.Open("mysql", dbStr)
if err != nil {
        return err
}
defer db.Close()

stmtIns, err := db.Prepare("INSERT IGNORE INTO alerts (timestamp, bot_id, slack_channel) VALUES (?, ?, ?)")
if err != nil {
        return err
}
defer stmtIns.Close()

_, err = stmtIns.Exec(timestampe, bot_id, slack_channel)
if err != nil {
        return err
}
結果

こんな感じで突っ込めました。

mysql> select * from alerts where slack_channel = 'infra' limit 10;
+-------------------+------------------+---------------+
| timestamp         | bot_id           | slack_channel |
+-------------------+------------------+---------------+
| 1513606245.000070 | Datadog          | infra         |
| 1513606374.000311 | Datadog          | infra         |
| 1513607148.000258 | incoming-webhook | infra         |
| 1513607148.000663 | incoming-webhook | infra         |
| 1513607209.000354 | incoming-webhook | infra         |
| 1513607210.000247 | incoming-webhook | infra         |
| 1513607270.000409 | incoming-webhook | infra         |
| 1513607271.000133 | incoming-webhook | infra         |
| 1513607333.000085 | incoming-webhook | infra         |
| 1513607333.000509 | incoming-webhook | infra         |
+-------------------+------------------+---------------+
10 rows in set (0.00 sec)



redashによる可視化

可視化するためのSQL

データを突っ込んでいるだけなので、時間単位のアラート件数を出すように以下のクエリを発行します。

  • timestampの小数点以下を切って
  • JSTにするためにoffsetつけて
  • formatを時間単位にする
mysql> SELECT DATE_FORMAT(FROM_UNIXTIME(CAST(timestamp AS UNSIGNED) + 32400), '%Y-%m-%d %H') AS time, COUNT(*) FROM alerts WHERE slack_channel = 'infra' AND bot_id = 'Datadog' GROUP BY time LIMIT 10;
+---------------+----------+
| time          | COUNT(*) |
+---------------+----------+
| 2017-12-18 23 |        4 |
| 2017-12-19 00 |        3 |
| 2017-12-19 01 |        3 |
| 2017-12-19 02 |        1 |
| 2017-12-19 03 |        1 |
| 2017-12-19 04 |        1 |
| 2017-12-19 05 |        1 |
| 2017-12-19 06 |        4 |
| 2017-12-19 07 |       14 |
| 2017-12-19 08 |       13 |
+---------------+----------+
10 rows in set, 111 warnings (0.00 sec)

クエリがかけたらredashでグラフ化

redashの使い方とかはここでは説明しませんが、MySQLにデータが入ってしまえば何か使い慣れたグラフ化ツールで可視化してあげるといいと思います。
f:id:kenjiszk:20171220043656p:plain
こうやって可視化してみると、朝と夜にアラートが多いということがわかります。



今後

最初に書いたように、以下のような観点でのアラートマネジメントを進めていこうと思っています。

  • なんか最近アラート多くないか?半年前と比べてどうなってんだろ。
  • ちょっと無駄なアラートが増えて来たかもしれない。
  • この半年でアラートの件数を半分に減らそう!