├── .gitignore ├── tsconfig.json ├── gulpfile.js ├── typings.json ├── package.json ├── lib ├── winston-dynamodb.d.ts └── winston-dynamodb.js ├── README.md ├── winston-dynamodb.coffee ├── winston-dynamodb.js └── src └── winston-dynamodb.ts /.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | .DS_Store 3 | .project 4 | node_modules 5 | .metadata 6 | *.tmproj 7 | *.sublime-project 8 | dump.rdb 9 | .idea -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "target": "es5", 6 | "noImplicitAny": false, 7 | "outDir": "lib", 8 | "sourceMap": false 9 | }, 10 | "exclude": [ 11 | "lib", 12 | "node_modules" 13 | ] 14 | } -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require("gulp"), 2 | gutil = require("gulp-util"), 3 | coffee = require("gulp-coffee"); 4 | 5 | gulp.task("coffee", function() { 6 | gulp.src("./**/*.coffee") 7 | .pipe(coffee({bare: false}).on("error", gutil.log)) 8 | .pipe(gulp.dest("./")); 9 | }); 10 | 11 | gulp.task("default", ["coffee"]); -------------------------------------------------------------------------------- /typings.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalDependencies": { 3 | "aws-sdk": "registry:dt/aws-sdk#0.0.0+20160802165139", 4 | "lodash": "registry:dt/lodash#4.14.0+20160802150749", 5 | "node": "registry:env/node#6.0.0+20160723033700", 6 | "winston": "registry:dt/winston#0.0.0+20160417152829" 7 | }, 8 | "dependencies": { 9 | "node-uuid": "registry:npm/node-uuid#1.4.7+20160723033700" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "winston-dynamodb", 3 | "description": "A Winston transport for Amazon DynamoDB", 4 | "version": "0.3.1", 5 | "author": { 6 | "name": "JeongWoo Chang", 7 | "email": "inspired.jw@gmail.com" 8 | }, 9 | "homepage": "https://github.com/inspiredjw/winston-dynamodb", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/inspiredjw/winston-dynamodb.git" 13 | }, 14 | "typings": "./lib/winston-dynamodb.d.ts", 15 | "keywords": [ 16 | "logging", 17 | "sysadmin", 18 | "tools", 19 | "winston", 20 | "amazon", 21 | "dynamodb", 22 | "aws" 23 | ], 24 | "dependencies": { 25 | "aws-sdk": "^2.1.32", 26 | "lodash": "^4.12.0", 27 | "node-uuid": "1.4.*" 28 | }, 29 | "devDependencies": { 30 | "gulp": "^3.9.0", 31 | "gulp-coffee": "^2.3.1", 32 | "gulp-util": "^3.0.5", 33 | "winston": "^1.0.0" 34 | }, 35 | "main": "./winston-dynamodb" 36 | } 37 | -------------------------------------------------------------------------------- /lib/winston-dynamodb.d.ts: -------------------------------------------------------------------------------- 1 | import * as winston from 'winston'; 2 | import { TransportInstance } from 'winston'; 3 | export interface DynamoDBTransportOptions { 4 | useEnvironment?: boolean; 5 | accessKeyId?: string; 6 | secretAccessKey?: string; 7 | region?: string; 8 | tableName: string; 9 | level: string; 10 | dynamoDoc?: boolean; 11 | } 12 | export interface DynamoDBTransportInstance extends TransportInstance { 13 | new (options?: DynamoDBTransportOptions): DynamoDBTransportInstance; 14 | } 15 | export declare class DynamoDB extends winston.Transport implements DynamoDBTransportInstance { 16 | regions: string[]; 17 | name: string; 18 | level: string; 19 | db: any; 20 | AWS: any; 21 | region: string; 22 | tableName: string; 23 | dynamoDoc: boolean; 24 | constructor(options?: DynamoDBTransportOptions); 25 | log(level: any, msg: any, meta: any, callback: any): any; 26 | } 27 | declare module "winston" { 28 | interface Transports { 29 | DynamoDB: DynamoDB; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DynamoDB Transport for Winston 2 | 3 | A DynamoDB transport for [winston][0]. 4 | 5 | ## Usage 6 | ```javascript 7 | var winston = require('winston'); 8 | 9 | require('winston-dynamodb').DynamoDB; 10 | 11 | winston.add(winston.transports.DynamoDB, options); 12 | ``` 13 | 14 | ## Options 15 | 16 | ``` 17 | accessKeyId : your AWS access key id 18 | secretAccessKey : your AWS secret access key 19 | region : the region where the domain is hosted 20 | useEnvironment : use process.env values for AWS access, secret, & region 21 | tableName : DynamoDB table name 22 | dynamoDoc : if this is set to true, the *meta* parameter will be stored as a subobject using DynamoDB's DocumentClient rather than as a JSON string. 23 | ``` 24 | 25 | ## Prerequisite 26 | 27 | Make a table with `tableName` 28 | 29 | The table schema depends on how you intend to use it. 30 | 31 | #### Simplest 32 | 33 | The table should have 34 | 35 | - hash key: (String) level 36 | - range key: (String) timestamp 37 | 38 | > Note: Timestamp has a millisecond resolution. So whether this key setup will work depends on how many log messages you expect. 39 | > 40 | > That is, the uniqueness of level + timestamp means max: 1 log message of a given level per millisecond. 41 | 42 | It is nice to have it as a range key for queries. 43 | 44 | #### More Robust 45 | 46 | To ensure you can log as many messages as you like, alternatively use: 47 | 48 | - hash key: (String) id (Will be a uuid) 49 | - range key: (String) timestamp 50 | 51 | Using the id as hash ensures that all log items will have unique keys and be included. 52 | 53 | ## Region 54 | 55 | Available Regions 56 | 57 | - us-east-1 58 | - us-west-1 59 | - us-west-2 60 | - eu-west-1 61 | - ap-northeast-1 62 | - ap-southeast-1 63 | - ap-southeast-2 64 | - sa-east-1 65 | 66 | ## AWS Credentials 67 | 68 | All of these options are values that you can find from your Amazon Web Services account: 'accessKeyId', 'secretAccessKey' and 'awsAccountId'. 69 | 70 | Alternatively, pass in useEnvironment: true and the process.env values will be used. 71 | > (Functions in AWS Lambda environment and works with default AWS Credentials Global Configuration .config in other node environments.) 72 | 73 | ## Installation 74 | 75 | ``` bash 76 | $ npm install winston 77 | $ npm install winston-dynamodb 78 | ``` 79 | 80 | #### Author: [JeongWoo Chang](http://twitter.com/inspiredjw) 81 | 82 | [0]: https://github.com/winstonjs/winston -------------------------------------------------------------------------------- /winston-dynamodb.coffee: -------------------------------------------------------------------------------- 1 | winston = require "winston" 2 | util = require "util" 3 | AWS = require "aws-sdk" 4 | uuid = require("node-uuid") 5 | _ = require "lodash" 6 | hostname = require("os").hostname() 7 | 8 | # Return timestamp with YYYY-MM-DD HH:mm:ss 9 | datify = (timestamp) -> 10 | date = new Date timestamp 11 | date = 12 | year: date.getFullYear() 13 | month: date.getMonth() + 1 14 | day: date.getDate() 15 | 16 | hour: date.getHours() 17 | minute: date.getMinutes() 18 | second: date.getSeconds() 19 | millisecond: date.getMilliseconds() 20 | 21 | keys = _.without Object.keys date, "year", "month", "day" 22 | date[key] = "0" + date[key] for key in keys when date[key] < 10 23 | "#{date.year}-#{date.month}-#{date.day} #{date.hour}:#{date.minute}:#{date.second}.#{date.millisecond}" 24 | 25 | DynamoDB = exports.DynamoDB = (options = {}) -> 26 | regions = [ 27 | "us-east-1" 28 | "us-west-1" 29 | "us-west-2" 30 | "eu-west-1" 31 | "eu-central-1" 32 | "ap-northeast-1" 33 | "ap-northeast-2" 34 | "ap-southeast-1" 35 | "ap-southeast-2" 36 | "sa-east-1" 37 | ] 38 | 39 | if options.useEnvironment 40 | options.accessKeyId = process.env.AWS_ACCESS_KEY_ID 41 | options.secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY 42 | options.region = process.env.AWS_REGION 43 | 44 | unless options.accessKeyId? 45 | throw new Error "need accessKeyId" 46 | 47 | unless options.secretAccessKey? 48 | throw new Error "need secretAccessKey" 49 | 50 | unless options.region? 51 | throw new Error "need region" 52 | 53 | unless options.region in regions 54 | throw new Error "unavailable region given" 55 | 56 | unless options.tableName? 57 | throw new Error "need tableName" 58 | 59 | unless options.useEnvironment 60 | AWS.config.update 61 | accessKeyId: options.accessKeyId 62 | secretAccessKey: options.secretAccessKey 63 | region: options.region 64 | 65 | # Winston Options 66 | @.name = "dynamodb" 67 | @.level = options.level or "info" 68 | 69 | # DynamoDB Options= 70 | @.db = new AWS.DynamoDB() 71 | @.AWS = AWS 72 | @.region = options.region 73 | 74 | # a-z, A-Z, 0-9, _ (underscore), - (hyphen) and . (period) 75 | @.tableName = options.tableName 76 | @.dynamoDoc = options.dynamoDoc 77 | 78 | util.inherits DynamoDB, winston.Transport 79 | 80 | DynamoDB::log = (level, msg, meta, callback) -> 81 | putCallback = (err, data) => 82 | if err 83 | @.emit "error", err 84 | callback err, null if callback 85 | else 86 | @.emit "logged" 87 | callback null, "logged" if callback 88 | 89 | if @.dynamoDoc == true 90 | params = 91 | TableName: @.tableName 92 | Item: 93 | id: uuid.v4() 94 | level: level 95 | timestamp: datify Date.now() 96 | msg: msg 97 | hostname: hostname 98 | 99 | unless _.isEmpty meta 100 | params.Item.meta = meta 101 | 102 | dynamoDocClient = new @.AWS.DynamoDB.DocumentClient({ 103 | service: @.db 104 | }) 105 | dynamoDocClient.put params, putCallback 106 | else 107 | params = 108 | TableName: @.tableName 109 | Item: 110 | id: 111 | "S": uuid.v4() 112 | level: 113 | "S": level 114 | timestamp: 115 | "S": datify Date.now() 116 | msg: 117 | "S": msg 118 | hostname: 119 | "S": hostname 120 | 121 | unless _.isEmpty meta 122 | params.Item.meta = "S": JSON.stringify meta 123 | 124 | @.db.putItem params, putCallback 125 | 126 | # Add DynamoDB to the transports by winston 127 | winston.transports.DynamoDB = DynamoDB 128 | -------------------------------------------------------------------------------- /winston-dynamodb.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var AWS, DynamoDB, _, datify, hostname, util, uuid, winston, 3 | indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 4 | 5 | winston = require("winston"); 6 | 7 | util = require("util"); 8 | 9 | AWS = require("aws-sdk"); 10 | 11 | uuid = require("node-uuid"); 12 | 13 | _ = require("lodash"); 14 | 15 | hostname = require("os").hostname(); 16 | 17 | datify = function(timestamp) { 18 | var date, i, key, keys, len; 19 | date = new Date(timestamp); 20 | date = { 21 | year: date.getFullYear(), 22 | month: date.getMonth() + 1, 23 | day: date.getDate(), 24 | hour: date.getHours(), 25 | minute: date.getMinutes(), 26 | second: date.getSeconds(), 27 | millisecond: date.getMilliseconds() 28 | }; 29 | keys = _.without(Object.keys(date, "year", "month", "day")); 30 | for (i = 0, len = keys.length; i < len; i++) { 31 | key = keys[i]; 32 | if (date[key] < 10) { 33 | date[key] = "0" + date[key]; 34 | } 35 | } 36 | return date.year + "-" + date.month + "-" + date.day + " " + date.hour + ":" + date.minute + ":" + date.second + "." + date.millisecond; 37 | }; 38 | 39 | DynamoDB = exports.DynamoDB = function(options) { 40 | var ref, regions; 41 | if (options == null) { 42 | options = {}; 43 | } 44 | regions = ["localhost", "us-east-1", "us-west-1", "us-west-2", "eu-west-1", "eu-central-1", "ap-northeast-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "sa-east-1"]; 45 | if (options.useEnvironment) { 46 | options.accessKeyId = process.env.AWS_ACCESS_KEY_ID; 47 | options.secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; 48 | options.region = process.env.AWS_REGION; 49 | } 50 | if (options.accessKeyId == null) { 51 | throw new Error("need accessKeyId"); 52 | } 53 | if (options.secretAccessKey == null) { 54 | throw new Error("need secretAccessKey"); 55 | } 56 | if (options.region == null) { 57 | throw new Error("need region"); 58 | } 59 | if (ref = options.region, indexOf.call(regions, ref) < 0) { 60 | throw new Error("unavailable region given"); 61 | } 62 | if (options.tableName == null) { 63 | throw new Error("need tableName"); 64 | } 65 | if (!options.useEnvironment) { 66 | AWS.config.update({ 67 | accessKeyId: options.accessKeyId, 68 | secretAccessKey: options.secretAccessKey, 69 | region: options.region 70 | }); 71 | } 72 | this.name = "dynamodb"; 73 | this.level = options.level || "info"; 74 | if (options.region == "localhost") { 75 | this.db = new AWS.DynamoDB({ 76 | endpoint: new AWS.Endpoint(options.endpoint) 77 | }); 78 | } 79 | else { 80 | this.db = new AWS.DynamoDB(); 81 | } 82 | this.AWS = AWS; 83 | this.region = options.region; 84 | this.tableName = options.tableName; 85 | return this.dynamoDoc = options.dynamoDoc; 86 | }; 87 | 88 | util.inherits(DynamoDB, winston.Transport); 89 | 90 | DynamoDB.prototype.log = function(level, msg, meta, callback) { 91 | var dynamoDocClient, params, putCallback; 92 | putCallback = (function(_this) { 93 | return function(err, data) { 94 | if (err) { 95 | _this.emit("error", err); 96 | if (callback) { 97 | return callback(err, null); 98 | } 99 | } else { 100 | _this.emit("logged"); 101 | if (callback) { 102 | return callback(null, "logged"); 103 | } 104 | } 105 | }; 106 | })(this); 107 | if (this.dynamoDoc === true) { 108 | params = { 109 | TableName: this.tableName, 110 | Item: { 111 | id: uuid.v4(), 112 | level: level, 113 | timestamp: datify(Date.now()), 114 | msg: msg, 115 | hostname: hostname 116 | } 117 | }; 118 | if (!_.isEmpty(meta)) { 119 | params.Item.meta = meta; 120 | } 121 | dynamoDocClient = new this.AWS.DynamoDB.DocumentClient({ 122 | service: this.db 123 | }); 124 | return dynamoDocClient.put(params, putCallback); 125 | } else { 126 | params = { 127 | TableName: this.tableName, 128 | Item: { 129 | id: { 130 | "S": uuid.v4() 131 | }, 132 | level: { 133 | "S": level 134 | }, 135 | timestamp: { 136 | "S": datify(Date.now()) 137 | }, 138 | msg: { 139 | "S": msg 140 | }, 141 | hostname: { 142 | "S": hostname 143 | } 144 | } 145 | }; 146 | if (!_.isEmpty(meta)) { 147 | params.Item.meta = { 148 | "S": JSON.stringify(meta) 149 | }; 150 | } 151 | return this.db.putItem(params, putCallback); 152 | } 153 | }; 154 | 155 | winston.transports.DynamoDB = DynamoDB; 156 | 157 | }).call(this); 158 | -------------------------------------------------------------------------------- /src/winston-dynamodb.ts: -------------------------------------------------------------------------------- 1 | import * as winston from 'winston'; 2 | import * as util from 'util'; 3 | import * as AWS from 'aws-sdk'; 4 | import * as uuid from 'node-uuid'; 5 | import * as _ from 'lodash'; 6 | import * as os from 'os'; 7 | 8 | import { Transport } from 'winston'; 9 | import { TransportInstance } from 'winston'; 10 | 11 | const hostname = os.hostname(); 12 | 13 | function datify(timestamp) { 14 | let dateTS = new Date(timestamp); 15 | let date = { 16 | year: dateTS.getFullYear(), 17 | month: dateTS.getMonth() + 1, 18 | day: dateTS.getDate(), 19 | hour: dateTS.getHours(), 20 | minute: dateTS.getMinutes(), 21 | second: dateTS.getSeconds(), 22 | millisecond: dateTS.getMilliseconds() 23 | }; 24 | 25 | let keys = _.without(Object.keys(date), "year", "month", "day"); 26 | let len = keys.length; 27 | for (let i = 0; i < len; i++) { 28 | let key = keys[i]; 29 | if (date[key] < 10) { 30 | date[key] = "0" + date[key]; 31 | } 32 | } 33 | return `${date.year}-${date.month}-${date.day} ${date.hour}:${date.minute}:${date.second}.${date.millisecond}`; 34 | } 35 | 36 | export interface DynamoDBTransportOptions { 37 | useEnvironment?: boolean; 38 | accessKeyId?: string; 39 | secretAccessKey?: string; 40 | region?: string; 41 | tableName: string; 42 | level: string; 43 | dynamoDoc?: boolean; 44 | } 45 | 46 | export interface DynamoDBTransportInstance extends TransportInstance { 47 | new (options?: DynamoDBTransportOptions): DynamoDBTransportInstance; 48 | } 49 | 50 | export class DynamoDB extends winston.Transport implements DynamoDBTransportInstance { 51 | regions: string[]; 52 | name: string; 53 | level: string; 54 | db; // Type? 55 | AWS; // Type? 56 | region: string; 57 | tableName: string; 58 | dynamoDoc: boolean; 59 | 60 | constructor(options?: DynamoDBTransportOptions) { 61 | super(options); 62 | 63 | if (options == null) { 64 | options = {}; 65 | } 66 | this.regions = ["us-east-1", "us-west-1", "us-west-2", "eu-west-1", "eu-central-1", "ap-northeast-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "sa-east-1"]; 67 | if (options.useEnvironment) { 68 | options.accessKeyId = process.env.AWS_ACCESS_KEY_ID; 69 | options.secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; 70 | options.region = process.env.AWS_REGION; 71 | } 72 | if (options.accessKeyId == null) { 73 | throw new Error("need accessKeyId"); 74 | } 75 | if (options.secretAccessKey == null) { 76 | throw new Error("need secretAccessKey"); 77 | } 78 | if (options.region == null) { 79 | throw new Error("need region"); 80 | } 81 | if (this.regions.indexOf(options.region) < 0) { 82 | throw new Error("unavailable region given"); 83 | } 84 | if (options.tableName == null) { 85 | throw new Error("need tableName"); 86 | } 87 | if (!options.useEnvironment) { 88 | AWS.config.update({ 89 | accessKeyId: options.accessKeyId, 90 | secretAccessKey: options.secretAccessKey, 91 | region: options.region 92 | }); 93 | } 94 | this.name = "dynamodb"; 95 | this.level = options.level || "info"; 96 | this.db = new AWS.DynamoDB(); 97 | this.AWS = AWS; 98 | this.region = options.region; 99 | this.tableName = options.tableName; 100 | this.dynamoDoc = options.dynamoDoc; 101 | } 102 | 103 | log(level, msg, meta, callback) { 104 | let dynamoDocClient, params; 105 | let putCallback = (_this) => { 106 | return (err, data) => { 107 | if (err) { 108 | _this.emit("error", err); 109 | if (callback) { 110 | return callback(err, null); 111 | } 112 | } else { 113 | _this.emit("logged"); 114 | if (callback) { 115 | return callback(null, "logged"); 116 | } 117 | } 118 | }; 119 | }; 120 | putCallback(this); 121 | if (this.dynamoDoc === true) { 122 | params = { 123 | TableName: this.tableName, 124 | Item: { 125 | id: uuid.v4(), 126 | level: level, 127 | timestamp: datify(Date.now()), 128 | msg: msg, 129 | hostname: hostname 130 | } 131 | }; 132 | if (!_.isEmpty(meta)) { 133 | params.Item.meta = meta; 134 | } 135 | dynamoDocClient = new this.AWS.DynamoDB.DocumentClient({ 136 | service: this.db 137 | }); 138 | return dynamoDocClient.put(params, putCallback); 139 | } else { 140 | params = { 141 | TableName: this.tableName, 142 | Item: { 143 | id: { 144 | "S": uuid.v4() 145 | }, 146 | level: { 147 | "S": level 148 | }, 149 | timestamp: { 150 | "S": datify(Date.now()) 151 | }, 152 | msg: { 153 | "S": msg 154 | }, 155 | hostname: { 156 | "S": hostname 157 | } 158 | } 159 | }; 160 | if (!_.isEmpty(meta)) { 161 | params.Item.meta = { 162 | "S": JSON.stringify(meta) 163 | }; 164 | } 165 | return this.db.putItem(params, putCallback); 166 | } 167 | } 168 | 169 | } 170 | 171 | import { Transports } from 'winston'; 172 | declare module "winston" { 173 | export interface Transports { 174 | DynamoDB: DynamoDB; 175 | } 176 | } -------------------------------------------------------------------------------- /lib/winston-dynamodb.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __extends = (this && this.__extends) || function (d, b) { 3 | for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; 4 | function __() { this.constructor = d; } 5 | d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 6 | }; 7 | var winston = require('winston'); 8 | var AWS = require('aws-sdk'); 9 | var uuid = require('node-uuid'); 10 | var _ = require('lodash'); 11 | var os = require('os'); 12 | var hostname = os.hostname(); 13 | function datify(timestamp) { 14 | var dateTS = new Date(timestamp); 15 | var date = { 16 | year: dateTS.getFullYear(), 17 | month: dateTS.getMonth() + 1, 18 | day: dateTS.getDate(), 19 | hour: dateTS.getHours(), 20 | minute: dateTS.getMinutes(), 21 | second: dateTS.getSeconds(), 22 | millisecond: dateTS.getMilliseconds() 23 | }; 24 | var keys = _.without(Object.keys(date), "year", "month", "day"); 25 | var len = keys.length; 26 | for (var i = 0; i < len; i++) { 27 | var key = keys[i]; 28 | if (date[key] < 10) { 29 | date[key] = "0" + date[key]; 30 | } 31 | } 32 | return date.year + "-" + date.month + "-" + date.day + " " + date.hour + ":" + date.minute + ":" + date.second + "." + date.millisecond; 33 | } 34 | var DynamoDB = (function (_super) { 35 | __extends(DynamoDB, _super); 36 | function DynamoDB(options) { 37 | _super.call(this, options); 38 | if (options == null) { 39 | options = {}; 40 | } 41 | this.regions = ["us-east-1", "us-west-1", "us-west-2", "eu-west-1", "eu-central-1", "ap-northeast-1", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "sa-east-1"]; 42 | if (options.useEnvironment) { 43 | options.accessKeyId = process.env.AWS_ACCESS_KEY_ID; 44 | options.secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY; 45 | options.region = process.env.AWS_REGION; 46 | } 47 | if (options.accessKeyId == null) { 48 | throw new Error("need accessKeyId"); 49 | } 50 | if (options.secretAccessKey == null) { 51 | throw new Error("need secretAccessKey"); 52 | } 53 | if (options.region == null) { 54 | throw new Error("need region"); 55 | } 56 | if (this.regions.indexOf(options.region) < 0) { 57 | throw new Error("unavailable region given"); 58 | } 59 | if (options.tableName == null) { 60 | throw new Error("need tableName"); 61 | } 62 | if (!options.useEnvironment) { 63 | AWS.config.update({ 64 | accessKeyId: options.accessKeyId, 65 | secretAccessKey: options.secretAccessKey, 66 | region: options.region 67 | }); 68 | } 69 | this.name = "dynamodb"; 70 | this.level = options.level || "info"; 71 | this.db = new AWS.DynamoDB(); 72 | this.AWS = AWS; 73 | this.region = options.region; 74 | this.tableName = options.tableName; 75 | this.dynamoDoc = options.dynamoDoc; 76 | } 77 | DynamoDB.prototype.log = function (level, msg, meta, callback) { 78 | var dynamoDocClient, params; 79 | var putCallback = function (_this) { 80 | return function (err, data) { 81 | if (err) { 82 | _this.emit("error", err); 83 | if (callback) { 84 | return callback(err, null); 85 | } 86 | } 87 | else { 88 | _this.emit("logged"); 89 | if (callback) { 90 | return callback(null, "logged"); 91 | } 92 | } 93 | }; 94 | }; 95 | putCallback(this); 96 | if (this.dynamoDoc === true) { 97 | params = { 98 | TableName: this.tableName, 99 | Item: { 100 | id: uuid.v4(), 101 | level: level, 102 | timestamp: datify(Date.now()), 103 | msg: msg, 104 | hostname: hostname 105 | } 106 | }; 107 | if (!_.isEmpty(meta)) { 108 | params.Item.meta = meta; 109 | } 110 | dynamoDocClient = new this.AWS.DynamoDB.DocumentClient({ 111 | service: this.db 112 | }); 113 | return dynamoDocClient.put(params, putCallback); 114 | } 115 | else { 116 | params = { 117 | TableName: this.tableName, 118 | Item: { 119 | id: { 120 | "S": uuid.v4() 121 | }, 122 | level: { 123 | "S": level 124 | }, 125 | timestamp: { 126 | "S": datify(Date.now()) 127 | }, 128 | msg: { 129 | "S": msg 130 | }, 131 | hostname: { 132 | "S": hostname 133 | } 134 | } 135 | }; 136 | if (!_.isEmpty(meta)) { 137 | params.Item.meta = { 138 | "S": JSON.stringify(meta) 139 | }; 140 | } 141 | return this.db.putItem(params, putCallback); 142 | } 143 | }; 144 | return DynamoDB; 145 | }(winston.Transport)); 146 | exports.DynamoDB = DynamoDB; 147 | --------------------------------------------------------------------------------