Create a gist now

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AWS Lambda function CloudWatch Logs -> Slack
var aws = require('aws-sdk'),
https = require('https'),
zlib = require('zlib');
util = require('util');
// If you've used KMS to encrypt your slack, insert your CiphertextBlob here
var ENCRYPTED_URL = 'AQEC1423...';
// IF NOT, you can take the risk to insert your Slack URL here
// e.g. '/services/QWERTY/ASDFGHJ/zxYTinNLK';
var UNENCRYPTED_URL = null;
// Your Slack channel name goes here
var CHANNEL = '#your channel';
// These words in a log entry will trigger a red color in Slack
var DANGER_MESSAGES = ["Error", "Exception"];
// These words in a log entry will trigger a yellow color in Slack
var WARNING_MESSAGES = ["Warning"];
/* OK, you can stop touching things now */
var config = {
hostName: 'hooks.slack.com',
maxAttempts: 10
};
var cloudWatchLogs = new aws.CloudWatchLogs({
apiVersion: '2014-03-28'
});
var encryptedSlackUrl = {
CiphertextBlob: new Buffer(ENCRYPTED_URL, 'base64')
};
var kms = new aws.KMS({
apiVersion: '2014-11-01'
});
if (!UNENCRYPTED_URL) {
kms.decrypt(encryptedSlackUrl, function (error, data) {
if (error) {
config.tokenInitError = error;
console.log(error);
} else {
config.url = data.Plaintext.toString('ascii');
}
});
} else {
config.url = UNENCRYPTED_URL;
}
exports.handler = function (event, context) {
var payload = new Buffer(event.awslogs.data, 'base64');
zlib.gunzip(payload, function (error, result) {
if (error) {
context.fail(error);
} else {
var result_parsed = JSON.parse(result.toString('ascii'));
var parsedEvents = result_parsed.logEvents.map(function(logEvent) {
return parseEvent(logEvent, result_parsed.logGroup, result_parsed.logStream);
});
postToSlack(parsedEvents);
}
});
function parseEvent(logEvent, logGroupName, logStreamName) {
return {
message: logEvent.message,
logGroupName: logGroupName,
logStreamName: logStreamName,
timestamp: new Date(logEvent.timestamp).toISOString()
};
}
function getSeverityLevel(message) {
var severity = "good";
var dangerMessages = [
"Error",
"Exception"
];
var warningMessages = [
"Warning"
];
for(var dangerMessagesItem in DANGER_MESSAGES) {
if (message.indexOf(DANGER_MESSAGES[dangerMessagesItem]) != -1) {
severity = "danger";
break;
}
}
if (severity == "good") {
for(var warningMessagesItem in WARNING_MESSAGES) {
if (message.indexOf(WARNING_MESSAGES[warningMessagesItem]) != -1) {
severity = "warning";
break;
}
}
}
return severity;
}
function postToSlack(parsedEvents, attempt) {
if (!config.url) {
if (!attempt) attempt = 1;
if (config.tokenInitError) {
console.log('Error in decrypt the token. Not retrying.');
return context.fail(config.tokenInitError);
}
if (attempt > config.maxAttempts) {
console.log('Decrypt timed out');
return context.fail("Timeout");
}
console.log('Cannot flush logs since authentication token has not been initialized yet. Retrying in 100ms');
setTimeout(function () { postToSlack(parsedEvents, attempt + 1) }, 100);
return;
}
var messages = parsedEvents.map(function(e) { return e.message }).join('\n');
var logGroup = parsedEvents[0] && parsedEvents[0].logGroupName || 'Missing logGroup';
try {
var options = {
method: 'POST',
hostname: config.hostName,
port: 443,
path: config.url
};
var postData = {
"channel": CHANNEL,
"username": "AWS CloudWatch",
"text": "*" + logGroup + "*",
"icon_emoji": ":aws:"
};
postData.attachments = [
{
"color": getSeverityLevel(messages),
"text": messages
}
];
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
context.done(null, "OK");
});
});
req.on('error', function(e) {
context.fail(e.message);
});
req.write(util.format("%j", postData));
req.end();
} catch (ex) {
console.log(ex.message);
context.fail(ex.message);
}
}
};
@tomfa

This comment has been minimized.

Show comment
Hide comment
Owner

tomfa commented Sep 3, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment