├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── .npmrc ├── .nvmrc ├── .nycrc ├── .travis.yml ├── API.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── client.js ├── node.js └── util │ ├── client │ ├── consoleLogger.js │ ├── logentriesLogger.js │ └── rollbarLogger.js │ ├── common │ ├── config.js │ ├── logForLevel.js │ ├── rollbar.js │ └── scrub.js │ └── server │ ├── logentriesLogger.js │ ├── requestLogger.js │ └── rollbarLogger.js ├── test ├── .eslintrc ├── benchmark │ └── index.js ├── browser.index.js ├── karma.conf.js ├── leakage │ └── check.js ├── runner.js ├── saucelabs-browsers.json ├── specs │ ├── logger.spec.js │ ├── requestLogger.spec.js │ └── util │ │ ├── logForLevel.spec.js │ │ └── scrub.spec.js ├── stress │ └── index.js ├── testLogger.js └── webpack.client.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015", 4 | "stage-0" 5 | ], 6 | plugins: [ 7 | ], 8 | "env": { 9 | "test": { 10 | "plugins": [ [ "istanbul", { "exclude": "test/**/*" } ] ] 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "wework", 5 | "wework/imports" 6 | ], 7 | "env": { 8 | "commonjs": true, 9 | "node": true 10 | }, 11 | "globals" : { 12 | }, 13 | "settings": { 14 | "import/resolver": "node" 15 | }, 16 | "parserOptions": { 17 | "ecmaVersion": 6, 18 | "sourceType": "module", 19 | "ecmaFeatures": { 20 | "experimentalObjectRestSpread" : true 21 | } 22 | }, 23 | "rules": { 24 | # ensure JSDoc comments are valid 25 | "valid-jsdoc": [1, { 26 | "requireReturn": false, 27 | requireParamDescription: false, 28 | requireReturnDescription: false 29 | }] 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | 3 | lib-cov 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | 12 | pids 13 | logs 14 | results 15 | 16 | npm-debug.log 17 | node_modules 18 | coverage 19 | .nyc_output 20 | .DS_Store 21 | 22 | # node-waf configuration 23 | .lock-wscript 24 | 25 | # Compiled binary addons (http://nodejs.org/api/addons.html) 26 | build/Release 27 | 28 | # Dependency directories 29 | node_modules 30 | 31 | # Optional npm cache directory 32 | .npm 33 | 34 | # Optional REPL history 35 | .node_repl_history 36 | 37 | # ESLint Cache 38 | .eslintcache 39 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Don't publish anything to npm but package.json, the dist 2 | # directory, API.md, CHANGELOG.md 3 | 4 | !dist/* 5 | !API.md 6 | !CHANGELOG.md 7 | !package.json -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | loglevel=error 2 | save-exact=true -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.9.0 -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": [ 3 | "html", 4 | "lcov", 5 | "cobertura", 6 | "text-summary" 7 | ], 8 | "report-dir": "./coverage/node", 9 | "include": [ 10 | "src/**/*.js" 11 | ], 12 | "require": [ 13 | "babel-register" 14 | ], 15 | "sourceMap": false, 16 | "instrument": false, 17 | "show-process-tree": true 18 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.*" 4 | 5 | # https://github.com/andywer/leakage#travis-ci 6 | addons: 7 | apt: 8 | sources: 9 | - ubuntu-toolchain-r-test 10 | packages: 11 | - g++-4.8 12 | before_install: 13 | - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi 14 | 15 | branches: 16 | only: 17 | - master 18 | - /^greenkeeper-.*$/ 19 | 20 | before_script: 21 | - "npm run dist" 22 | 23 | script: 24 | - "npm run test:ci" 25 | 26 | after_success: 27 | - SAUCELABS=true npm run test:browser 28 | 29 | cache: 30 | directories: 31 | - node_modules 32 | 33 | notifications: 34 | email: false 35 | 36 | sudo: false 37 | -------------------------------------------------------------------------------- /API.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Table of Contents 4 | 5 | - [ClientLogger](#clientlogger) 6 | - [ClientConsoleLogger](#clientconsolelogger) 7 | - [write](#write) 8 | - [ClientLogentriesLogger](#clientlogentrieslogger) 9 | - [write](#write-1) 10 | - [ClientRollbarLogger](#clientrollbarlogger) 11 | - [write](#write-2) 12 | - [NodeLogger](#nodelogger) 13 | - [BUNYAN_CONFIG_FIELDS](#bunyan_config_fields) 14 | - [DEFAULT_ROOT_FIELDS](#default_root_fields) 15 | - [BUNYAN_LOGGER_LEVELS](#bunyan_logger_levels) 16 | - [DEFAULT_CONFIG](#default_config) 17 | - [assembleConfig](#assembleconfig) 18 | - [toBunyanConfig](#tobunyanconfig) 19 | - [logForLevel](#logforlevel) 20 | - [logIt](#logit) 21 | - [ServerRollbarLogger](#serverrollbarlogger) 22 | - [write](#write-3) 23 | - [bunyanToRollbarLevelMap](#bunyantorollbarlevelmap) 24 | - [bunyanLevelToRollbarLevelName](#bunyanleveltorollbarlevelname) 25 | - [ServerLogentriesLogger](#serverlogentrieslogger) 26 | - [createRequestLogger](#createrequestlogger) 27 | - [requestLoggerMiddleware](#requestloggermiddleware) 28 | 29 | ## ClientLogger 30 | 31 | [src/client.js:80-86](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/client.js#L80-L86 "Source code on GitHub") 32 | 33 | A logger than can be used in browsers 34 | 35 | **Parameters** 36 | 37 | - `config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** we-js-logger config (optional, default `{}`) 38 | - `logger` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** an instance of a `bunyan` logger to use internally. 39 | this is meant to be used by the `child` method. 40 | 41 | ## ClientConsoleLogger 42 | 43 | [src/util/client/consoleLogger.js:9-9](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/consoleLogger.js#L9-L9 "Source code on GitHub") 44 | 45 | Pretty logging to `console` for client applications 46 | 47 | ### write 48 | 49 | [src/util/client/consoleLogger.js:16-48](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/consoleLogger.js#L16-L48 "Source code on GitHub") 50 | 51 | Transport to `console` 52 | 53 | **Parameters** 54 | 55 | - `data` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{}`) 56 | 57 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 58 | 59 | ## ClientLogentriesLogger 60 | 61 | [src/util/client/logentriesLogger.js:10-23](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/logentriesLogger.js#L10-L23 "Source code on GitHub") 62 | 63 | Custom bunyan stream that transports to Logentries from client applications 64 | 65 | **Parameters** 66 | 67 | - `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 68 | - `$0.name` 69 | - `$0.token` 70 | 71 | ### write 72 | 73 | [src/util/client/logentriesLogger.js:30-37](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/logentriesLogger.js#L30-L37 "Source code on GitHub") 74 | 75 | Transport logs to Logentries 76 | 77 | **Parameters** 78 | 79 | - `data` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{}`) 80 | 81 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 82 | 83 | ## ClientRollbarLogger 84 | 85 | [src/util/client/rollbarLogger.js:21-37](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/rollbarLogger.js#L21-L37 "Source code on GitHub") 86 | 87 | Custom rollbar stream that transports to logentries from a browser 88 | Wortks with a global Rollbar instance that is already initialized. 89 | Note this expects rollbar to be loaded in the head, not via an npm module. 90 | See for details on 91 | integrating Rollbar in client apps 92 | 93 | **Parameters** 94 | 95 | - `$0` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 96 | - `$0.token` 97 | - `$0.environment` 98 | - `$0.codeVersion` 99 | 100 | ### write 101 | 102 | [src/util/client/rollbarLogger.js:44-56](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/client/rollbarLogger.js#L44-L56 "Source code on GitHub") 103 | 104 | Transport logs to Rollbar 105 | 106 | **Parameters** 107 | 108 | - `data` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{}`) 109 | 110 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 111 | 112 | ## NodeLogger 113 | 114 | [src/node.js:68-82](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/node.js#L68-L82 "Source code on GitHub") 115 | 116 | A logger than can be used in node processes 117 | 118 | **Parameters** 119 | 120 | - `config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** we-js-logger config (optional, default `{}`) 121 | - `logger` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)?** an instance of a `bunyan` logger to use internally. 122 | this is meant to be used by the `child` method. 123 | 124 | ## BUNYAN_CONFIG_FIELDS 125 | 126 | [src/util/common/config.js:9-14](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L9-L14 "Source code on GitHub") 127 | 128 | Config keys that should always be passed to 129 | `bunyan.createLogger` 130 | 131 | ## DEFAULT_ROOT_FIELDS 132 | 133 | [src/util/common/config.js:22-26](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L22-L26 "Source code on GitHub") 134 | 135 | Whitelist of extra config keys that should be 136 | passed to `bunyan.createLogger` to form 137 | root logger fields. 138 | 139 | ## BUNYAN_LOGGER_LEVELS 140 | 141 | [src/util/common/config.js:33-33](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L33-L33 "Source code on GitHub") 142 | 143 | Array of bunyan's different log levels. 144 | 145 | 146 | ## DEFAULT_CONFIG 147 | 148 | [src/util/common/config.js:36-48](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L36-L48 "Source code on GitHub") 149 | 150 | ## assembleConfig 151 | 152 | [src/util/common/config.js:61-68](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L61-L68 "Source code on GitHub") 153 | 154 | Merges config with DEFAULT_CONFIG, and appends passed in streams 155 | with pre-configured streams for the runtime. 156 | 157 | This is used to configure this library, not bunyan as it has a lot of 158 | extra information. See `toBunyanConfig` below. 159 | 160 | **Parameters** 161 | 162 | - `config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 163 | - `getStreamsForRuntime` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** returns appended config.streams 164 | 165 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** runtimeConfig 166 | 167 | ## toBunyanConfig 168 | 169 | [src/util/common/config.js:79-81](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/config.js#L79-L81 "Source code on GitHub") 170 | 171 | Create a config objct for bunyan from a full `we-js-logger` config object. 172 | Extra keys passed to `bunyan.createLogger` become root logger fields, pass 173 | a custom `config.rootFields` to control this behavior 174 | 175 | **Parameters** 176 | 177 | - `config` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 178 | - `config.rootFields` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)>** extra fields to pass to bunyan 179 | 180 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** config for bunyan.createLogger 181 | 182 | ## logForLevel 183 | 184 | [src/util/common/logForLevel.js:10-22](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/logForLevel.js#L10-L22 "Source code on GitHub") 185 | 186 | Creates a log method for a particular level 187 | 188 | **Parameters** 189 | 190 | - `level` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** 191 | 192 | Returns **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** 193 | 194 | ## logIt 195 | 196 | [src/util/common/logForLevel.js:19-21](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/logForLevel.js#L19-L21 "Source code on GitHub") 197 | 198 | Log at a level. 199 | Must be bound to a logger instance. 200 | 201 | **Parameters** 202 | 203 | - `args` **any** 204 | 205 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 206 | 207 | ## ServerRollbarLogger 208 | 209 | [src/util/server/rollbarLogger.js:12-21](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/server/rollbarLogger.js#L12-L21 "Source code on GitHub") 210 | 211 | Custom bunyan stream that transports to Rollbar from a node process. 212 | See for integration details 213 | 214 | **Parameters** 215 | 216 | - `token` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** , codeVersion, and environment 217 | - `token.token` 218 | - `token.codeVersion` 219 | - `token.environment` 220 | 221 | ### write 222 | 223 | [src/util/server/rollbarLogger.js:31-44](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/server/rollbarLogger.js#L31-L44 "Source code on GitHub") 224 | 225 | handles `err` and `req` properties, attaches any custom data, 226 | and calls the appropriate Rollbar method. 227 | 228 | **Parameters** 229 | 230 | - `data` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{}`) 231 | 232 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 233 | 234 | ## bunyanToRollbarLevelMap 235 | 236 | [src/util/common/rollbar.js:11-18](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/rollbar.js#L11-L18 "Source code on GitHub") 237 | 238 | Map of bunyan log levels to Rollbar levels 239 | 240 | 241 | 242 | ## bunyanLevelToRollbarLevelName 243 | 244 | [src/util/common/rollbar.js:25-28](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/common/rollbar.js#L25-L28 "Source code on GitHub") 245 | 246 | Convert bunyan log level to rollbar level. Defaults to 'error'. 247 | 248 | **Parameters** 249 | 250 | - `level` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** bunyan log level (optional, default `bunyan.ERROR`) 251 | 252 | Returns **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Rollbar log level 253 | 254 | ## ServerLogentriesLogger 255 | 256 | [src/util/server/logentriesLogger.js:10-20](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/server/logentriesLogger.js#L10-L20 "Source code on GitHub") 257 | 258 | Custom bunyan stream that transports to logentries from a node process 259 | 260 | **Parameters** 261 | 262 | - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 263 | - `options.token` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** 264 | - `options.level` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** 265 | - `options.name` 266 | 267 | Returns **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** bunyan stream config 268 | 269 | ## createRequestLogger 270 | 271 | [src/util/server/requestLogger.js:12-48](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/server/requestLogger.js#L12-L48 "Source code on GitHub") 272 | 273 | Create a request loging express middleware 274 | 275 | **Parameters** 276 | 277 | - `logger` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** a logger instance 278 | - `options` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** (optional, default `{}`) 279 | - `options.reqIdHeader` **[String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)?** (optional, default `DEFAULT_HEADER_NAME`) 280 | 281 | Returns **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** 282 | 283 | ## requestLoggerMiddleware 284 | 285 | [src/util/server/requestLogger.js:24-47](https://github.com/wework/we-js-logger/blob/40092326eb67b7e4226500081ec51bc9e64b0277/src/util/server/requestLogger.js#L24-L47 "Source code on GitHub") 286 | 287 | Request Logger Middleware 288 | Adds base logging to every request 289 | Attaches a `log` child to each request object 290 | 291 | **Parameters** 292 | 293 | - `req` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 294 | - `res` **[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** 295 | - `next` **[Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function)** 296 | 297 | Returns **[undefined](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined)** 298 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | Generated by [auto-changelog](https://github.com/CookPete/auto-changelog). 5 | 6 | #### [v2.0.2](https://github.com/wework/we-js-logger/compare/v2.0.1...v2.0.2) 7 | > 6 July 2018 8 | - Adds javascript to root fields of the logger [`#159`](https://github.com/wework/we-js-logger/pulls/159) 9 | 10 | #### [v2.0.1](https://github.com/wework/we-js-logger/compare/v2.0.0...v2.0.1) 11 | > 5 July 2018 12 | - Exposes rollbar for the node logger [`#158`](https://github.com/wework/we-js-logger/pulls/158) 13 | - Updates lodash dependency [`aea217c`](https://github.com/wework/we-js-logger/commit/aea217c9a813a50380897951ed383d249b4d3c27) 14 | 15 | #### [v2.0.0](https://github.com/wework/we-js-logger/compare/v2.0.0-5...v2.0.0) 16 | > 7 November 2017 17 | 18 | #### [v2.0.0-5](https://github.com/wework/we-js-logger/compare/v2.0.0-4...v2.0.0-5) 19 | > 7 November 2017 20 | - Expose bunyanStdSerializers on Logger instances [`6cbf258`](https://github.com/wework/we-js-logger/commit/6cbf2586bf2f35bc49af4f92aa0dc47142ffb2ae) 21 | 22 | #### [v2.0.0-4](https://github.com/wework/we-js-logger/compare/v2.0.0-3...v2.0.0-4) 23 | > 7 November 2017 24 | - Cleanup rollbar args [`1ea3b8f`](https://github.com/wework/we-js-logger/commit/1ea3b8f13522e752ca9c63c5b079be78a5f3b785) 25 | 26 | #### [v2.0.0-3](https://github.com/wework/we-js-logger/compare/v2.0.0-2...v2.0.0-3) 27 | > 7 November 2017 28 | - Switch from ghooks to husky [`0269709`](https://github.com/wework/we-js-logger/commit/026970981c04d6e7c9a78f8892df6e9ed7b3158c) 29 | - Give rollbar loggers the correct context [`1ccf8a2`](https://github.com/wework/we-js-logger/commit/1ccf8a27d5c607f65ce6c4f96869a8fefe6f3395) 30 | 31 | #### [v2.0.0-2](https://github.com/wework/we-js-logger/compare/v2.0.0-1...v2.0.0-2) 32 | > 7 November 2017 33 | - Fixup server rollbar logger [`7fe98f9`](https://github.com/wework/we-js-logger/commit/7fe98f9603cf28394d9fed4d8aeaa92aaef7e9e1) 34 | - Fixup server rollbar logger [`825a941`](https://github.com/wework/we-js-logger/commit/825a941ba2da4238063e804b86266b3894f44b88) 35 | 36 | #### [v2.0.0-1](https://github.com/wework/we-js-logger/compare/v2.0.0-0...v2.0.0-1) 37 | > 6 November 2017 38 | - Refactor [Client|Server]RollbarLogger to work with notifier API changes [`dd44c1c`](https://github.com/wework/we-js-logger/commit/dd44c1cb09909776bb364ba85478b28b17ff522c) 39 | 40 | #### [v2.0.0-0](https://github.com/wework/we-js-logger/compare/v1.0.3...v2.0.0-0) 41 | > 6 November 2017 42 | - Upgrade to Rollbar 2 [`#128`](https://github.com/wework/we-js-logger/pulls/128) 43 | - Update loadtest to the latest version 🚀 [`#79`](https://github.com/wework/we-js-logger/pulls/79) 44 | - Update karma-webpack to the latest version 🚀 [`#72`](https://github.com/wework/we-js-logger/pulls/72) 45 | - Tweak test commands [`1754b23`](https://github.com/wework/we-js-logger/commit/1754b231c775914ec93540cf75022709dec9b852) 46 | - upgrade to Rollbar 2.x [`efef298`](https://github.com/wework/we-js-logger/commit/efef2983b1e8937998c346eea57d5785651790e0) 47 | - Update yarn.lock [`e916f48`](https://github.com/wework/we-js-logger/commit/e916f487637660bacfafa7f81342005df3bd11ad) 48 | 49 | #### [v1.0.3](https://github.com/wework/we-js-logger/compare/v1.0.2...v1.0.3) 50 | > 9 February 2017 51 | - Switch to eslint-config-wework [`#78`](https://github.com/wework/we-js-logger/pulls/78) 52 | - Update express to the latest version 🚀 [`#73`](https://github.com/wework/we-js-logger/pulls/73) 53 | - Update karma to the latest version 🚀 [`#74`](https://github.com/wework/we-js-logger/pulls/74) 54 | - Update coveralls to the latest version 🚀 [`#76`](https://github.com/wework/we-js-logger/pulls/76) 55 | - Safe access to console for oldIE [`#77`](https://github.com/wework/we-js-logger/pulls/77) 56 | - Style updates to adhere to eslint-config-wework [`c059822`](https://github.com/wework/we-js-logger/commit/c05982237f3429f103ef4281e2457aed4bc052f0) 57 | - Migrate to eslint-config-wework [`68ca93e`](https://github.com/wework/we-js-logger/commit/68ca93e6e8fa52678213048ffd76908e2c11071f) 58 | - Lint after bundle so relative imports of self work [`8f375c6`](https://github.com/wework/we-js-logger/commit/8f375c612bd0248991713ed263120a5e045c4c6a) 59 | 60 | #### [v1.0.2](https://github.com/wework/we-js-logger/compare/v1.0.1...v1.0.2) 61 | > 24 January 2017 62 | - Prefer serializers in context to hide-secrets [`#70`](https://github.com/wework/we-js-logger/pulls/70) 63 | 64 | #### [v1.0.1](https://github.com/wework/we-js-logger/compare/v1.0.0...v1.0.1) 65 | > 24 January 2017 66 | - Pass name to logentries client and only init once [`#69`](https://github.com/wework/we-js-logger/pulls/69) 67 | 68 | #### [v1.0.0](https://github.com/wework/we-js-logger/compare/v0.5.1...v1.0.0) 69 | > 24 January 2017 70 | - Update babel-plugin-external-helpers to the latest version 🚀 [`#64`](https://github.com/wework/we-js-logger/pulls/64) 71 | - Update babel-preset-es2015 to the latest version 🚀 [`#65`](https://github.com/wework/we-js-logger/pulls/65) 72 | - Update karma-mocha-reporter to the latest version 🚀 [`#66`](https://github.com/wework/we-js-logger/pulls/66) 73 | - (perf) Refactor logger class to encapsulate bunyan instance; add requestLogger stress test [`#68`](https://github.com/wework/we-js-logger/pulls/68) 74 | - Add benchmark vs naked bunyan [`#59`](https://github.com/wework/we-js-logger/pulls/59) 75 | - Logger wraps bunyan instance instead of pretending to be it, perf optimizations for scrub fields [`70fbf00`](https://github.com/wework/we-js-logger/commit/70fbf00c0b215bb6f33d11a788904adb942bf003) 76 | - Update deps and fixup tests [`98cdea5`](https://github.com/wework/we-js-logger/commit/98cdea565600354bb1c9c3666d33857580100dbd) 77 | - Load test request logger [`dea0cbe`](https://github.com/wework/we-js-logger/commit/dea0cbeb777befe2e0d89c97b49811bdf4782d1e) 78 | 79 | #### [v0.5.1](https://github.com/wework/we-js-logger/compare/v0.5.0...v0.5.1) 80 | > 10 January 2017 81 | - add garbage collection tests, and fix scrubFields [`#57`](https://github.com/wework/we-js-logger/pulls/57) 82 | - Remove nyc from leakage target [`47cde2a`](https://github.com/wework/we-js-logger/commit/47cde2a9a1e87b3775ea245d0d102f1911aab68a) 83 | 84 | #### [v0.5.0](https://github.com/wework/we-js-logger/compare/v0.4.0...v0.5.0) 85 | > 6 January 2017 86 | - make config.scrubFields work globally, not just for Rollbar [`#53`](https://github.com/wework/we-js-logger/pulls/53) 87 | - Update babel-plugin-istanbul to the latest version 🚀 [`#50`](https://github.com/wework/we-js-logger/pulls/50) 88 | - Update babel-plugin-istanbul to the latest version 🚀 [`#49`](https://github.com/wework/we-js-logger/pulls/49) 89 | - Update dependencies to enable Greenkeeper 🌴 [`#48`](https://github.com/wework/we-js-logger/pulls/48) 90 | - update tests and use bunyan constant [`87687fc`](https://github.com/wework/we-js-logger/commit/87687fc8306f1b342c64fb249c1e6ffa58760dd6) 91 | - chore(package): update dependencies [`a7ccabb`](https://github.com/wework/we-js-logger/commit/a7ccabb95ac42934ffed8d5667872751dbc52145) 92 | - chore(package): update babel-plugin-istanbul to version 3.1.2 [`a5b0b75`](https://github.com/wework/we-js-logger/commit/a5b0b75da37fe58cc3f64ef36319f6105b4c4512) 93 | 94 | #### [v0.4.0](https://github.com/wework/we-js-logger/compare/v0.3.0...v0.4.0) 95 | > 3 January 2017 96 | - Add support for Rollbar scrubFields option [`#47`](https://github.com/wework/we-js-logger/pulls/47) 97 | - Update mocha to version 3.2.0 🚀 [`#43`](https://github.com/wework/we-js-logger/pulls/43) 98 | - Update rollup to version 0.36.4 🚀 [`#42`](https://github.com/wework/we-js-logger/pulls/42) 99 | - Update nyc to version 10.0.0 🚀 [`#41`](https://github.com/wework/we-js-logger/pulls/41) 100 | - Update uuid to version 3.0.0 🚀 [`#38`](https://github.com/wework/we-js-logger/pulls/38) 101 | - Update karma-mocha-reporter to version 2.2.1 🚀 [`#39`](https://github.com/wework/we-js-logger/pulls/39) 102 | - lodash@4.17.1 breaks build 🚨 [`#36`](https://github.com/wework/we-js-logger/pulls/36) 103 | - Update nyc to version 9.0.1 🚀 [`#35`](https://github.com/wework/we-js-logger/pulls/35) 104 | - Update np to version 2.10.1 🚀 [`#33`](https://github.com/wework/we-js-logger/pulls/33) 105 | - Update eslint-plugin-import to version 2.2.0 🚀 [`#28`](https://github.com/wework/we-js-logger/pulls/28) 106 | - Update karma-mocha to version 1.3.0 🚀 [`#30`](https://github.com/wework/we-js-logger/pulls/30) 107 | - Update coveralls to version 2.11.15 🚀 [`#31`](https://github.com/wework/we-js-logger/pulls/31) 108 | - add support for Rollbar scrubFields option [`202d968`](https://github.com/wework/we-js-logger/commit/202d9686cc13580539da64579f8c2cbe79eb299e) 109 | - chore(package): update coveralls to version 2.11.15 [`a28563b`](https://github.com/wework/we-js-logger/commit/a28563b4459e113024de4d6b8f5d9d18edbfb7d7) 110 | - chore(package): update rollup to version 0.36.4 [`0b16484`](https://github.com/wework/we-js-logger/commit/0b16484f2599b4563b5a6439ef954caf9e349bfe) 111 | 112 | #### [v0.3.0](https://github.com/wework/we-js-logger/compare/v0.2.1...v0.3.0) 113 | > 9 November 2016 114 | - Use fork of le_js to address oldIE issue [`#29`](https://github.com/wework/we-js-logger/pulls/29) 115 | - Update np to version 2.10.0 🚀 [`#27`](https://github.com/wework/we-js-logger/pulls/27) 116 | - Update babel-core to version 6.18.2 🚀 [`#25`](https://github.com/wework/we-js-logger/pulls/25) 117 | - Update eslint to version 3.9.1 🚀 [`#23`](https://github.com/wework/we-js-logger/pulls/23) 118 | - Update babel-loader to version 6.2.7 🚀 [`#21`](https://github.com/wework/we-js-logger/pulls/21) 119 | - Update webpack to version 1.13.3 🚀 [`#19`](https://github.com/wework/we-js-logger/pulls/19) 120 | - Use consolidated saucelabs account [`#16`](https://github.com/wework/we-js-logger/pulls/16) 121 | - Update test webpack config with recommended config from bunyan [`#15`](https://github.com/wework/we-js-logger/pulls/15) 122 | - Update all dependencies 🌴 [`#13`](https://github.com/wework/we-js-logger/pulls/13) 123 | - Witelist greenkeeper branches for travis [`#14`](https://github.com/wework/we-js-logger/pulls/14) 124 | - chore(package): update dependencies [`7206756`](https://github.com/wework/we-js-logger/commit/7206756ea0dc0d86e42911900940b0d771f52d70) 125 | - chore(package): update np to version 2.10.0 [`ff5973d`](https://github.com/wework/we-js-logger/commit/ff5973dd48ff5626c98034404f498337fe594787) 126 | - chore(package): update babel-core to version 6.18.2 [`522f8fb`](https://github.com/wework/we-js-logger/commit/522f8fbd0261bac69c1169dcd914bfb053eae369) 127 | 128 | #### [v0.2.1](https://github.com/wework/we-js-logger/compare/v0.2.0...v0.2.1) 129 | > 23 October 2016 130 | - Rename server request logger request id header name option and make the config object optional [`58d5819`](https://github.com/wework/we-js-logger/commit/58d581984caa03c468f4b5d05d7e2a61cc66374b) 131 | 132 | #### [v0.2.0](https://github.com/wework/we-js-logger/compare/v0.1.0...v0.2.0) 133 | > 23 October 2016 134 | - Fix config and be explicit about which fields are passed to bunyan [`f3e1a96`](https://github.com/wework/we-js-logger/commit/f3e1a9681b0dab3292c89b3e6f419dc0ce5d2599) 135 | 136 | #### [v0.1.0](https://github.com/wework/we-js-logger/compare/v0.0.9...v0.1.0) 137 | > 23 October 2016 138 | - Git hook config [`#11`](https://github.com/wework/we-js-logger/pulls/11) 139 | - Add server tools [`#10`](https://github.com/wework/we-js-logger/pulls/10) 140 | - Refactor NodeLogger to expose server-only tools [`fbda3e1`](https://github.com/wework/we-js-logger/commit/fbda3e1f528488061d774fa9a128d08f369ac492) 141 | - Configure git hooks, linter config, and cleanup linter errors [`29d0484`](https://github.com/wework/we-js-logger/commit/29d048448c3c837486fab52cecc6661a56f7d165) 142 | - Enhance build tooling; add a tdd target [`ed7f70c`](https://github.com/wework/we-js-logger/commit/ed7f70c8f79cc86b4fa4a839428518ef21110032) 143 | 144 | #### [v0.0.9](https://github.com/wework/we-js-logger/compare/v0.0.8...v0.0.9) 145 | > 19 October 2016 146 | - Add missing import statement [`7df4225`](https://github.com/wework/we-js-logger/commit/7df42251c1e07f9c7cbdd6ab5f9634a1dfcfeed3) 147 | 148 | #### [v0.0.8](https://github.com/wework/we-js-logger/compare/v0.0.7...v0.0.8) 149 | > 19 October 2016 150 | - better integration with rollbar browser snippet [`ac654a8`](https://github.com/wework/we-js-logger/commit/ac654a851709a8d26ad8956393728687ec3f7951) 151 | - name stdout transports [`fc28af2`](https://github.com/wework/we-js-logger/commit/fc28af21e5197e73de124fbc86e6e612270b8f18) 152 | 153 | #### [v0.0.7](https://github.com/wework/we-js-logger/compare/v0.0.6...v0.0.7) 154 | > 19 October 2016 155 | - client rollbar integration fixes [`1c96e32`](https://github.com/wework/we-js-logger/commit/1c96e32cf27d1f4f970d726d5dd7b0197ed13d83) 156 | 157 | #### [v0.0.6](https://github.com/wework/we-js-logger/compare/v0.0.5...v0.0.6) 158 | > 19 October 2016 159 | - use _.get directly [`ff5ed72`](https://github.com/wework/we-js-logger/commit/ff5ed722e99156387e74178b911f9452345c4a6f) 160 | 161 | #### [v0.0.5](https://github.com/wework/we-js-logger/compare/v0.0.4...v0.0.5) 162 | > 19 October 2016 163 | - generate docs [`ae23546`](https://github.com/wework/we-js-logger/commit/ae2354626e00f65aa5feb15e5da0217f4d826179) 164 | - Fix Rollbar config check [`6c634cb`](https://github.com/wework/we-js-logger/commit/6c634cb848e8d1f992ad93f0e1f091e3fc7c8a79) 165 | 166 | #### [v0.0.4](https://github.com/wework/we-js-logger/compare/v0.0.3...v0.0.4) 167 | > 19 October 2016 168 | - Update docs [`1d4106d`](https://github.com/wework/we-js-logger/commit/1d4106d13e9597a55513ec44bf914e5b4108f980) 169 | 170 | #### [v0.0.3](https://github.com/wework/we-js-logger/compare/v0.0.2...v0.0.3) 171 | > 18 October 2016 172 | - Pass rollbar token to ClientRollbarLogger [`3c7793e`](https://github.com/wework/we-js-logger/commit/3c7793e08716fe44134c82f6f79bcb29b3e03364) 173 | 174 | #### [v0.0.2](https://github.com/wework/we-js-logger/compare/v0.0.2-1...v0.0.2) 175 | > 18 October 2016 176 | - Fix pkg [`#5`](https://github.com/wework/we-js-logger/pulls/5) 177 | - Add docs. Closes #3 [`#3`](https://github.com/wework/we-js-logger/issues/3) 178 | - Handle client rollbar init more gracefully [`fc81634`](https://github.com/wework/we-js-logger/commit/fc81634f76dcb6df1e904c1ee2f6ecab1e9f1bc1) 179 | 180 | #### [v0.0.2-1](https://github.com/wework/we-js-logger/compare/v0.0.2-0...v0.0.2-1) 181 | > 18 October 2016 182 | - Fixup npm scripts for dist [`14a04e7`](https://github.com/wework/we-js-logger/commit/14a04e7da7e060818ffa089cd70d5dc3cd835389) 183 | 184 | #### [v0.0.2-0](https://github.com/wework/we-js-logger/compare/v0.0.1...v0.0.2-0) 185 | > 18 October 2016 186 | - Use node v6.9.0 LTS for development and CI [`#4`](https://github.com/wework/we-js-logger/pulls/4) 187 | - Add before_script to travis build [`#1`](https://github.com/wework/we-js-logger/pulls/1) 188 | - Fix the package [`27676a6`](https://github.com/wework/we-js-logger/commit/27676a6f42a997acea25c318bf6dd775bab6b68e) 189 | 190 | #### v0.0.1 191 | > 18 October 2016 192 | - Initial commit [`3a23034`](https://github.com/wework/we-js-logger/commit/3a23034cfa419603ca14ab2e472d2a348d3bee06) 193 | 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 WeWork Projects 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | we-js-logger 2 | ==================== 3 | 4 | [![Build Status][travis-image]][travis-url] 5 | [![Coverage Status][coveralls-image]][coveralls-url] 6 | [![NPM version][npm-version-image]][npm-url] 7 | [![NPM downloads][npm-downloads-image]][npm-url] 8 | [![MIT License][license-image]][license-url] 9 | 10 | [![Sauce Test Status][saucelabs-image]][saucelabs-url] 11 | 12 | >Logger for node processes and browser applications with transports to Rollbar and Logentries 13 | 14 | # Introduction 15 | 16 | This is an opinionated logger for JS applications: 17 | 18 | - Uses [bunyan](https://github.com/trentm/node-bunyan), a JSON logger, under the hood 19 | - Transports logs to Logentries and/or Rollbar 20 | - Universal. Can be used in the browser and Node.js processes 21 | 22 | # Usage 23 | 24 | ```js 25 | import Logger from 'we-js-logger'; 26 | const log = new Logger({ 27 | name: 'my-logger', 28 | environment: 'production', 29 | level: 'debug', 30 | codeVersion: process.env.SHA_VERSION, 31 | logentriesToken: process.env.LOGENTRIES_TOKEN, 32 | rollbarToken: process.env.ROLLBAR_TOKEN, 33 | scrubFields: ['password'], // blacklist field keys being sent through logger 34 | }); 35 | ``` 36 | 37 | ## Node.js usage 38 | 39 | This package can be used via `npm` and `node` with no special considerations. 40 | 41 | ## Browser usage 42 | 43 | This package exposes a `client` build for browser usage. It is referenced in the `browser` field of `package.json`, so module loaders that follow this spec will load it easily. 44 | 45 | For example, we commonly use `webpack` to load this module. 46 | 47 | ### Webpack Considerations 48 | 49 | *TODO document webpack setup* 50 | 51 | ## Configuration 52 | 53 | See https://github.com/wework/we-js-logger/blob/master/API.md#we-js-loggerutillogger for API documentation 54 | 55 | ## Examples 56 | 57 | ```js 58 | log.fatal({ err }, 'Application crashing because something terrible happened.'); 59 | 60 | log.error({ err, req }, 'API request failed'); 61 | 62 | log.info({ action }, 'Something relevant happened') 63 | 64 | log.debug({ event, action }, 'Something useful for developers happened'); 65 | 66 | ``` 67 | 68 | See https://github.com/trentm/node-bunyan#log-method-api for more detail. 69 | 70 | # Logentries Integration 71 | 72 | *More docs coming soon.* 73 | 74 | Providing the `Logger` constructor a `logentriesToken` option enables this transport. 75 | 76 | # Rollbar Integration 77 | 78 | ## Node 79 | This library will initialize Rollbar 80 | See https://rollbar.com/docs/notifier/rollbar.js/#quick-start-server for documentation on setting up Rollbar for node processes. 81 | 82 | ## Browser 83 | For browser usage, this library expects Rollbar to be loaded via their quick-start script tag. This also allows Rollbar to capture any errors before the logger's initialization code, if that's important to you. 84 | 85 | See https://rollbar.com/docs/notifier/rollbar.js/#quick-start-browser for documentation on setting up Rollbar for browser applications 86 | 87 | # Development 88 | 89 | In lieu of a formal style guide, please ensure PRs follow the conventions present, and have been properly linted and tested. Feel free to open issues to discuss. 90 | 91 | Be aware this module is tested in both browser and node runtimes. 92 | 93 | ## Available tasks 94 | 95 | ### Build and test 96 | Runs all tests, static analysis, and bundle for distribution 97 | ```shell 98 | $ npm start 99 | ``` 100 | 101 | ### Test 102 | Runs browser and node tests 103 | ```shell 104 | $ npm test 105 | ``` 106 | 107 | Runs browser tests via PhantomJS only 108 | ```shell 109 | $ npm run test:browser 110 | ``` 111 | 112 | Runs browser tests via SauceLabs only 113 | ```shell 114 | $ SAUCELABS=true npm run test:browser 115 | ``` 116 | 117 | Runs node tests only 118 | ```shell 119 | $ npm run test:node 120 | ``` 121 | 122 | ### TDD 123 | Runs browser and node tests in watch mode, re-bundles on src file change 124 | ```shell 125 | $ npm run tdd 126 | ``` 127 | 128 | ### Docs 129 | Regenerate `API.md` docs from JSDoc comments 130 | ```shell 131 | $ npm run docs 132 | ``` 133 | 134 | ### Bundle 135 | Packages client and node bundles for distribution, output to `/dist` 136 | ```shell 137 | $ npm run bundle 138 | ``` 139 | 140 | ### Distribute 141 | Lints, cleans, bundles, and generates docs for distribution, output to `/dist` 142 | ```shell 143 | $ npm run dist 144 | ``` 145 | 146 | ### Release 147 | We're using `np` to simplify publishing to git + npm. A changelog and docs are generated as part of this script. 148 | 149 | ```shell 150 | $ npm run release 151 | $ npm run release patch # patch release 152 | $ npm run release 100.10.1 # release specific version 153 | ``` 154 | 155 | 156 | 157 | [npm-url]: https://npmjs.org/package/we-js-logger 158 | [npm-version-image]: http://img.shields.io/npm/v/we-js-logger.svg?style=flat-square 159 | [npm-downloads-image]: http://img.shields.io/npm/dm/we-js-logger.svg?style=flat-square 160 | 161 | [coveralls-image]:https://coveralls.io/repos/github/wework/we-js-logger/badge.svg?branch=master 162 | [coveralls-url]:https://coveralls.io/github/wework/we-js-logger?branch=master 163 | 164 | [travis-url]:https://travis-ci.org/wework/we-js-logger 165 | [travis-image]: https://travis-ci.org/wework/we-js-logger.svg?branch=master 166 | 167 | [saucelabs-image]:https://saucelabs.com/browser-matrix/wework-we-js-logger.svg 168 | [saucelabs-url]:https://saucelabs.com/u/wework-we-js-logger 169 | 170 | [license-url]: LICENSE 171 | [license-image]: http://img.shields.io/badge/license-MIT-000000.svg?style=flat-square 172 | 173 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "we-js-logger", 3 | "version": "2.0.2", 4 | "description": "A logger for Node and Browser JS with Rollbar and Logentries transports", 5 | "author": "WeWork Digital ", 6 | "contributors": [ 7 | "Mike Nason (https://github.com/nason)" 8 | ], 9 | "license": "MIT", 10 | "homepage": "https://github.com/wework/we-js-logger", 11 | "bugs": { 12 | "url": "https://github.com/wework/we-js-logger/issues" 13 | }, 14 | "files": [ 15 | "dist/", 16 | "API.md", 17 | "CHANGELOG.md" 18 | ], 19 | "main": "dist/node.js", 20 | "browser": "dist/client.js", 21 | "scripts": { 22 | "benchmark": "node test/benchmark", 23 | "test": "npm run test:browser && npm run test:node && npm run test:stress", 24 | "tdd": "npm-run-all --parallel test:*:tdd", 25 | "test:ci": "npm run test:browser && npm run test:node:ci && npm run test:leakage", 26 | "test:browser": "NODE_ENV=test karma start test/karma.conf.js", 27 | "test:browser:tdd": "npm run test:browser -- --auto-watch --no-single-run", 28 | "test:node": "NODE_ENV=test nyc mocha --exit test/runner test/specs/*.spec.js test/specs/**/*.spec.js", 29 | "test:node:tdd": "npm run test:node -- --cache --watch", 30 | "test:node:ci": "npm run test:node && nyc report --reporter=text-lcov | coveralls", 31 | "test:leakage": "NODE_ENV=test mocha --exit test/runner test/leakage/check.js", 32 | "test:stress": "NODE_ENV=test mocha --exit test/runner test/stress", 33 | "test:bundle:tdd": "npm-run-all -l --parallel 'bundle:* -- --watch'", 34 | "lint": "eslint --format ./node_modules/eslint-friendly-formatter --cache src test", 35 | "clean": "rimraf dist && mkdirp dist", 36 | "bundle": "npm-run-all -l --parallel bundle:node bundle:browser", 37 | "bundle:node": "rollup -c --environment RUNTIME_ENV:node", 38 | "bundle:browser": "rollup -c --environment RUNTIME_ENV:client", 39 | "start": "npm test && npm run dist", 40 | "dist": "npm run clean && npm run bundle && npm run lint && npm run docs", 41 | "docs": "documentation build src/client.js src/node.js --github --format md --output API.md", 42 | "security-scan": "nsp check --output summary --warn-only", 43 | "pretest": "npm run bundle", 44 | "preversion": "npm run dist; git add API.md", 45 | "version": "auto-changelog --template compact --package; git add CHANGELOG.md", 46 | "release": "np", 47 | "precommit": "npm run lint", 48 | "prepush": "npm run security-scan" 49 | }, 50 | "repository": { 51 | "type": "git", 52 | "url": "git@github.com:wework/we-js-logger.git" 53 | }, 54 | "engines": { 55 | "node": ">=6.0" 56 | }, 57 | "keywords": [ 58 | "javascript", 59 | "es6", 60 | "es2015", 61 | "universal", 62 | "isomorphic", 63 | "logging", 64 | "rollbar", 65 | "logentries", 66 | "bunyan" 67 | ], 68 | "dependencies": { 69 | "bunyan": "^1.8.12", 70 | "bunyan-format": "^0.2.1", 71 | "console": "0.5.2", 72 | "hide-secrets": "1.1.0", 73 | "le_js": "git://github.com/nason/le_js.git#7a75be7c1dd2438e3f1183a68bd2162b80bc94d8", 74 | "le_node": "^1.7.0", 75 | "lodash": "^4.17.5", 76 | "rollbar": "^2.3.1", 77 | "uuid": "3.1.0" 78 | }, 79 | "devDependencies": { 80 | "auto-changelog": "1.0.2", 81 | "babel-core": "6.26.0", 82 | "babel-loader": "6.4.1", 83 | "babel-plugin-external-helpers": "6.22.0", 84 | "babel-plugin-istanbul": "4.1.5", 85 | "babel-plugin-transform-es2015-modules-umd": "6.24.1", 86 | "babel-polyfill": "6.26.0", 87 | "babel-preset-es2015": "6.24.1", 88 | "babel-preset-stage-0": "6.24.1", 89 | "babel-register": "6.26.0", 90 | "benchmark": "2.1.4", 91 | "chai": "3.5.0", 92 | "coveralls": "3.0.0", 93 | "documentation": "5.3.3", 94 | "eslint": "4.4.1", 95 | "eslint-config-wework": "3.0.0", 96 | "eslint-friendly-formatter": "2.0.7", 97 | "express": "4.16.2", 98 | "husky": "0.14.3", 99 | "imports-loader": "0.7.1", 100 | "json-loader": "0.5.7", 101 | "karma": "1.7.1", 102 | "karma-coverage": "1.1.1", 103 | "karma-mocha": "1.3.0", 104 | "karma-mocha-reporter": "2.2.5", 105 | "karma-phantomjs-launcher": "1.0.4", 106 | "karma-sauce-launcher": "1.2.0", 107 | "karma-sinon-chai": "1.3.3", 108 | "karma-sourcemap-loader": "0.3.7", 109 | "karma-webpack": "2.0.5", 110 | "leakage": "0.3.0", 111 | "loadtest": "2.3.0", 112 | "mkdirp": "0.5.1", 113 | "mocha": "4.0.1", 114 | "np": "2.16.1", 115 | "npm-run-all": "4.1.1", 116 | "nsp": "3.1.0", 117 | "nyc": "11.3.0", 118 | "phantomjs-prebuilt": "2.1.16", 119 | "rimraf": "2.6.2", 120 | "rollup": "0.50.0", 121 | "rollup-plugin-babel": "3.0.2", 122 | "rollup-plugin-cleanup": "2.0.0", 123 | "rollup-plugin-conditional": "1.1.1", 124 | "rollup-plugin-filesize": "1.4.2", 125 | "rollup-watch": "4.3.1", 126 | "semver": "5.4.1", 127 | "sinon": "1.17.7", 128 | "sinon-chai": "2.8.0", 129 | "webpack": "1.14.0" 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import babel from 'rollup-plugin-babel'; 2 | import filesize from 'rollup-plugin-filesize'; 3 | import conditional from 'rollup-plugin-conditional'; 4 | import cleanup from 'rollup-plugin-cleanup'; 5 | 6 | // Valid build targets 7 | const supportedRuntimes = [ 8 | 'client', 9 | 'node' 10 | ]; 11 | 12 | // NPM injects the name from `package.json` to this env var 13 | const pkgName = process.env.npm_package_name; 14 | 15 | // Pass to rollup via --environment flag 16 | const runtimeEnv = process.env.RUNTIME_ENV; 17 | 18 | if (!supportedRuntimes.includes(runtimeEnv)) { 19 | throw new Error( 20 | `Invalid runtime environment ${runtimeEnv} sepcified. Valid options: ${supportedRuntimes.join(', ')}` 21 | ); 22 | } 23 | 24 | export default { 25 | input: `src/${runtimeEnv}.js`, 26 | output: [ 27 | { 28 | file: `dist/${runtimeEnv}.js`, 29 | sourcemap: `dist/${runtimeEnv}.map.js`, 30 | format: 'cjs' 31 | } 32 | ], 33 | moduleId: pkgName, 34 | name: pkgName, 35 | external: [ 36 | 'bunyan', 37 | 'bunyan-format', 38 | 'hide-secrets', 39 | 'lodash/isFunction', 40 | 'lodash/isObject', 41 | 'lodash/mapValues', 42 | 'lodash/omit', 43 | 'lodash/pick', 44 | 'lodash/tail', 45 | 'rollbar', 46 | 47 | // Client-only 48 | 'console', 49 | 'le_js', 50 | 'lodash/get', 51 | 52 | // Server 53 | 'le_node', 54 | ], 55 | plugins: [ 56 | 57 | conditional( 58 | runtimeEnv === 'client', 59 | [ 60 | babel({ 61 | exclude: './node_modules/**', 62 | moduleIds: true, 63 | 64 | // Custom babelrc for build 65 | babelrc: false, 66 | presets: [ 67 | [ 'es2015', { 'modules': false } ], 68 | 'stage-0' 69 | ], 70 | plugins: [ 71 | 'external-helpers' 72 | ] 73 | }) 74 | ] 75 | ), 76 | 77 | cleanup({ 78 | comments: ['some', 'jsdoc'] 79 | }), 80 | 81 | filesize() 82 | ] 83 | }; 84 | -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | import bunyan, { stdSerializers } from 'bunyan'; 2 | 3 | // Safe console access for oldIE 4 | import console from 'console'; 5 | 6 | import logForLevel from './util/common/logForLevel'; 7 | import { assembleConfig, toBunyanConfig, BUNYAN_LOGGER_LEVELS } from './util/common/config'; 8 | 9 | import ClientConsoleLogger from './util/client/consoleLogger'; 10 | import ClientLogentriesLogger from './util/client/logentriesLogger'; 11 | import ClientRollbarLogger, { isGlobalRollbarConfigured } from './util/client/rollbarLogger'; 12 | 13 | /** 14 | * Add standard Client logger streams to `config.streams` 15 | * @private 16 | * @param {Object} config 17 | * @param {Array?} config.streams 18 | * @returns {Array} 19 | */ 20 | function getStreams(config) { 21 | // Any passed in streams 22 | const streams = Array.isArray(config.streams) 23 | ? [...config.streams] 24 | : []; 25 | 26 | // Nice console output 27 | if (config.stdout) { 28 | streams.push({ 29 | name: 'stdout', 30 | level: config.level, 31 | stream: new ClientConsoleLogger(), 32 | type: 'raw', 33 | }); 34 | } 35 | 36 | // Rollbar Transport 37 | // Messages at the warn level or higher are transported to Rollbar 38 | // Detects presence of global Rollbar and passed in token 39 | if (isGlobalRollbarConfigured()) { 40 | if (config.rollbarToken) { 41 | streams.push({ 42 | name: 'rollbar', 43 | level: 'warn', 44 | stream: new ClientRollbarLogger({ 45 | token: config.rollbarToken, 46 | environment: config.environment, 47 | codeVersion: config.codeVersion, 48 | }), 49 | type: 'raw', 50 | }); 51 | } 52 | } else { 53 | /* eslint-disable no-console */ 54 | console.warn('Client rollbar is not correctly configured'); 55 | /* eslint-enable */ 56 | } 57 | 58 | // Transport client logs 59 | if (config.logentriesToken) { 60 | streams.push({ 61 | name: 'logentries', 62 | level: config.level, 63 | stream: new ClientLogentriesLogger({ 64 | name: config.name, 65 | token: config.logentriesToken, 66 | }), 67 | type: 'raw', 68 | }); 69 | } 70 | 71 | return streams; 72 | } 73 | 74 | /** 75 | * A logger than can be used in browsers 76 | * @param {Object} config - we-js-logger config 77 | * @param {Object?} logger - an instance of a `bunyan` logger to use internally. 78 | * this is meant to be used by the `child` method. 79 | */ 80 | export default function ClientLogger(config = {}, logger) { 81 | const clientConfig = assembleConfig(config, getStreams); 82 | logger = logger || bunyan.createLogger(toBunyanConfig(clientConfig)); // eslint-disable-line no-param-reassign 83 | 84 | this._config = config; 85 | this._logger = logger; 86 | } 87 | 88 | /* eslint-disable prefer-spread, prefer-rest-params */ 89 | ClientLogger.prototype.child = function () { 90 | const childLogger = this._logger.child.apply(this._logger, arguments); 91 | return new ClientLogger(this._config, childLogger); 92 | }; 93 | /* eslint-enable */ 94 | 95 | // Dynamically hoist + wrap bunyan log instance methods (logger.info, logger.warn, etc) 96 | BUNYAN_LOGGER_LEVELS.forEach((level) => { 97 | ClientLogger.prototype[level] = logForLevel(level); 98 | }); 99 | 100 | // Expose bunyan stdSerializers 101 | ClientLogger.bunyanStdSerializers = stdSerializers; 102 | -------------------------------------------------------------------------------- /src/node.js: -------------------------------------------------------------------------------- 1 | import bunyan, { stdSerializers } from 'bunyan'; 2 | import Rollbar from 'rollbar'; 3 | import bunyanFormat from 'bunyan-format'; 4 | 5 | import { assembleConfig, toBunyanConfig, BUNYAN_LOGGER_LEVELS } from './util/common/config'; 6 | import logForLevel from './util/common/logForLevel'; 7 | 8 | import ServerRollbarLogger from './util/server/rollbarLogger'; 9 | import ServerLogentriesLogger from './util/server/logentriesLogger'; 10 | import createRequestLogger from './util/server/requestLogger'; 11 | 12 | /** 13 | * Add standard Node logger streams to `config.streams` 14 | * @private 15 | * @param {Object} config 16 | * @param {Array?} config.streams 17 | * @returns {Array} 18 | */ 19 | function getStreams(config) { 20 | const streams = Array.isArray(config.streams) 21 | ? [...config.streams] 22 | : []; 23 | 24 | // Nice output to stdout 25 | if (config.stdout) { 26 | streams.push({ 27 | name: 'stdout', 28 | level: config.level, 29 | stream: bunyanFormat({ outputMode: 'short' }), 30 | type: 'stream', 31 | }); 32 | } 33 | 34 | // Rollbar Transport 35 | // Messages at the warn level or higher are transported to Rollbar 36 | // Messages with an `err` and/or `req` data params are handled specially 37 | if (config.rollbarToken) { 38 | streams.push({ 39 | name: 'rollbar', 40 | level: 'warn', 41 | stream: new ServerRollbarLogger({ 42 | token: config.rollbarToken, 43 | environment: config.environment, 44 | codeVersion: config.codeVersion, 45 | }), 46 | type: 'raw', 47 | }); 48 | } 49 | 50 | // Transport server logs 51 | if (config.logentriesToken) { 52 | streams.push(new ServerLogentriesLogger({ 53 | name: config.name, 54 | token: config.logentriesToken, 55 | level: config.level, 56 | })); 57 | } 58 | 59 | return streams; 60 | } 61 | 62 | /** 63 | * A logger than can be used in node processes 64 | * @param {Object} config - we-js-logger config 65 | * @param {Object?} logger - an instance of a `bunyan` logger to use internally. 66 | * this is meant to be used by the `child` method. 67 | */ 68 | export default function NodeLogger(config = {}, logger) { 69 | const serverConfig = assembleConfig(config, getStreams); 70 | logger = logger || bunyan.createLogger(toBunyanConfig(serverConfig)); // eslint-disable-line no-param-reassign 71 | 72 | this._config = config; 73 | this._logger = logger; 74 | 75 | // Server-specific extras 76 | this.requestLogger = createRequestLogger(this, serverConfig); 77 | 78 | this.rollbar = new Rollbar({ 79 | accessToken: config.rollbarToken, 80 | }); 81 | this.rollbarErrorMiddleware = this.rollbar.errorHandler(); 82 | } 83 | 84 | /* eslint-disable prefer-spread, prefer-rest-params */ 85 | NodeLogger.prototype.child = function () { 86 | const childLogger = this._logger.child.apply(this._logger, arguments); 87 | return new NodeLogger(this._config, childLogger); 88 | }; 89 | /* eslint-enable */ 90 | 91 | // Dynamically hoist + wrap bunyan log instance methods (logger.info, logger.warn, etc) 92 | BUNYAN_LOGGER_LEVELS.forEach((level) => { 93 | NodeLogger.prototype[level] = logForLevel(level); 94 | }); 95 | 96 | // Expose bunyan stdSerializers 97 | NodeLogger.bunyanStdSerializers = stdSerializers; 98 | -------------------------------------------------------------------------------- /src/util/client/consoleLogger.js: -------------------------------------------------------------------------------- 1 | import bunyan from 'bunyan'; 2 | 3 | // Safe console access for oldIE 4 | import console from 'console'; 5 | 6 | /** 7 | * Pretty logging to `console` for client applications 8 | */ 9 | export default function ClientConsoleLogger() {} 10 | 11 | /** 12 | * Transport to `console` 13 | * @param {Object} data 14 | * @returns {undefined} 15 | */ 16 | ClientConsoleLogger.prototype.write = function (data = {}) { 17 | const loggerName = data.component ? `${data.name}/${data.component}` : data.name; 18 | 19 | let levelCss; 20 | const defaultCss = 'color: DimGray'; 21 | const msgCss = 'color: SteelBlue'; 22 | 23 | if (data.level < bunyan.DEBUG) { 24 | levelCss = 'color: DeepPink'; 25 | } else if (data.level < bunyan.INFO) { 26 | levelCss = 'color: GoldenRod'; 27 | } else if (data.level < bunyan.WARN) { 28 | levelCss = 'color: DarkTurquoise'; 29 | } else if (data.level < bunyan.ERROR) { 30 | levelCss = 'color: Purple'; 31 | } else if (data.level < bunyan.FATAL) { 32 | levelCss = 'color: Crimson'; 33 | } else { 34 | levelCss = 'color: Black'; 35 | } 36 | 37 | console.log( // eslint-disable-line no-console 38 | '[%s] %c%s%c: %s: %c%s', 39 | data.time, 40 | levelCss, bunyan.nameFromLevel[data.level], 41 | defaultCss, loggerName, 42 | msgCss, data.msg 43 | ); 44 | 45 | if (data.err && data.err.stack) { 46 | console.error(data.err.stack); // eslint-disable-line no-console 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/util/client/logentriesLogger.js: -------------------------------------------------------------------------------- 1 | import isFunction from 'lodash/isFunction'; 2 | import bunyan from 'bunyan'; 3 | import LE from 'le_js'; 4 | 5 | /** 6 | * Custom bunyan stream that transports to Logentries from client applications 7 | * @param {String} options.token 8 | * @param {String} options.level 9 | */ 10 | export default function ClientLogentriesLogger({ name, token }) { 11 | try { 12 | // If a LE logger does not exist with this name, this will throw 13 | LE.to(name); 14 | } catch (err) { 15 | // Init the LE logger 16 | LE.init({ 17 | name, 18 | token, 19 | no_format: true, 20 | page_info: 'per-page', 21 | }); 22 | } 23 | } 24 | 25 | /** 26 | * Transport logs to Logentries 27 | * @param {Object} data 28 | * @returns {undefined} 29 | */ 30 | ClientLogentriesLogger.prototype.write = function (data = {}) { 31 | const level = bunyan.nameFromLevel[data.level]; 32 | if (isFunction(LE[level])) { 33 | LE[level](data); 34 | } else { 35 | LE.log(data); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /src/util/client/rollbarLogger.js: -------------------------------------------------------------------------------- 1 | import omit from 'lodash/omit'; 2 | import get from 'lodash/get'; 3 | import isFunction from 'lodash/isFunction'; 4 | 5 | import { bunyanLevelToRollbarLevelName } from '../common/rollbar'; 6 | 7 | // Rollbar script exposes this global immediately, whether or not its already initialized 8 | export const isGlobalRollbarConfigured = () => !!get(global, 'Rollbar'); 9 | 10 | /** 11 | * Custom rollbar stream that transports to logentries from a browser 12 | * Wortks with a global Rollbar instance that is already initialized. 13 | * Note this expects rollbar to be loaded in the head, not via an npm module. 14 | * See https://rollbar.com/docs/notifier/rollbar.js/#quick-start for details on 15 | * integrating Rollbar in client apps 16 | * 17 | * @param {String} options.token 18 | * @param {String} options.environment 19 | * @param {String} options.codeVersion 20 | */ 21 | export default function ClientRollbarLogger({ token, environment, codeVersion }) { 22 | // Rollbar may already be initialized, but thats ok 23 | // https://rollbar.com/docs/notifier/rollbar.js/configuration/ 24 | global.Rollbar.configure({ 25 | accessToken: token, 26 | environment, 27 | captureUncaught: true, 28 | captureUnhandledRejections: true, 29 | payload: { 30 | environment, 31 | javascript: { 32 | code_version: codeVersion, 33 | source_map_enabled: true, 34 | }, 35 | }, 36 | }); 37 | } 38 | 39 | /** 40 | * Transport logs to Rollbar 41 | * @param {Object} data 42 | * @returns {undefined} 43 | */ 44 | ClientRollbarLogger.prototype.write = function (data = {}) { 45 | const rollbarLevelName = bunyanLevelToRollbarLevelName(data.level); 46 | const scopeData = omit(data, ['err', 'level']); 47 | const payload = Object.assign({ level: rollbarLevelName }, scopeData); 48 | 49 | // https://rollbar.com/docs/notifier/rollbar.js/#rollbarlog 50 | const logFn = global.Rollbar[rollbarLevelName]; 51 | if (isFunction(logFn)) { 52 | logFn.call(global.Rollbar, data.msg, data.err, payload); 53 | } else { 54 | global.Rollbar.error(data.msg, data.err, payload); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /src/util/common/config.js: -------------------------------------------------------------------------------- 1 | import bunyan, { levelFromName } from 'bunyan'; 2 | import pick from 'lodash/pick'; 3 | 4 | /** 5 | * Config keys that should always be passed to 6 | * `bunyan.createLogger` 7 | * @type {Array} 8 | */ 9 | const BUNYAN_CONFIG_FIELDS = [ 10 | 'name', 11 | 'level', 12 | 'streams', 13 | 'serializers', 14 | ]; 15 | 16 | /** 17 | * Whitelist of extra config keys that should be 18 | * passed to `bunyan.createLogger` to form 19 | * root logger fields. 20 | * @type {Array} 21 | */ 22 | const DEFAULT_ROOT_FIELDS = [ 23 | 'environment', 24 | 'release', 25 | 'javascript', 26 | ]; 27 | 28 | /** 29 | * Array of bunyan's different log levels. 30 | * https://github.com/trentm/node-bunyan#levels 31 | * @type {Array} 32 | */ 33 | export const BUNYAN_LOGGER_LEVELS = Object.keys(levelFromName); 34 | 35 | /** @type {Object} default config to Logger classes */ 36 | export const DEFAULT_CONFIG = Object.freeze({ 37 | name: 'WeWork', 38 | environment: null, 39 | codeVersion: null, 40 | level: 'info', 41 | stdout: true, 42 | streams: null, 43 | serializers: bunyan.stdSerializers, 44 | logentriesToken: null, 45 | rollbarToken: null, 46 | rootFields: DEFAULT_ROOT_FIELDS, 47 | scrubFields: [], 48 | }); 49 | 50 | /** 51 | * Merges config with DEFAULT_CONFIG, and appends passed in streams 52 | * with pre-configured streams for the runtime. 53 | * 54 | * This is used to configure this library, not bunyan as it has a lot of 55 | * extra information. See `toBunyanConfig` below. 56 | * 57 | * @param {Object} config 58 | * @param {Function} getStreamsForRuntime - returns appended config.streams 59 | * @returns {Object} runtimeConfig 60 | */ 61 | export function assembleConfig(config, getStreamsForRuntime) { 62 | const baseConfig = Object.assign({}, DEFAULT_CONFIG, config); 63 | 64 | // Add our custom streams and return a full `we-js-logger` config object 65 | return Object.assign(baseConfig, { 66 | streams: getStreamsForRuntime(baseConfig), 67 | }); 68 | } 69 | 70 | /** 71 | * Create a config objct for bunyan from a full `we-js-logger` config object. 72 | * Extra keys passed to `bunyan.createLogger` become root logger fields, pass 73 | * a custom `config.rootFields` to control this behavior 74 | * 75 | * @param {Object} config 76 | * @param {String[]} config.rootFields - extra fields to pass to bunyan 77 | * @return {Object} config for bunyan.createLogger 78 | */ 79 | export function toBunyanConfig(config) { 80 | return pick(config, BUNYAN_CONFIG_FIELDS.concat(config.rootFields)); 81 | } 82 | -------------------------------------------------------------------------------- /src/util/common/logForLevel.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable prefer-spread */ 2 | 3 | import scrub from './scrub'; 4 | 5 | /** 6 | * Creates a log method for a particular level 7 | * @param {String} level 8 | * @return {Function} 9 | */ 10 | export default function logForLevel(level) { 11 | 12 | /** 13 | * Log at a level. 14 | * Must be bound to a logger instance. 15 | * 16 | * @param {*} args 17 | * @return {undefined} 18 | */ 19 | return function logIt(...args) { 20 | return this._logger[level].apply(this._logger, scrub(args, this._config, this._logger)); 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/util/common/rollbar.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | 3 | import bunyan from 'bunyan'; 4 | 5 | /** 6 | * Map of bunyan log levels to Rollbar levels 7 | * https://github.com/trentm/node-bunyan#levels 8 | * https://rollbar.com/docs/notifier/rollbar.js/api/#rollbardebuginfowarnwarningerrorcritical 9 | * @type {Object} 10 | */ 11 | const bunyanToRollbarLevelMap = { 12 | fatal: 'critical', 13 | error: 'error', 14 | warn: 'warning', 15 | info: 'info', 16 | debug: 'debug', 17 | trace: 'debug', 18 | }; 19 | 20 | /** 21 | * Convert bunyan log level to rollbar level. Defaults to 'error'. 22 | * @param {String} level - bunyan log level 23 | * @returns {String} Rollbar log level 24 | */ 25 | export const bunyanLevelToRollbarLevelName = (level = bunyan.ERROR) => { 26 | const bunyanLevelName = bunyan.nameFromLevel[level]; 27 | return bunyanToRollbarLevelMap[bunyanLevelName] || 'error'; 28 | }; 29 | -------------------------------------------------------------------------------- /src/util/common/scrub.js: -------------------------------------------------------------------------------- 1 | import isObject from 'lodash/isObject'; 2 | import tail from 'lodash/tail'; 3 | import mapValues from 'lodash/mapValues'; 4 | import hideSecrets from 'hide-secrets'; 5 | 6 | function getScrubConfig(config) { 7 | return { badWords: config.scrubFields }; 8 | } 9 | 10 | function handleContext(data, config = {}, logger) { 11 | const serializedFields = Object.keys(logger.serializers || {}); 12 | 13 | return mapValues(data, (val, key) => { 14 | if (serializedFields.includes(key)) { 15 | return val; 16 | } 17 | 18 | return hideSecrets(val, getScrubConfig(config)); 19 | }); 20 | } 21 | 22 | export default function scrub(args, config = {}, logger) { 23 | if (Array.isArray(config.scrubFields) && config.scrubFields.length) { 24 | if (isObject(args[0])) { 25 | return [handleContext(args[0], config, logger), ...hideSecrets(tail(args), getScrubConfig(config))]; 26 | } 27 | 28 | return hideSecrets(args, getScrubConfig(config)); 29 | } 30 | 31 | return args; 32 | } 33 | -------------------------------------------------------------------------------- /src/util/server/logentriesLogger.js: -------------------------------------------------------------------------------- 1 | import Logentries from 'le_node'; 2 | 3 | /** 4 | * Custom bunyan stream that transports to logentries from a node process 5 | * @param {Object} options 6 | * @param {String} options.token 7 | * @param {String} options.level 8 | * @returns {Object} - bunyan stream config 9 | */ 10 | export default function ServerLogentriesLogger({ name, token, level }) { 11 | const loggerDefinition = Logentries.bunyanStream({ 12 | name, 13 | token, 14 | secure: true, 15 | withStack: true, 16 | }); 17 | loggerDefinition.level = level; 18 | 19 | return loggerDefinition; 20 | } 21 | -------------------------------------------------------------------------------- /src/util/server/requestLogger.js: -------------------------------------------------------------------------------- 1 | const uuid = require('uuid'); 2 | 3 | const DEFAULT_HEADER_NAME = 'x-request-id'; 4 | 5 | /** 6 | * Create a request loging express middleware 7 | * @param {Object} logger - a logger instance 8 | * @param {Object} options 9 | * @param {String?} options.reqIdHeader 10 | * @returns {Function} 11 | */ 12 | export default function createRequestLogger(logger, { reqIdHeader = DEFAULT_HEADER_NAME } = {}) { 13 | 14 | /** 15 | * Request Logger Middleware 16 | * Adds base logging to every request 17 | * Attaches a `log` child to each request object 18 | * 19 | * @param {Object} req 20 | * @param {Object} res 21 | * @param {Function} next 22 | * @returns {undefined} 23 | */ 24 | return function requestLoggerMiddleware(req, res, next) { 25 | const id = req.get(reqIdHeader) || uuid.v4(); 26 | let log = logger.child({ component: 'request', req_id: id, req }); 27 | 28 | // attach a logger to each request 29 | req.log = log; // eslint-disable-line no-param-reassign 30 | 31 | res.setHeader(reqIdHeader, id); 32 | 33 | log.info('start request'); 34 | 35 | const time = process.hrtime(); 36 | res.on('finish', () => { 37 | const diff = process.hrtime(time); 38 | const duration = diff[0] * 1e3 + diff[1] * 1e-6; // eslint-disable-line no-mixed-operators 39 | log.info({ res, duration }, 'end request'); 40 | 41 | // Release the request logger for GC 42 | req.log = null; // eslint-disable-line no-param-reassign 43 | log = null; 44 | }); 45 | 46 | next(); 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/util/server/rollbarLogger.js: -------------------------------------------------------------------------------- 1 | import Rollbar from 'rollbar'; 2 | import omit from 'lodash/omit'; 3 | import isFunction from 'lodash/isFunction'; 4 | import { bunyanLevelToRollbarLevelName } from '../common/rollbar'; 5 | 6 | /** 7 | * @description Custom bunyan stream that transports to Rollbar from a node process. 8 | * See https://rollbar.com/docs/notifier/node_rollbar/ for integration details 9 | * 10 | * @param {Object} token, codeVersion, and environment 11 | */ 12 | export default function ServerRollbarLogger({ token, codeVersion, environment }) { 13 | // https://rollbar.com/docs/notifier/rollbar.js/#quick-start-server 14 | this._rollbar = new Rollbar({ 15 | accessToken: token, 16 | captureUncaught: true, 17 | captureUnhandledRejections: true, 18 | code_version: codeVersion, 19 | environment, 20 | }); 21 | } 22 | 23 | /** 24 | * Transport to Rollbar 25 | * @description handles `err` and `req` properties, attaches any custom data, 26 | * and calls the appropriate Rollbar method. 27 | * 28 | * @param {Object} data 29 | * @returns {undefined} 30 | */ 31 | ServerRollbarLogger.prototype.write = function (data = {}) { 32 | const rollbarLevelName = bunyanLevelToRollbarLevelName(data.level); 33 | const scopeData = omit(data, ['req', 'err', 'level']); 34 | const payload = Object.assign({ level: rollbarLevelName }, scopeData); 35 | 36 | // https://rollbar.com/docs/notifier/rollbar.js/#rollbarlog-1 37 | const logFn = this._rollbar[rollbarLevelName]; 38 | 39 | if (isFunction(logFn)) { 40 | logFn.call(this._rollbar, data.msg, data.err, data.req, payload); 41 | } else { 42 | this._rollbar.error(data.msg, data.err, data.req, payload); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "mocha": true 4 | }, 5 | "globals": { 6 | "sinon": false, 7 | "expect": false 8 | }, 9 | "rules": { 10 | "no-unused-expressions": 0, 11 | "max-len": 0, 12 | "mocha/no-exclusive-tests": 1, 13 | "mocha/handle-done-callback": 1, 14 | "mocha/no-nested-tests": 1 15 | }, 16 | "plugins": [ 17 | "mocha" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /test/benchmark/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | const Benchmark = require('benchmark'); 4 | const bunyan = require('bunyan'); 5 | const WeLogger = require('../..'); 6 | 7 | const suite = new Benchmark.Suite(); 8 | 9 | const weLogger = new WeLogger({ stdout: false }); 10 | const weLoggerScrub = new WeLogger({ stdout: false, scrubFields: ['foo'] }); 11 | const bunyanLogger = bunyan.createLogger({ name: 'bunyan', streams: [] }); 12 | 13 | const context = { 14 | data: { some: 'stuff' }, 15 | foo: 'bar', 16 | }; 17 | const msg = 'hello world'; 18 | const msgExtra = { pretty: 'print me please' }; 19 | 20 | suite 21 | .add('we-js-logger', () => { 22 | weLogger.info(context, msg, msgExtra); 23 | }) 24 | .add('we-js-logger child', () => { 25 | const log = weLogger.child(); 26 | log.info(context, msg, msgExtra); 27 | }) 28 | .add('we-js-logger (with scrub fields)', () => { 29 | weLoggerScrub.info(context, msg, msgExtra); 30 | }) 31 | .add('we-js-logger child (with scrub fields)', () => { 32 | const log = weLoggerScrub.child(); 33 | log.info(context, msg, msgExtra); 34 | }) 35 | .add('bunyan', () => { 36 | bunyanLogger.info(context, msg, msgExtra); 37 | }) 38 | .add('bunyan child', () => { 39 | const log = bunyanLogger.child(); 40 | log.info(context, msg, msgExtra); 41 | }) 42 | // add listeners 43 | .on('cycle', (event) => { 44 | console.log(String(event.target)); 45 | }) 46 | .on('complete', function () { 47 | console.log(`Fastest is ${this.filter('fastest').map('name')}`); 48 | }) 49 | // run async 50 | .run({ async: true }); 51 | -------------------------------------------------------------------------------- /test/browser.index.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | require('./runner'); 3 | 4 | // shim global rollbar 5 | global.Rollbar = { 6 | options: { 7 | accessToken: 'testShim', 8 | }, 9 | configure: _.noop, 10 | scope: _.noop, 11 | critical: _.noop, 12 | error: _.noop, 13 | warning: _.noop, 14 | info: _.noop, 15 | debug: _.noop, 16 | log: _.noop, 17 | }; 18 | 19 | // require all `/test/specs/**/*.spec.js` 20 | const testsContext = require.context('./specs', true, /\.spec\.js$/); 21 | testsContext.keys().forEach(testsContext); 22 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | const webpackClientConfig = require('./webpack.client.config'); 2 | const saucelabsBrowsers = require('./saucelabs-browsers.json').browsers; 3 | 4 | module.exports = function (config) { 5 | // Default 6 | let browsers = ['PhantomJS']; 7 | // Saucelabs run 8 | if (process.env.SAUCELABS === 'true') { 9 | browsers = Object.keys(saucelabsBrowsers); 10 | } 11 | 12 | // karma configuration 13 | config.set({ 14 | basePath: '../', 15 | autoWatch: true, 16 | singleRun: true, 17 | frameworks: ['mocha', 'sinon-chai'], 18 | sauceLabs: { 19 | testName: 'we-js-logger/client', 20 | }, 21 | browserNoActivityTimeout: 120000, 22 | concurrency: 2, 23 | customLaunchers: saucelabsBrowsers, 24 | files: [ 25 | 'test/browser.index.js', 26 | ], 27 | browsers, 28 | reporters: process.env.SAUCELABS ? ['mocha', 'saucelabs'] : ['mocha'], 29 | 30 | mochaReporter: { 31 | output: 'autowatch', 32 | showDiff: true, 33 | }, 34 | 35 | preprocessors: { 36 | 'test/browser.index.js': ['webpack'], 37 | }, 38 | 39 | // webpack test configuration 40 | webpack: webpackClientConfig, 41 | 42 | // webpack-dev-middleware configuration 43 | webpackMiddleware: { 44 | stats: 'errors-only', 45 | }, 46 | }); 47 | }; 48 | -------------------------------------------------------------------------------- /test/leakage/check.js: -------------------------------------------------------------------------------- 1 | import { iterate } from 'leakage'; 2 | import bunyan from 'bunyan'; 3 | import Logger from '../..'; 4 | 5 | const TIMEOUT = 90 * 1000; 6 | const ITERATIONS = 60; 7 | 8 | describe('Heap Tests', () => { 9 | let log; 10 | 11 | describe('we-js-logger', () => { 12 | beforeEach(() => { 13 | log = new Logger({ stdout: false }); 14 | }); 15 | 16 | it(`does not leak when logging ${ITERATIONS} times`, function () { 17 | this.timeout(TIMEOUT); 18 | iterate(() => { 19 | log.info({}, 'log time', {}); 20 | }, { iterations: ITERATIONS }); 21 | }); 22 | 23 | it(`does not leak when building and logging child ${ITERATIONS} times`, function () { 24 | this.timeout(TIMEOUT); 25 | iterate(() => { 26 | const child = log.child(); 27 | child.info({}, 'child time', {}); 28 | }, { iterations: ITERATIONS }); 29 | }); 30 | }); 31 | 32 | describe('we-js-logger (with scrub fields)', () => { 33 | beforeEach(() => { 34 | log = new Logger({ stdout: false, scrubFields: ['foo'] }); 35 | }); 36 | 37 | it(`does not leak when logging ${ITERATIONS} times`, function () { 38 | this.timeout(TIMEOUT); 39 | iterate(() => { 40 | log.info({}, 'log time', {}); 41 | }, { iterations: ITERATIONS }); 42 | }); 43 | 44 | it(`does not leak when building and logging child ${ITERATIONS} times`, function () { 45 | this.timeout(TIMEOUT); 46 | iterate(() => { 47 | const child = log.child(); 48 | child.info({}, 'child time', {}); 49 | }, { iterations: ITERATIONS }); 50 | }); 51 | }); 52 | 53 | // Unskip if you want to compare to raw bunyan 54 | describe.skip('bunyan', () => { 55 | beforeEach(() => { 56 | log = new bunyan.createLogger({ name: 'test', streams: [] }); // eslint-disable-line new-cap 57 | }); 58 | 59 | it(`does not leak when logging ${ITERATIONS} times`, function () { 60 | this.timeout(TIMEOUT); 61 | return iterate(() => { 62 | log.info({}, 'log time', {}); 63 | }, { iterations: ITERATIONS }); 64 | }); 65 | 66 | it(`does not leak when building and logging child ${ITERATIONS} times`, function () { 67 | this.timeout(TIMEOUT); 68 | return iterate(() => { 69 | const child = log.child(); 70 | child.info({}, 'child time', {}); 71 | }, { iterations: ITERATIONS }); 72 | }); 73 | }); 74 | }); 75 | 76 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | require('babel-polyfill'); 3 | 4 | const chai = require('chai'); 5 | const sinon = require('sinon'); 6 | 7 | // NODE only 8 | // TODO better way to guard this 9 | // For browser tests, sinon-chai is loaded in karma config 10 | if (typeof document === 'undefined') { 11 | const sinonChai = require('sinon-chai'); // eslint-disable-line global-require 12 | chai.use(sinonChai); 13 | } 14 | 15 | global.expect = chai.expect; 16 | global.should = chai.should; 17 | global.assert = chai.assert; 18 | global.sinon = sinon; 19 | 20 | // Sandbox sinon for each test 21 | beforeEach(() => { 22 | global.sinon = sinon.sandbox.create(); 23 | }); 24 | afterEach(() => { 25 | global.sinon.restore(); 26 | }); 27 | -------------------------------------------------------------------------------- /test/saucelabs-browsers.json: -------------------------------------------------------------------------------- 1 | { 2 | "browsers": { 3 | "sl_chrome": { 4 | "base": "SauceLabs", 5 | "browserName": "chrome" 6 | }, 7 | "sl_firefox": { 8 | "base": "SauceLabs", 9 | "browserName": "firefox" 10 | }, 11 | "sl_safari": { 12 | "base": "SauceLabs", 13 | "browserName": "safari", 14 | "platform": "OS X 10.10" 15 | }, 16 | "sl_IE_9": { 17 | "base": "SauceLabs", 18 | "browserName": "internet explorer", 19 | "platform": "Windows 2008", 20 | "version": "9" 21 | }, 22 | "sl_IE_10": { 23 | "base": "SauceLabs", 24 | "browserName": "internet explorer", 25 | "platform": "Windows 2012", 26 | "version": "10" 27 | }, 28 | "sl_IE_11": { 29 | "base": "SauceLabs", 30 | "browserName": "internet explorer", 31 | "platform": "Windows 8.1", 32 | "version": "11" 33 | }, 34 | "sl_iOS": { 35 | "base": "SauceLabs", 36 | "browserName": "iphone", 37 | "platform": "OS X 10.10", 38 | "version": "8.1" 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /test/specs/logger.spec.js: -------------------------------------------------------------------------------- 1 | import bunyan, { stdSerializers } from 'bunyan'; 2 | import TestLogger from '../testLogger'; 3 | 4 | // Require the package. For client tests, webpack should 5 | // resolve to the browser version automatically. 6 | import Logger from '../..'; 7 | 8 | // Logentries validates the token it is passed, 9 | // here is a fake one in an acceptable format 10 | const fakeToken = '00000000-0000-0000-0000-000000000000'; 11 | 12 | describe('we-js-logger', () => { 13 | it('exports a "class"', () => { 14 | expect(Logger).to.be.a('function'); 15 | expect(new Logger()).to.be.ok; 16 | }); 17 | 18 | it('exposes "bunyanStdSerializers"', () => { 19 | expect(Logger.bunyanStdSerializers).to.eql(stdSerializers); 20 | }); 21 | 22 | if (typeof document === 'undefined') { 23 | it('exposes the rollbar instance', () => { 24 | expect(new Logger()).to.have.property('rollbar'); 25 | }); 26 | } 27 | 28 | describe('options', () => { 29 | it('accepts a name', () => { 30 | const name = 'WeTest!'; 31 | const log = new Logger({ name }); 32 | 33 | expect(log._logger.fields.name).to.equal(name); 34 | }); 35 | 36 | it('accepts custom streams', () => { 37 | const name = 'TestingCustomStream!'; 38 | const level = 'debug'; 39 | const msg = 'Testing!'; 40 | const cb = sinon.stub(); 41 | const log = new Logger({ 42 | name, 43 | level, 44 | streams: [ 45 | { 46 | type: 'raw', 47 | stream: new TestLogger({ cb }), 48 | }, 49 | ], 50 | }); 51 | 52 | expect(log._logger.streams.filter((config) => { 53 | return config.stream instanceof TestLogger; 54 | }), 'Adds the custom stream').to.be.ok; 55 | 56 | expect(log._logger.streams).to.have.length.gte(1, 'Has more than one stream'); 57 | 58 | log.debug({ foo: 'bar' }, msg); 59 | expect(cb.lastCall.args[0]).to.include({ 60 | name, 61 | msg, 62 | foo: 'bar', 63 | }, 'Uses the custom stream'); 64 | 65 | // TODO test that logs at a higher level dont go to the transport 66 | }); 67 | 68 | it('accepts a stdout flag', () => { 69 | expect(new Logger({ stdout: false })._logger.streams).to.have.length(0); 70 | 71 | // The actual stdout stream differs based on runtime env, just 72 | // asserting is is there. 73 | expect(new Logger({ stdout: true })._logger.streams).to.have.length(1); 74 | }); 75 | 76 | it('accepts a logentriesToken', () => { 77 | expect(new Logger()._logger.streams.find((config) => { 78 | return config.name === 'logentries'; 79 | })).to.not.be.ok; 80 | 81 | const log = new Logger({ 82 | logentriesToken: fakeToken, 83 | }); 84 | 85 | // The actual logentries stream differs based on runtime env, just 86 | // asserting one is there 87 | expect(log._logger.streams.find((config) => { 88 | return config.name === 'logentries'; 89 | })).to.be.ok; 90 | }); 91 | 92 | it('accepts a rollbarToken', () => { 93 | expect(new Logger()._logger.streams.find((config) => { 94 | return config.name === 'rollbar'; 95 | })).to.not.be.ok; 96 | 97 | const log = new Logger({ 98 | rollbarToken: fakeToken, 99 | }); 100 | 101 | // The actual rollbar stream differs based on runtime env, just 102 | // asserting one is there 103 | expect(log._logger.streams.find((config) => { 104 | return config.name === 'rollbar'; 105 | })).to.be.ok; 106 | }); 107 | 108 | it.skip('accepts an environment', () => {}); 109 | it.skip('accepts a codeVersion', () => {}); 110 | it.skip('accepts custom serializers', () => {}); 111 | }); 112 | 113 | describe('config.scrubFields', () => { 114 | let log; 115 | 116 | beforeEach(() => { 117 | log = new Logger({ 118 | release: { foo: 'bar' }, 119 | scrubFields: [ 120 | 'scrubMe', 121 | ], 122 | }); 123 | log._logger._emit = sinon.stub(); 124 | }); 125 | 126 | it('hides secrets for fields', () => { 127 | log.info({ data: { scrubMe: 'This should be ignored', tlc: 'No Scrubs' } }); 128 | expect(log._logger._emit).to.have.been.calledOnce; 129 | expect(log._logger._emit.firstCall.args[0].data.scrubMe).to.equal('[SECRET]'); 130 | expect(log._logger._emit.firstCall.args[0].data.tlc).to.equal('No Scrubs'); 131 | }); 132 | 133 | it('hides secrets for msg', () => { 134 | log.info('Testing Log', { scrubMe: 'This should be ignored', tlc: 'No Scrubs' }); 135 | expect(log._logger._emit).to.have.been.calledOnce; 136 | expect(log._logger._emit.firstCall.args[0].msg).to.equal("Testing Log { scrubMe: '[SECRET]', tlc: 'No Scrubs' }"); 137 | }); 138 | 139 | describe('child log', () => { 140 | let childLog; 141 | 142 | beforeEach(() => { 143 | childLog = log.child({ component: 'child/test' }); 144 | childLog._logger._emit = sinon.stub(); 145 | }); 146 | 147 | it('hides secrets for fields', () => { 148 | childLog.info({ data: { scrubMe: 'This should be ignored', tlc: 'No Scrubs' } }); 149 | expect(childLog._logger._emit).to.have.been.calledOnce; 150 | expect(childLog._logger._emit.firstCall.args[0].data.scrubMe).to.equal('[SECRET]'); 151 | expect(childLog._logger._emit.firstCall.args[0].data.tlc).to.equal('No Scrubs'); 152 | }); 153 | 154 | it('hides secrets for msg', () => { 155 | childLog.info('Testing Log', { scrubMe: 'This should be ignored', tlc: 'No Scrubs' }); 156 | expect(childLog._logger._emit).to.have.been.calledOnce; 157 | expect(childLog._logger._emit.firstCall.args[0].msg).to.equal("Testing Log { scrubMe: '[SECRET]', tlc: 'No Scrubs' }"); 158 | }); 159 | }); 160 | }); 161 | 162 | 163 | describe('instance', () => { 164 | let log; 165 | 166 | beforeEach(() => { 167 | log = new Logger({ 168 | release: { foo: 'bar' }, 169 | javascript: { browser: 'Chrome' }, 170 | }); 171 | }); 172 | 173 | describe('root fields', () => { 174 | it('has extra root fields "release" and "environment" by default', () => { 175 | expect(log._logger.fields).to.have.property('release'); 176 | expect(log._logger.fields).to.have.property('environment'); 177 | }); 178 | 179 | it('does not have extra root fields, such as "rollbarToken"', () => { 180 | expect(log._logger.fields).not.to.have.property('rollbarToken'); 181 | expect(log._logger.fields).not.to.have.property('logentriesToken'); 182 | expect(log._logger.fields).not.to.have.property('scrubFields'); 183 | expect(log._logger.fields).not.to.have.property('stdout'); 184 | }); 185 | 186 | it('can be configured to include any keys', () => { 187 | const customLog = new Logger({ 188 | rootFields: [ 'badIdea' ], 189 | badIdea: 'supersecret', 190 | release: { foo: 'not gonna be included unless specified in rootFields' }, 191 | }); 192 | expect(customLog._logger.fields).to.have.property('badIdea'); 193 | expect(customLog._logger.fields).not.to.have.property('release'); 194 | }); 195 | 196 | it('should allow the javascript field', () => { 197 | expect(log._logger.fields).to.have.property('javascript'); 198 | }); 199 | }); 200 | 201 | describe('log methods', () => { 202 | it('has a #fatal method', () => { 203 | expect(log.fatal).to.be.a('function'); 204 | }); 205 | it('has a #error method', () => { 206 | expect(log.error).to.be.a('function'); 207 | }); 208 | it('has a #warn method', () => { 209 | expect(log.warn).to.be.a('function'); 210 | }); 211 | it('has a #info method', () => { 212 | expect(log.info).to.be.a('function'); 213 | }); 214 | it('has a #debug method', () => { 215 | expect(log.debug).to.be.a('function'); 216 | }); 217 | it('has a #trace method', () => { 218 | expect(log.trace).to.be.a('function'); 219 | }); 220 | }); 221 | 222 | describe('#child', () => { 223 | it('calls child on the logger bunyan instance', () => { 224 | sinon.spy(bunyan.prototype, 'child'); 225 | log.child(); 226 | 227 | expect(bunyan.prototype.child).to.have.been.calledOnce; 228 | }); 229 | 230 | it('instantiates a new logger with the new child', () => { 231 | const stubChild = {}; 232 | sinon.stub(bunyan.prototype, 'child').returns(stubChild); 233 | const child = log.child(); 234 | 235 | expect(child).not.to.eql(log); 236 | expect(child._logger).to.eql(stubChild); 237 | }); 238 | 239 | it('instantiates a new logger with the same config', () => { 240 | const child = log.child(); 241 | expect(child._config).to.eql(log._config); 242 | }); 243 | }); 244 | 245 | // FIXME find a better way to do this. Should we use `env-universal`? 246 | if (typeof document === 'undefined') { 247 | describe('node only', () => { 248 | it('exposes a requestLogger middleware', () => { 249 | expect(log.requestLogger).to.be.a('function'); 250 | }); 251 | it('exposes a rollbarErrorMiddleware middleware', () => { 252 | expect(log.rollbarErrorMiddleware).to.be.a('function'); 253 | }); 254 | }); 255 | } 256 | }); 257 | 258 | describe.skip('Rollbar Transport', () => {}); 259 | 260 | describe.skip('Logentries Transport', () => {}); 261 | }); 262 | -------------------------------------------------------------------------------- /test/specs/requestLogger.spec.js: -------------------------------------------------------------------------------- 1 | import Logger from '../..'; 2 | import TestLogger from '../testLogger'; 3 | 4 | import createRequestLogger from '../../src/util/server/requestLogger'; 5 | 6 | // Only run these tests in node 7 | // FIXME find a better way to do this. Should we use `env-universal`? 8 | if (typeof document === 'undefined') { 9 | describe('util/server/requestLogger', () => { 10 | describe('#createRequestLogger', () => { 11 | it('is a function', () => { 12 | expect(createRequestLogger).to.be.a('function'); 13 | }); 14 | 15 | it('returns a function', () => { 16 | expect(createRequestLogger()).to.be.a('function'); 17 | }); 18 | }); 19 | 20 | describe('middleware', () => { 21 | let logger; 22 | let middleware; 23 | let cb; 24 | let req; 25 | let res; 26 | let next; 27 | 28 | beforeEach(() => { 29 | req = { 30 | get: sinon.stub(), 31 | }; 32 | res = { 33 | setHeader: sinon.stub(), 34 | on: sinon.stub().yields(), 35 | }; 36 | next = sinon.stub(); 37 | 38 | cb = sinon.stub(); 39 | 40 | logger = new Logger({ 41 | stdout: false, 42 | streams: [ 43 | { type: 'raw', stream: new TestLogger({ cb }) }, 44 | ], 45 | }); 46 | 47 | middleware = createRequestLogger(logger); 48 | }); 49 | 50 | it('reads a request id from the x-request-id header (configurable)', () => { 51 | const testReqId = 'abcd1234'; 52 | req.get.returns(testReqId); 53 | 54 | middleware(req, res, next); 55 | 56 | expect(cb.firstCall.args[0]).to.include({ 57 | req_id: testReqId, 58 | }); 59 | }); 60 | 61 | it('generates an id if no x-request-id header is present on the request', () => { 62 | middleware(req, res, next); 63 | 64 | expect(cb.firstCall.args[0].req_id).to.be.ok; 65 | }); 66 | 67 | it('logs a start request item', () => { 68 | middleware(req, res, next); 69 | 70 | expect(cb.firstCall.args[0]).to.include({ 71 | msg: 'start request', 72 | }); 73 | }); 74 | 75 | it('logs an end request item', () => { 76 | middleware(req, res, next); 77 | expect(cb.lastCall.args[0]).to.include({ 78 | msg: 'end request', 79 | }); 80 | }); 81 | 82 | it('includes the response time in the end request log', () => { 83 | middleware(req, res, next); 84 | 85 | const duration = cb.lastCall.args[0].duration; 86 | expect(duration).to.be.ok; 87 | expect(duration).to.be.gt(0); 88 | }); 89 | 90 | it('sets a x-request-id header (configurable) on the response', () => { 91 | const testReqId = '1234abcd'; 92 | req.get.returns(testReqId); 93 | 94 | middleware(req, res, next); 95 | 96 | res.setHeader.calledWith('x-request-id', testReqId); 97 | }); 98 | 99 | it('calls next', () => { 100 | middleware(req, res, next); 101 | expect(next).to.have.been.calledOnce; 102 | }); 103 | 104 | it('calls next immediately', () => { 105 | // Replace this stub with one that yields after a tick 106 | res.on = sinon.stub().yieldsAsync(); 107 | 108 | middleware(req, res, next); 109 | expect(next).to.have.been.calledOnce; 110 | }); 111 | 112 | it('attaches a `log` function to the request object', () => { 113 | // Don't yield here since the logger is cleared once the response is finished 114 | res.on = sinon.stub(); 115 | middleware(req, res, next); 116 | 117 | expect(req.log).to.be.an('object'); 118 | expect(req.log.info).to.be.a('function'); 119 | }); 120 | 121 | it('includes useful request information in the `req.log` fields', () => { 122 | const testReqId = '1234abcd'; 123 | req.get.returns(testReqId); 124 | // Don't yield here since the logger is cleared once the response is finished 125 | res.on = sinon.stub(); 126 | middleware(req, res, next); 127 | 128 | expect(req.log._logger.fields.component).to.eql('request'); 129 | expect(req.log._logger.fields.req).to.eql(req); 130 | expect(req.log._logger.fields.req_id).to.eql(testReqId); 131 | }); 132 | 133 | it('clears `req.log` after the response is finished', () => { 134 | middleware(req, res, next); 135 | expect(req.log).to.not.be.ok; 136 | }); 137 | }); 138 | }); 139 | } 140 | -------------------------------------------------------------------------------- /test/specs/util/logForLevel.spec.js: -------------------------------------------------------------------------------- 1 | import logForLevel from '../../../src/util/common/logForLevel'; 2 | import * as scrubModule from '../../../src/util/common/scrub'; 3 | 4 | describe('util/common/logForLevel', () => { 5 | it('returns a function', () => { 6 | expect(logForLevel()).to.be.a('function'); 7 | }); 8 | 9 | describe('log method', () => { 10 | let logIt; 11 | let ctx; 12 | 13 | beforeEach(() => { 14 | ctx = { 15 | _logger: { 16 | info: sinon.spy(), 17 | }, 18 | _config: {}, 19 | }; 20 | logIt = logForLevel('info').bind(ctx); 21 | }); 22 | 23 | it("calls the internal logger's `level` log method", () => { 24 | logIt(); 25 | expect(ctx._logger.info).to.have.been.calledOnce; 26 | }); 27 | 28 | it('passes all arguments through', () => { 29 | const args = ['foo', 'bar', 'baz', 'BAM!', {}]; 30 | logIt(...args); 31 | 32 | expect(ctx._logger.info).to.have.been.calledWith(...args); 33 | }); 34 | 35 | it('calls scrub on the arguments', () => { 36 | sinon.stub(scrubModule, 'default'); 37 | 38 | const args = ['foo', 'bar', 'baz', 'BAM!', {}]; 39 | logIt(...args); 40 | 41 | expect(scrubModule.default).to.have.been.calledWith(args, ctx._config); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /test/specs/util/scrub.spec.js: -------------------------------------------------------------------------------- 1 | import scrub from '../../../src/util/common/scrub'; 2 | 3 | // FIXME stub out `hideSecrets` -- doesn't seem to be working on first pass 4 | // import * as hideSecretsModule from 'hide-secrets'; 5 | 6 | // TODO test special `handleContext` behavior 7 | 8 | describe('util/common/scrub', () => { 9 | let args; 10 | beforeEach(() => { 11 | args = ['test', 'args', 'are', 'so', { fun: 'yay' }]; 12 | }); 13 | 14 | it('returns args if no config.scrubFields are present', () => { 15 | const result = scrub(args, { scrubFields: [] }); 16 | expect(result).to.eql(args); 17 | }); 18 | 19 | it('passes args through hide-secrets if config.scrubFields are present', () => { 20 | const result = scrub(args, { scrubFields: ['fun'] }); 21 | expect(result).to.eql(['test', 'args', 'are', 'so', { fun: '[SECRET]' }]); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/stress/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This test spins up a very basic express server 3 | * with the we-js-logger request logger middleware, 4 | * and measures request performance under load. 5 | */ 6 | 7 | const express = require('express'); 8 | const loadtest = require('loadtest'); 9 | const Logger = require('../..'); 10 | 11 | const PORT = 3030; 12 | const TEST_LENGTH = 30; // seconds 13 | 14 | const logger = new Logger({ 15 | stdout: false, 16 | scrubFields: ['test'], 17 | }); 18 | 19 | let count = 0; 20 | const app = express(); 21 | app.use(logger.requestLogger); 22 | app.get('*', (req, res) => { 23 | count += 1; 24 | 25 | req.log.info( 26 | { data: { test: 'super secret' } }, 27 | 'every request logs', 28 | { test: 'big secret', count } 29 | ); 30 | res.status(200).send(); 31 | }); 32 | 33 | const loadOptions = { 34 | url: `http://localhost:${PORT}`, 35 | // Run stress test for this long. 36 | maxSeconds: TEST_LENGTH, 37 | // Simulate this number of concurrent clients 38 | concurrency: 10, 39 | // Each making this number of requests per second 40 | requestsPerSecond: 20, 41 | }; 42 | 43 | function printResults(results) { 44 | // eslint-disable-next-line no-console 45 | console.log(` 46 | Completed requests: ${results.totalRequests} 47 | Requests per second: ${results.rps} 48 | Total time: ${results.totalTimeSeconds} 49 | Mean latency: ${results.meanLatencyMs} 50 | 51 | Percentage of requests served within a certain time: 52 | 50% ${results.percentiles['50']}ms 53 | 90% ${results.percentiles['90']}ms 54 | 95% ${results.percentiles['95']}ms 55 | 99% ${results.percentiles['99']}ms 56 | 100% ${results.maxLatencyMs}ms (longest request) 57 | `); 58 | } 59 | 60 | 61 | describe('Request Logger -- Stress Test', function () { 62 | // `loadtest` requests come in after the set test time -- give a little extra room here 63 | this.timeout((TEST_LENGTH + 10) * 1000); 64 | // Retry a few times in case of result variance 65 | this.retries(4); 66 | 67 | let listener; 68 | before((done) => { 69 | listener = app.listen(PORT, done); 70 | }); 71 | 72 | after((done) => { 73 | listener.close(done); 74 | }); 75 | 76 | it('express server with requestLogger and scrubFields does not lock up under load', (done) => { 77 | loadtest.loadTest(loadOptions, (error, results) => { 78 | if (error) { 79 | done(error); 80 | return; 81 | } 82 | 83 | printResults(results); 84 | 85 | expect(results.totalErrors).to.eql(0, 'there are no request errors'); 86 | expect(results.meanLatencyMs).to.be.lte(10, 'mean response is less than 10ms'); 87 | expect(results.percentiles['99']).to.be.lte(20, '99% of responses under 20ms'); 88 | // expect(results.maxLatencyMs).to.be.lte(40, 'max response is less than 40ms'); 89 | done(); 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /test/testLogger.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @module we-js-logger/test/testLogger 3 | * @description Custom rollbar stream for testing 4 | */ 5 | 6 | export default function TestLogger(opts) { 7 | this.cb = opts.cb; 8 | } 9 | 10 | TestLogger.prototype.write = function (data = {}) { 11 | this.cb(data); 12 | }; 13 | -------------------------------------------------------------------------------- /test/webpack.client.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | devtool: 'inline-source-map', 5 | module: { 6 | noParse: [ 7 | /node_modules(\\|\/)sinon/, 8 | ], 9 | loaders: [ 10 | { 11 | test: /\.json$/, 12 | loader: 'json-loader', 13 | }, 14 | { 15 | test: /\.js$/, 16 | include: [ 17 | path.resolve(process.cwd(), 'src'), 18 | path.resolve(process.cwd(), 'test'), 19 | ], 20 | loader: 'babel-loader', 21 | }, 22 | { 23 | test: /sinon(\\|\/)pkg(\\|\/)sinon\.js/, 24 | loader: 'imports?define=>false,require=>false', 25 | }, 26 | ], 27 | }, 28 | resolve: { 29 | alias: { 30 | sinon: 'sinon/pkg/sinon', 31 | }, 32 | }, 33 | 34 | // Shim unnecessary node-only deps in client builds 35 | // https://github.com/trentm/node-bunyan#webpack 36 | node: { 37 | fs: 'empty', 38 | mv: 'empty', 39 | 'dtrace-provider': 'empty', 40 | 'safe-json-stringify': 'empty', 41 | 'source-map-support': 'empty', 42 | }, 43 | }; 44 | --------------------------------------------------------------------------------