Lambda 실시간 로그 감시, 로그 레벨마다 통지처를 SNS로 일괄 관리하고 Slack이나 메일로 통지하고 싶다

하고 싶은 일


  • CloudWatch Alarm 알림 대상
  • AWS Lambda 로그 모니터링 알림 대상

  • 알림 레벨에 따라 SNS에서 소스 코드 변경없이 유연하게 일괄 관리할 수 있도록하고 싶은 실현 방식을 검토한 메모.

    예를 들면, 다음과 같이 통지 레벨마다 통지 목적지를 정의한다.


    알림 레벨 (SNS 주제)
    알림 대상
    정의


    INFO
    · Slack #log_info
    시스템 로그. 조사시의 효율화 목적으로 평상시는 보지 않는 로그.

    WARN
    · Slack #log_warn
    이상 사태의 예조를 알리는 통지. 장해의 미연 방지에 연결되는 액션 검토하기 위하여.

    ERROR
    · Slack #log_error · 메일 서비스 운영자 ML
    비정상적인 상황이 명백한 심각한 오류.


    검토한 결론은 이런 느낌.


    Lambda의 실행 로그는 구독 필터로 필터링하여 각 SNS에 통지하는 Lambda를 물고 SNS에서 Slack 알림은 Lambda의 Blueprint "cloudwatch-alarm-to-slack"을 CloudWatch 경유 이외의 알림에도 대응할 수 있도록 수정하여 대응했다.

    로그 모니터링 대상 Lambda



    index.js
    'use strict';
    
    exports.handler = (event, context, callback) => {
    
        // INFO通知したい時
        console.log('SNS-INFO Subject-INFO Message-INFO');
        // WARN通知したい時
        console.log('SNS-WARN Subject-WARN Message-WARN');
        // ERROR通知したい時
        console.log('SNS-ERROR Subject-ERROR Message-ERROR');
    
        callback(null);
    };
    

    이하의 로그 패턴에 합치했을 경우에 통지할 수 있도록 했다.
    SNS-<LEVEL> <SUBJECT> <MESSAGE>

    CloudWatchLogs 구독 필터로 SNS 알림까지



    구독 필터 설정 방법



    로그 모니터링 대상 Lambda에 대해 CloudWatchLogs에서 대상 로그 그룹에 대해 Lambda 스트리밍을 설정합니다. 로그 형식은 "AWS Lambda"를 선택하여 구독 필터로 다음을 등록합니다.
    [timestamp=*Z, request_id="*-*", level="SNS-*", subject, message]
    

    필터 및 패턴 구문 을 참고로 SNS 통지시의 건명( subject )과 메시지 본문( message )을 Lambda로 취급할 수 있는 변수로 자동 추출해 주는 기능을 사용했다.

    SNS에 알리는 람다



    index.js
    'use strict';
    
    const zlib = require('zlib');
    const aws = require('aws-sdk');
    const sns = new aws.SNS({apiVersion: '2010-03-31'});
    
    function publishToSnsTopic(topicArn, subject, message) {
        sns.publish({
            TopicArn: topicArn,
            Subject: subject,
            Message: message
        }, function (err, data) {
            if (err) { console.log('[Error] Failed sns published: ' + data);}
        });
    }
    
    exports.handler = (event, context, callback) => {
        const base64Logs = new Buffer(event['awslogs']['data'], 'base64');
        zlib.gunzip(base64Logs, function(err, binary) {
            var data = JSON.parse(binary.toString('utf8'));
    
            if(!Array.isArray(data.logEvents)) { return; }
            data.logEvents.forEach(function(d) {
               if (d.extractedFields === undefined ) { return; }
               if (d.extractedFields.level === undefined ) { return; }
    
               var topicArn;
               switch (d.extractedFields.level) {
                   case "SNS-INFO":
                       topicArn = process.env.SNS_INFO_TOPIC_ARN;
                       break;
                   case "SNS-WARN":
                       topicArn = process.env.SNS_WARN_TOPIC_ARN;
                       break;
                   case "SNS-ERROR":
                       topicArn = process.env.SNS_ERROR_TOPIC_ARN;
                       break;
                   default:
                       console.error('[ERROR] Level is not matched: ' + data.message);
                       return;
               }
               publishToSnsTopic(topicArn, d.extractedFields.subject, d.extractedFields.message);
            });
        });
    };
    

    소스 코드를 변경하지 않고 SNS를 변경할 수 있도록 Lambda의 환경 변수에서 SNS의 ARN을 관리할 수 있도록 했다.
    Lambda 역할에는 sns:Publish 정책 권한이 필요합니다.

    SNS에서 Slack 알림 람다



    Blueprint의 "cloudwatch-alarm-to-slack"을 아래의 diff처럼 수정.
    CloudWatch 경유가 아니면 JSON 퍼스 에러가 되므로, CloudWatchLogs 경유도 대응할 수 있도록 수정했다.
    KMS라든지의 설정은 여기의 기사를 참고로 했다.
    @@ -84,18 +84,23 @@ function postMessage(message, callback) {
     }
    
     function processEvent(event, callback) {
    -    const message = JSON.parse(event.Records[0].Sns.Message);
    -
    -    const alarmName = message.AlarmName;
    -    //var oldState = message.OldStateValue;
    -    const newState = message.NewStateValue;
    -    const reason = message.NewStateReason;
    +    const messageStr = event.Records[0].Sns.Message;
    +    const subject = event.Records[0].Sns.Subject;
    
         const slackMessage = {
             channel: slackChannel,
    -        text: `${alarmName} state is now ${newState}: ${reason}`,
    +        text: subject + "\n" + messageStr
         };
    
    +    try {
    +        const message = JSON.parse(messageStr);
    +        const alarmName = message.AlarmName;
    +        //var oldState = message.OldStateValue;
    +        const newState = message.NewStateValue;
    +        const reason = message.NewStateReason;
    +        slackMessage.text = `${alarmName} state is now ${newState}: ${reason}`;
    +    } catch (e) {}
    

    이것으로 완료.

    Lambda 로그 모니터링 Slack 알림





    CloudWatch Alert의 Slack 알림





    참고



    * cloudwatchlogs -> lambda -> SNS를 시도했습니다.
    * AWS Lambda에서 CloudWatch Logs 로그 본문을 Slack 알림(2)

    좋은 웹페이지 즐겨찾기