From ecda51ce47371f77982ed7de9bd1f2af439bf146 Mon Sep 17 00:00:00 2001 From: Gunnar Lium Date: Wed, 30 Mar 2016 08:18:29 +0200 Subject: [PATCH] Initial commit --- .editorconfig | 14 +++++ .gitignore | 1 + .npmignore | 2 + README.md | 42 ++++++++++++++ index.js | 112 ++++++++++++++++++++++++++++++++++++++ object.assign-polyfill.js | 25 +++++++++ package.json | 35 ++++++++++++ 7 files changed, 231 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 README.md create mode 100644 index.js create mode 100644 object.assign-polyfill.js create mode 100644 package.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..73a9d28 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +; http://editorconfig.org +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[{package.json,.travis.yml}] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..eb79dd5 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +node_modules +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..d7a3c8b --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +Lambda Papertrail Logger +======================== + +A module for logging to Papertrail from AWS Lambda. + +It will add metadata to the logs based on the context the function is run in, and format everything as JSON for easy reuse in other services. + +This module is designed to be used within Aptoma, and is therefore rather opinionated with limited configuration options. + +Installation +------------ + + npm install --save @aptoma/lambda-papertrail-logger + +Usage +----- + +```js +var createLogger = require('@aptoma/lambda-papertrail-logger'); + +exports.handler = function (event, context) { + var log = createLogger(context, papertrailConfig); + log.info('Executing handler', event); +}; +``` + +The logger is an extension of `winston.Logger`. + +In addition to being created with the relevant transports and formatters, it also exposes a `log.timerEvent(event)` method. + +`log.timerEvent(event)` will format a timer event created by [@aptoma/timer](https://github.com/aptoma/node.timer) for sending to our internal event analytics service: + +```js +var createLogger = require('@aptoma/lambda-papertrail-logger'); +var timer = require('@aptoma/timer'); + +exports.handler = function (event, context) { + var elapsed = timer('ProcessingTime'); + var log = createLogger(context, papertrailConfig); + log.timerEvent(elapsed()); +}; +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..65125a9 --- /dev/null +++ b/index.js @@ -0,0 +1,112 @@ +'use strict'; + +require('./object.assign-polyfill'); +var winston = require('winston'); +var moment = require('moment-timezone'); +var papertrailTransport = require('winston-papertrail').Papertrail; + +/** + * @typedef {Object} LambdaLogger + * @augments {winston.Logger} + * @property {function} timerEvent + */ + +/** + * @typedef {Object} TimerEvent + * @property {string} name + * @property {string} summary + * @property {float} msec + * @property {Array} hrtime + */ + +/** + * @typedef {Object} LambdaContext + * @property {string} functionName + * @property {string} invokedFunctionArn + * @property {string} awsRequestId + * @property {string} functionVersion + */ + +/** + * @typedef {Object} PapertrailConfig + * @property {string} host + * @property {int} port + * @see https://papertrailapp.com/account/destinations + */ + +/** + * @param {LambdaContext} context + * @param {PapertrailConfig} papertrailConfig + * @returns {LambdaLogger} + */ +module.exports = function createLogger(context, papertrailConfig) { + var log = new (winston.Logger)({ + transports: [ + // This transport is used to output message to your local console, and is also the one picked up by Cloudwatch + new (winston.transports.Console)({ + formatter: function (options) { + return timestamp() + ' ' + options.message; + } + }) + ] + }); + + // Only send logs to Papertrail if we're running on Lambda, and have provided config options + if (context.awsRequestId && papertrailConfig && papertrailConfig.host) { + addPapertrailLogger(context, papertrailConfig); + } + + /** + * Log timer event, formatted for sending to Grimm + * + * @param {TimerEvent} event + */ + log.timerEvent = function (event) { + log.info(event.summary, { + name: event.name, + fields: { + msec: parseFloat(event.msec.toFixed(3)) + }, + tags: { + service: context.functionName + }, + _tags: ['event'] + }); + }; + + return log; + + /** + * @param {LambdaContext} context The context object supplied to the handler by AWS Lambda + * @param {PapertrailConfig} papertrailConfig + */ + function addPapertrailLogger(context, papertrailConfig) { + log.add(papertrailTransport, { + host: papertrailConfig.host, + port: papertrailConfig.port, + program: context.functionName, + hostname: getHostnameForARN(context.invokedFunctionArn), + includeMetaInMessage: false, + messageFormat: function (level, message, meta) { + return JSON.stringify(Object.assign({ + _time: timestamp(), + msg: message + }, meta, { + version: context.functionVersion, + requestId: context.awsRequestId + })); + } + }); + } + + function getHostnameForARN(ARN) { + var arnParts = ARN.split(':'); + return arnParts[3] ? 'aws-lambda-' + arnParts[3] : 'aws-lambda'; + } + + function timestamp() { + var m = moment(); + m.tz('Europe/Oslo'); + return m.format('YYYY-MM-DD HH:mm:ss.SSS'); + } +}; diff --git a/object.assign-polyfill.js b/object.assign-polyfill.js new file mode 100644 index 0000000..4ed52d0 --- /dev/null +++ b/object.assign-polyfill.js @@ -0,0 +1,25 @@ +'use strict'; +// Polyfill for Object.assign, while running Node.js < v4 +// @link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign +if (typeof Object.assign !== 'function') { + (function () { + Object.assign = function (target) { + if (target === undefined || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + var output = Object(target); + for (var index = 1; index < arguments.length; index++) { + var source = arguments[index]; + if (source !== undefined && source !== null) { + for (var nextKey in source) { + if (source.hasOwnProperty(nextKey)) { + output[nextKey] = source[nextKey]; + } + } + } + } + return output; + }; + })(); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..545999f --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "@aptoma/lambda-papertrail-logger", + "version": "1.0.0", + "description": "A logger to send AWS Lambda logs to Papertrail", + "author": "Gunnar Lium ", + "main": "index.js", + "repository": { + "type": "git", + "url": "git@github.com:aptoma/lambda-papertrail-logger.git" + }, + "scripts": { + "test": "jscs index.js", + "release": "release-it -n -i patch", + "release:minor": "release-it -n -i minor", + "release:major": "release-it -n -i major" + }, + "dependencies": { + "moment-timezone": "^0.5.3", + "winston": "^2.2.0", + "winston-papertrail": "gunnarlium/winston-papertrail#temp-fixes" + }, + "license": "MIT", + "jscsConfig": { + "preset": "yandex", + "disallowMultipleVarDecl": "exceptUndefined", + "maximumLineLength": null, + "validateIndentation": "\t" + }, + "npm": { + "publish": true + }, + "github": { + "release": true + } +}