LoginSignup
9
2

More than 1 year has passed since last update.

CloudWatchのログをSlack通知する方法

Last updated at Posted at 2023-06-26

はじめに

今回、CloudWatchのログをSlack通知する実装をしました。
こんなに簡単に実装できるんだ〜という感じでしたが、色んなサービスが連携して面白かったので記録しておきます。

やりたいこと:

CloudWatchに溜められるlogの中に、FATAL、ERROR、WARNINGの情報がある場合、slackのログ監視用チャンネルにログの内容を出力する

前提:

・slack通知したいログがCloudWatchに出力されている
・slack通知用のアプリを作成している
・slack通知用のチャンネルを用意している(アプリをインテグレーションしている)

CloudWatchのログをSlack通知する全体像

【参考】

ログデータの流れまとめ

  1. AWS CloudWatchの特定のロググループに出力される
  2. サブスクリプションフィルターのパターンに一致した場合、紐づけたLambda関数に送られる
  3. Lambdaの中で整形される
  4. slackにポストされる

実装手順

1. ログをslackにポストする用のLambda関数を作成

2. 出力対象ログが属するロググループにサブスクリプションフィルターを設定

・ログの送信先として、先ほど作成したLambda関数を選択する
・サブスクリプションフィルターパターンにFATAL、ERROR、WARNINGをフィルタするように設定する

※CloudWatchに出力されているログの形に合わせてパターンを設定する
例:JSON形式でログが出力されている場合のパターン↓
{ ( $.level = "FATAL" ) || ( $.level = "WARN" ) || ( $.level = "ERROR" ) ) }

3. System Manegerのパラメータストアでパラメータを新規作成し、slackアプリのトークンを保存

※ 安全な文字列のタイプを指定し、文字列を暗号化する

4. LambdaのIAMロールにSSM読み取り用ポリシー追加

※System Manegerのパラメータストアに登録した内容を取り出すために、Lambdaに紐づくIAMロールにポリシー(iam-ssm-get-parameter)を追加

5. Lambdaの中身を書いていく

・ログの情報を取り出す
・メッセージを整形
・postMessageする

require 'json'
require 'base64'
require 'zlib'
require 'aws-sdk-ssm'
require 'aws-sdk-kms'
require 'slack-ruby-client'

def lambda_handler(event:, context:)
  data = JSON.parse(Zlib::GzipReader.new(StringIO.new(Base64.decode64(event["awslogs"]["data"]))).read)

  # 集計するログの基本情報
  log_group = data["logGroup"]
  log_stream = data["logStream"]
  log_events = data["logEvents"]

  # ログの基本情報
  base_message = "■対象ロググループ:" + log_group + " > " + log_stream + "\n"

  # ログの本文
  logs = []
  log_events.each { |log_event|
    logs_json = JSON.parse(log_event["message"])
    logs.push(logs_json)
  }

  # slackに送信
  post_slack(base_message, logs)
end

#
# slackに送信
#
def post_slack(base_message, logs)
  # 送信するテキスト:ログの基本情報
  text = "<!here>\n"
  text += base_message

  # アタッチメントテキスト:ログの内容
  attachments = []
  attachment_texts = []

  logs.each do |log_json|
    timestamp = log_json["time"]
    attachment_text = "■発生時刻: " + timestamp + "\n"
    attachment_text += "■ログメッセージ: " + message + "\n"
    attachment_texts.push(attachment_text)
  end

  attachments.push({ fallback: 'Error', text: attachment_texts.join("\n\n"), color: 'danger' })
  slack_client.chat_postMessage(channel: 'チャンネル名', text: text, attachments: attachments, as_user: true)
end

#
# slackクライアント
#
def get_slack_client
  ssm_client = Aws::SSM::Client.new
  get_token_request = {
    name: 'xxx', # パラメータ名
    with_decryption: true # 暗号化されている場合は復号し、暗号化されていない場合は何もしない
  }

  Slack.configure do |config|
    config.token = ssm_client.get_parameter(get_token_request).parameter.value
  end
  Slack::Web::Client.new
end

最後に

最近AWSを触り始めた初学者ですが、AWSについてもっと知りたい欲が出てきました。
また、面白いことがあれば記事にしたいと思います!

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2