CloudWatch Logsで任意の文字列を監視し、メールで通知する

はじめに

CloudWatch Logsで集めたログの中で任意の文字列(例えば、Errorとか、Criticalとか)が記載されていた場合、メールなどで通知させたい事がある。

Node.jsをLambdaで動かす事で文字列監視を実装した。

通知先の作成

まずはSNSでメールの宛先から作成しARNを控え、エンドポイントとなるメールアドレスを設定する。 

node.js(lambda)

関数コードを編集

 

ERROR、CRITICALという文字列があったら監視するスクリプト

var zlib = require('zlib');
var aws = require('aws-sdk');
var sns = new aws.SNS({ region: 'ap-northeast-1' });
exports.handler = function(input, context, callback) {
  var data = new Buffer(input.awslogs.data, 'base64');
  zlib.gunzip(data, function(e, rst) {
    if (e) {
      callback(e);
    } else {
      rst = JSON.parse(rst.toString('utf-8'));
      var errL = rst['logEvents']
          .filter(function(evt) { return evt['message'].match(/ERROR/i) ;})
          .filter(function(evt) { return !evt['message'].match(/$^/) ;})
          .map(function(evt) { return evt['message'] });
      console.log('processing[Error]' + errL.length + '/' + rst['logEvents'].length + ' events.');
      var critL = rst['logEvents']
          .filter(function(evt) { return evt['message'].match(/CRITICAL/i) ;})
          .filter(function(evt) { return !evt['message'].match(/$^/) ;})
          .map(function(evt) { return evt['message'] });
      console.log('processing[Crit]' + critL.length + '/' + rst['logEvents'].length + ' events.');
      if (errL.length === 0 && critL.length === 0) { callback(); return; }
      var date = new Date();
      date.setTime(date.getTime() + 1000*60*60*9);
      var dateTime = date.getFullYear() + '/' + ("0" + (date.getMonth() + 1)).slice(-2) + '/' + ("0" + date.getDate()).slice(-2) +
           ' ' + ("0" + date.getHours()).slice(-2) + ':' + ("0" + date.getMinutes()).slice(-2) + ':' + ("0" + date.getSeconds()).slice(-2);
      if (0 < errL.length) {
        // has error log
        var sjct = '[Error] Notify From CloudWatch Logs';
        var pl = { default: '' };
        pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
        pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
        pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
        pl['default'] += 'Messages:\n';
        pl['default'] += errL.join('\n---\n');
        sns.publish({
          Subject: sjct,
          Message: JSON.stringify(pl),
          MessageStructure: 'json',
          TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
        }, function(err, data) {
          if (err) {
            callback(err);
          } else if (0 < critL.length) {
            // has waring too
            var sjct = '[Critical] Notify From CloudWatch Logs';
            var pl = { default: '' };
            pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
            pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
            pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
            pl['default'] += 'Messages:\n';
            pl['default'] += critL.join('\n---\n');
            sns.publish({
              Subject: sjct,
              Message: JSON.stringify(pl),
              MessageStructure: 'json',
              TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
            }, function(err, data) {
              if (err) callback(err);
              else callback(null, data);
            });
          } else {
            callback(null, data);
          }
        });
      } else {
        // has NOT error log == criticaling only
        var sjct = '[Critical] Notify From CloudWatch Logs';
        var pl = { default: '' };
        pl['default'] += 'NotifyAt: ' + dateTime.valueOf() + '\n';
        pl['default'] += 'Log: ' + rst['logGroup'] + ' - ' + rst['logStream'] + '\n';
        pl['default'] += 'Filter: ' + rst['subscriptionFilters'] + '\n';
        pl['default'] += 'Messages:\n';
        pl['default'] += critL.join('\n---\n');
        sns.publish({
          Subject: sjct,
          Message: JSON.stringify(pl),
          MessageStructure: 'json',
          TargetArn: 'arn:aws:sns:ap-northeast-1:1234567890(your no):(your arn)'
        }, function(err, data) {
          if (err) callback(err);
          else callback(null, data);
        });
      }
    }
  });
};

 

トリガーを設定

今回はCloudWatch Logsがトリガーとなるので、CloudWatch Logsを選択し、その後対象のログを選択する。

 

通知先の設定

Amazon SNSのARNを選択する(予めSNSでトピックを作っておく必要がある)

以上