반응형

이전 포스팅에서, cloudwatch에서 알람이 발생할 때마다 SNS, 그리고 lambda를 trigger해서 slack으로 알람을 보냈다.

* 이전 포스팅:

https://countrymouse.tistory.com/entry/awscloudwatchslack

 

[AWS] cloudwatch, lambda를 통해 slack으로 알람 보내기 (Nodejs)

Cloudwatch에서 알람 조건을 만들어서, 해당 알람이 발생할 때 slack으로 notify하는 환경을 만들어보자. 1. AWS > CloudWatch > 경보 생성 1) 지표 선택 2) 지표에서 원하는 tracking값 및 임계치 선택 (알람..

countrymouse.tistory.com

 

그런데, alarm으로 받는 text의 포맷이 아래와 같이 가독성이 떨어진다. 그래서, 보다 가독성 있게 수정해보기로 한다.

 

위 가독성 없는 텍스트를 아래 포맷으로 수정한다.

 

코드는 아래 티스토리 블로거 향로님의 코드를 사용하였고 아래는 한줄한줄 해석을 달아보았다. 

(직접 하나씩 만들어보려고 했는데, 반나절의 삽질을 해도 원하는 대로 잘 안돼서 다른 분의 코드를 사용하기로 했다...)

https://jojoldu.tistory.com/586

 

CloudWatch 이상 지표를 슬랙 알람으로 받기 (feat. SNS, Lambda)

AWS 서비스를 이용하면 CloudWatch를 통해 서비스의 이상 지표를 손쉽게 확인할 수 있습니다. 이를테면 다음과 같은 경우인데요. 평소보다 로드밸런서로 들어오는 요청양이 2배이상 높다거나 RDS의 C

jojoldu.tistory.com

 

향로님 코드에 cause부분과 관련된 코드는 삭제하였고(나는 '원인'을 '설명'으로 대체했다), 일부분에 주석을 추가하였다.

// 구성 -> 환경변수로 webhook을 받도록 합니다.
const ENV = process.env
if (!ENV.webhook) throw new Error('Missing environment variable: webhook')

const webhook = ENV.webhook;
const https = require('https')

const statusColorsAndMessage = {
    ALARM: {"color": "danger", "message":"위험"},
    INSUFFICIENT_DATA: {"color": "warning", "message":"데이터 부족"},
    OK: {"color": "good", "message":"정상"}
}


// 1. 여기서부터 시작 
exports.handler = async (event) => {
    await exports.processEvent(event);
}


// 2. processEvent로 넘어간다.
exports.processEvent = async (event) => {
    console.log('Event:', JSON.stringify(event))
    const snsMessage = event.Records[0].Sns.Message;
    console.log('SNS Message:', snsMessage);
    const postData = exports.buildSlackMessage(JSON.parse(snsMessage))  // 3. buildSlackMessage
    await exports.postSlack(postData, webhook);
}

// snsMessage는 이전 포스팅 값들이 담겨 있는데, 참고해보자.


// 3. buildSlackMessage(위 jane_bot2에서 출력한 snsMessage를 받아와서 각각 처리한다.)
exports.buildSlackMessage = (data) => {
    const newState = statusColorsAndMessage[data.NewStateValue];  // 위 jane_bot2 기준 ALARM
    const oldState = statusColorsAndMessage[data.OldStateValue];     // 위 jane_bot2 기준 OK
    const executeTime = exports.toYyyymmddhhmmss(data.StateChangeTime); // 2022-05-23T03:16:02.297+0000
    const description = data.NewStateReason;  // 기존 코드엔 description, cause가 둘 다 있지만 나는 description에 대체했다.
    return {
        attachments: [
            {
                title: `[${data.AlarmName}]`,
                color: newState.color,
                fields: [
                    {
                        title: 'Timestamp',
                        value: executeTime
                    },
                    {
                        title: 'Alarm',
                        value: description
                    },
                    {
                        title: 'Previous Status',
                        value: oldState.message,
                        short: true
                    },
                    {
                        title: 'Current Status',
                        value: `*${newState.message}*`,
                        short: true
                    },
                    {
                        title: 'Link',
                        value: exports.createLink(data)
                    }
                ]
            }
        ]
    }
}

// CloudWatch 알람 바로 가기 링크
exports.createLink = (data) => {
    return `https://console.aws.amazon.com/cloudwatch/home?region=${exports.exportRegionCode(data.AlarmArn)}#alarm:alarmFilter=ANY;name=${encodeURIComponent(data.AlarmName)}`;
}

exports.exportRegionCode = (arn) => {
    return  arn.replace("arn:aws:cloudwatch:", "").split(":")[0];
}


// 타임존 UTC -> KST (2022-05-23T03:16:02.297+0000 --> 2022-05-23 13:14:48)
exports.toYyyymmddhhmmss = (timeString) => {

    if(!timeString){
        return '';
    }

    const kstDate = new Date(new Date(timeString).getTime() + 32400000);

    function pad2(n) { return n < 10 ? '0' + n : n }

    return kstDate.getFullYear().toString()
        + '-'+ pad2(kstDate.getMonth() + 1)
        + '-'+ pad2(kstDate.getDate())
        + ' '+ pad2(kstDate.getHours())
        + ':'+ pad2(kstDate.getMinutes())
        + ':'+ pad2(kstDate.getSeconds());
}  

exports.postSlack = async (message, slackUrl) => {
    return await request(exports.options(slackUrl), message);
}

exports.options = (slackUrl) => {
    const {host, pathname} = new URL(slackUrl);
    return {
        hostname: host,
        path: pathname,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
    };
}

function request(options, data) {

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            res.setEncoding('utf8');
            let responseBody = '';

            res.on('data', (chunk) => {
                responseBody += chunk;
            });

            res.on('end', () => {
                resolve(responseBody);
            });
        });

        req.on('error', (err) => {
            console.error(err);
            reject(err);
        });

        req.write(JSON.stringify(data));
        req.end();
    });
}

 

완성!

반응형

+ Recent posts