Last active
November 7, 2018 19:14
-
-
Save psutton1179a/59c5a6d9ddb9c02ba8c0b0cfc91d29af to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Splunk logging for AWS Lambda | |
* | |
* This function logs to a Splunk host using Splunk's HTTP event collector API. | |
* | |
* Define the following Environment Variables in the console below to configure | |
* this function to log to your Splunk host: | |
* | |
* 1. SPLUNK_HEC_URL: URL address for your Splunk HTTP event collector endpoint. | |
* Default port for event collector is 8088. Example: https://host.com:8088/services/collector | |
* | |
* 2. SPLUNK_HEC_TOKEN: Token for your Splunk HTTP event collector. | |
* To create a new token for this Lambda function, refer to Splunk Docs: | |
* http://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector#Create_an_Event_Collector_token | |
*/ | |
'use strict'; | |
const loggerConfig = { | |
url: process.env.SPLUNK_HEC_URL, | |
token: process.env.SPLUNK_HEC_TOKEN, | |
}; | |
const SplunkLogger = require('./lib/mysplunklogger'); | |
const logger = new SplunkLogger(loggerConfig); | |
exports.handler = (event, context, callback) => { | |
console.log('Received event:', JSON.stringify(event, null, 2)); | |
// Log JSON objects to Splunk | |
logger.log(event); | |
// Log JSON objects with optional 'context' argument (recommended) | |
// This adds valuable Lambda metadata including functionName as source, awsRequestId as field | |
logger.log(event, context); | |
// Log strings | |
logger.log(`value1 = ${event.key1}`, context); | |
// Log with user-specified timestamp - useful for forwarding events with embedded | |
// timestamps, such as from AWS IoT, AWS Kinesis, AWS CloudWatch Logs | |
// Change "Date.now()" below to event timestamp if specified in event payload | |
logger.logWithTime(Date.now(), event, context); | |
// Advanced: | |
// Log event with user-specified request parameters - useful to set input settings per event vs token-level | |
// Full list of request parameters available here: | |
// http://docs.splunk.com/Documentation/Splunk/latest/RESTREF/RESTinput#services.2Fcollector | |
logger.logEvent({ | |
time: Date.now(), | |
host: 'serverless', | |
source: `lambda:${context.functionName}`, | |
sourcetype: 'httpevent', | |
event: event, | |
index: 'cloudtrail', | |
}); | |
// Send all the events in a single batch to Splunk | |
logger.flushAsync((error, response) => { | |
if (error) { | |
callback(error); | |
} else { | |
console.log(`Response from Splunk:\n${response}`); | |
callback(null, event.key1); // Echo back the first key value | |
} | |
}); | |
}; | |
'use strict'; | |
const url = require('url'); | |
const Logger = function Logger(config) { | |
this.url = config.url; | |
this.token = config.token; | |
this.addMetadata = true; | |
this.setSource = true; | |
this.parsedUrl = url.parse(this.url); | |
// eslint-disable-next-line import/no-dynamic-require | |
this.requester = require(this.parsedUrl.protocol.substring(0, this.parsedUrl.protocol.length - 1)); | |
// Initialize request options which can be overridden & extended by consumer as needed | |
this.requestOptions = { | |
hostname: this.parsedUrl.hostname, | |
path: this.parsedUrl.path, | |
port: this.parsedUrl.port, | |
method: 'POST', | |
headers: { | |
Authorization: `Splunk ${this.token}`, | |
}, | |
rejectUnauthorized: false, | |
}; | |
this.payloads = []; | |
}; | |
// Simple logging API for Lambda functions | |
Logger.prototype.log = function log(message, context) { | |
this.logWithTime(Date.now(), message, context); | |
}; | |
Logger.prototype.logWithTime = function logWithTime(time, message, context) { | |
const payload = {}; | |
if (Object.prototype.toString.call(message) === '[object Array]') { | |
throw new Error('message argument must be a string or a JSON object.'); | |
} | |
payload.event = message; | |
// Add Lambda metadata | |
if (typeof context !== 'undefined') { | |
if (this.addMetadata) { | |
// Enrich event only if it is an object | |
if (message === Object(message)) { | |
payload.event = JSON.parse(JSON.stringify(message)); // deep copy | |
payload.event.awsRequestId = context.awsRequestId; | |
} | |
} | |
if (this.setSource) { | |
payload.source = `lambda:${context.functionName}`; | |
} | |
} | |
payload.time = new Date(time).getTime() / 1000; | |
this.logEvent(payload); | |
}; | |
Logger.prototype.logEvent = function logEvent(payload) { | |
this.payloads.push(JSON.stringify(payload)); | |
}; | |
Logger.prototype.flushAsync = function flushAsync(callback) { | |
callback = callback || (() => {}); // eslint-disable-line no-param-reassign | |
console.log('Sending event(s)'); | |
const req = this.requester.request(this.requestOptions, (res) => { | |
res.setEncoding('utf8'); | |
console.log('Response received'); | |
res.on('data', (data) => { | |
let error = null; | |
if (res.statusCode !== 200) { | |
error = new Error(`error: statusCode=${res.statusCode}\n\n${data}`); | |
console.error(error); | |
} | |
this.payloads.length = 0; | |
callback(error, data); | |
}); | |
}); | |
req.on('error', (error) => { | |
callback(error); | |
}); | |
req.end(this.payloads.join(''), 'utf8'); | |
}; | |
module.exports = Logger; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment