diff --git a/README.md b/README.md index 25dc6cc..3e27ade 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,10 @@ ex, `-s ./example-file-path` or `--save-state ./example-file-path`. `-e or --access-token` Etherscan access token, used to access etherscan for ABI importing. +`--loggly-token` Loggly access token +`--loggly-subdomain` Loggly subdomain +`--loggly-tag` Loggly tag + ### ENV Variables Environmental variables come second in priority, you can specify every parameter indicated as an ENV variable. Additionally you can mix between different settings if convenient for your application.In your `.env` you can specify parameters as @@ -66,6 +70,13 @@ Environmental variables come second in priority, you can specify every parameter `ACCESS_TOKEN` +`LOGGLY_ACCESS_TOKEN` + +`LOGGLY_SUB_DOMAIN` + +`LOGGLY_TAG` + + The inputs are very similar to when using CLI only `QUICK_MODE` is different in the sense that it can use true/false values ### Config file diff --git a/package.json b/package.json index 58a9151..bdfb2fd 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "request": "^2.82.0", "request-promise": "^4.2.1", "web3": "0.19.0", - "winston": "2.3.1", - "yamljs": "^0.3.0" + "winston": "^2.4.0", + "yamljs": "^0.3.0", + "winston-loggly-bulk": "^2.0.1" } } diff --git a/src/command.js b/src/command.js index e30a9bf..329865d 100644 --- a/src/command.js +++ b/src/command.js @@ -18,6 +18,10 @@ const defaultSaveState = null; const defaultOutputType = 'terminal'; const defaultAccessToken = ''; +const defaultLogglyAccessToken = ''; +const defaultLogglySubDomain = ''; +const defaultLogglyTag = ''; + const validLoggerValues = ['info', 'error', 'debug']; /** @@ -109,6 +113,9 @@ export default (watchPath) => { .option('-l, --log-level [n]', 'Log level', handelInputValues('LOG_LEVEL', watchConfig.logLevel, defaultLogLevel)) .option('-o,--output-type [n]', 'Output type', handelInputValues('OUTPUT_TYPE', watchConfig.outputType, defaultOutputType)) .option('-e,--access-token [n]', 'etherscan access token', handelInputValues('ACCESS_TOKEN', watchConfig.accessToken, defaultAccessToken)) + .option('--loggly-token,--loggly-token [n]', 'loggly access token', handelInputValues('LOGGLY_ACCESS_TOKEN', watchConfig.logglyAccessToken, defaultLogglyAccessToken)) + .option('--loggly-subdomain,--loggly-subdomain [n]', 'loggly subdomain', handelInputValues('LOGGLY_SUB_DOMAIN', watchConfig.logglySubDomain, defaultLogglySubDomain)) + .option('--loggly-tag,--loggly-tag [n]', 'loggly tag', handelInputValues('LOGGLY_TAG', watchConfig.logglyTag, defaultLogglyTag)) .parse(process.argv); } if (typeof program === 'undefined') { throw new Error(noArgserrorMsg); } @@ -125,6 +132,9 @@ export default (watchPath) => { logLevel: validateParamBasedOnValue(program.logLevel, validLoggerValues, loggererrorMsg), outputType: program.outputType, accessToken: program.accessToken, + logglyAccessToken: program.logglyToken, + logglySubDomain: program.logglySubdomain, + logglyTag: program.logglyTag, }; }; diff --git a/src/index.js b/src/index.js index 4c331d8..675e0e9 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import logger, { logError, setLoggerLevel } from './logger'; +import logger, { logError, setLoggerLevel, setLogglyTransport } from './logger'; import command, { getCommandVars } from './command'; import Decoder from './decoder'; import { isAddress } from './web3/utils'; @@ -63,8 +63,15 @@ const transactionHandler = async (transaction) => { */ const main = async () => { const { from, to, addresses, quickMode, - lastBlockNumberFilePath, logLevel } = command(); + lastBlockNumberFilePath, logLevel, + logglyAccessToken, + logglySubDomain, + logglyTag, + } = command(); + setLoggerLevel(logLevel); + setLogglyTransport(logglyAccessToken, logglySubDomain, logglyTag); + logger.debug('Start process'); addresses.forEach((address) => { if (!isAddress(address)) throw new Error(`Address ${address} is not a valid ethereum address`); }); const PromisifiedAbiObjects = addresses.map(async address => ( diff --git a/src/logger.js b/src/logger.js index 850087f..7d38dbb 100644 --- a/src/logger.js +++ b/src/logger.js @@ -1,43 +1,83 @@ import winston from 'winston'; + import { getCommandVars } from './command'; +require('winston-loggly-bulk'); + const loggerConsoleOptions = { timestamp: false, colorize: false, formatter: options => `${options.message}`, + json: true, + stringify: true, }; -const logger = new (winston.Logger)({ - transports: [ - new (winston.transports.Console)(loggerConsoleOptions), - ] }); +let logger; + +function getLogger() { + logger = new winston.Logger(); + logger.add(winston.transports.Console, loggerConsoleOptions); + + return logger; +} + +logger = getLogger(); /** -* sets logger level -* @param {string} -*/ + * sets logger level + * @param {string} + */ export const setLoggerLevel = (logLevel) => { logger.transports.console.level = logLevel; }; + +/** + * add loggly transport + * @param token + * @param subdomain + * @param tags + */ +export const setLogglyTransport = (token, subdomain, tags) => { + logger.add(winston.transports.Loggly, { + token, + subdomain, + tags: [tags], + json: true, + }); +}; + /** * This will print out the error as json formatted * @param {*} error * @param {*} customMessage */ export const logError = (error, customMessage = null, isStack = true) => { - switch (getCommandVars('outputType')) { + const commandVars = getCommandVars('outputType'); + switch (commandVars) { case 'terminal': logger.error(customMessage); logger.error(error.message); logger.error(error.stack); break; + case 'loggly': + const json = { + type: 'Error', + message: error.message, + stack: isStack ? error.stack : null, + details: customMessage, + }; + winston.log('error', json); + logger.error(JSON.stringify(json)); + break; case 'graylog': default: - logger.error(JSON.stringify({ type: 'Error', + logger.error(JSON.stringify({ + type: 'Error', message: error.message, stack: isStack ? error.stack : null, - details: customMessage })); + details: customMessage, + })); break; } }; diff --git a/src/output/index.js b/src/output/index.js index 4005403..961424a 100644 --- a/src/output/index.js +++ b/src/output/index.js @@ -1,6 +1,7 @@ import logger from '../logger'; import grayLogFromat from './graylogFormat'; import terminalFormat from './terminalFormat'; +import logglyFormat from './logglyFormat'; export default (data, type = 'terminal') => { switch (type) { @@ -8,10 +9,14 @@ export default (data, type = 'terminal') => { logger.log('info', terminalFormat(data)); break; case 'graylog': - logger.log('info', JSON.stringify(grayLogFromat(data.transaction, - data.decodedInputDataResult, data.decodedLogs))); + logger.log('info', grayLogFromat(data.transaction, + data.decodedInputDataResult, data.decodedLogs)); + break; + case 'loggly': + logger.log('info', logglyFormat(data.transaction, + data.decodedInputDataResult, data.decodedLogs)); break; default: - throw new Error(`${type} output module is undefind`); + throw new Error(`${type} output module is undefined`); } }; diff --git a/src/output/logglyFormat.js b/src/output/logglyFormat.js new file mode 100644 index 0000000..b22dc90 --- /dev/null +++ b/src/output/logglyFormat.js @@ -0,0 +1,35 @@ +const formatLogs = (logs) => { + if (!logs) return []; + + return logs.map((log) => { + let eventText = `${log.name}(`; + log.events.forEach((i, idx, events) => { + eventText += `${events[idx].name}=${events[idx].value}`; + if (idx !== events.length - 1) { + eventText += ','; + } + }); + eventText += ')'; + return eventText; + }); +}; +export default (transaction, decodedTransaction, decodedLogs) => ({ + networkId: transaction.networkId, + blockHash: transaction.blockHash, + blockNumber: transaction.blockNumber, + fromAddress: transaction.from, + toAddress: transaction.to, + transactionHash: transaction.hash, + input: transaction.creates ? + decodedTransaction.constructorData : transaction.input, + gas: transaction.gas, + gasPrice: transaction.gasPrice, + status: transaction.status, + value: transaction.value, + transactionType: transaction.contractAddress ? 'Contract Creation' : 'Transaction', + contractAddress: transaction.contractAddress, + methodName: decodedTransaction.name, + methodParameters: decodedTransaction.params, + etherscanLink: `https://etherscan.io/tx/${transaction.hash}`, + events: formatLogs(decodedLogs), +}); diff --git a/yarn.lock b/yarn.lock index dee130c..199d479 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2242,7 +2242,7 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stringify-safe@5.0.x, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -2515,6 +2515,10 @@ mocha@^3.5.3: mkdirp "0.5.1" supports-color "3.1.2" +moment@^2.18.1: + version "2.19.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.4.tgz#17e5e2c6ead8819c8ecfad83a0acccb312e94682" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2552,6 +2556,14 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" +node-loggly-bulk@^2.0.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-loggly-bulk/-/node-loggly-bulk-2.2.1.tgz#c4ee8b0e3c334ecbb744e321f5ffe73f0c3a88d5" + dependencies: + json-stringify-safe "5.0.x" + moment "^2.18.1" + request ">=2.76.0 <3.0.0" + node-pre-gyp@^0.6.36: version "0.6.37" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.37.tgz#3c872b236b2e266e4140578fe1ee88f693323a05" @@ -3055,6 +3067,33 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +"request@>=2.76.0 <3.0.0": + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + request@^2.81.0, request@^2.82.0: version "2.82.0" resolved "https://registry.yarnpkg.com/request/-/request-2.82.0.tgz#2ba8a92cd7ac45660ea2b10a53ae67cd247516ea" @@ -3463,7 +3502,7 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" -tough-cookie@>=2.3.3, tough-cookie@~2.3.0: +tough-cookie@>=2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" dependencies: @@ -3603,9 +3642,16 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -winston@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-2.3.1.tgz#0b48420d978c01804cf0230b648861598225a119" +winston-loggly-bulk@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/winston-loggly-bulk/-/winston-loggly-bulk-2.0.1.tgz#9110858ff51efccae94159355b5519c72fefd8b7" + dependencies: + node-loggly-bulk "^2.0.1" + winston "^2.3.1" + +winston@^2.3.1, winston@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.0.tgz#808050b93d52661ed9fb6c26b3f0c826708b0aee" dependencies: async "~1.0.0" colors "1.0.x"