├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .npmignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CREDITS.md ├── LICENSE ├── Makefile ├── README.md ├── docker-compose.yml ├── examples ├── all_batching.js ├── basic.js ├── custom_format.js ├── manual_batching.js └── retry.js ├── index.js ├── licenses └── LICENSE-SPLUNK-LOGGING ├── package-lock.json ├── package.json ├── scripts └── test_specific.sh └── test ├── test_bunyan.js ├── test_config.js └── test_stream.js /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | on: 3 | release: 4 | types: [published] 5 | jobs: 6 | build: 7 | name: Create Release 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | # Setup .npmrc file to publish to npm 12 | - uses: actions/setup-node@v1 13 | with: 14 | node-version: '14.0' 15 | registry-url: 'https://registry.npmjs.org' 16 | - run: npm install 17 | # Publish to npm 18 | - run: npm publish 19 | env: 20 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 21 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | [push, pull_request] 5 | 6 | jobs: 7 | test-execution: 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | node-version: 12 | - "14.0" 13 | - "10.0" 14 | splunk-version: 15 | - latest 16 | - "8.0" 17 | services: 18 | splunk: 19 | image: splunk/splunk:${{matrix.splunk-version}} 20 | env: 21 | SPLUNK_START_ARGS: --accept-license 22 | SPLUNK_PASSWORD: changed! 23 | ports: 24 | - 8088:8088 25 | - 8089:8089 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Use Node.js ${{ matrix.node-version }} 29 | uses: actions/setup-node@v2 30 | with: 31 | node-version: ${{ matrix.node-version }} 32 | - run: npm install 33 | - name: Test Execution 34 | run: npm test 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | npm-DEBUG.log 4 | test/config.json 5 | .idea 6 | docs -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | // Settings 3 | "passfail" : false, // Stop on first error. 4 | "maxerr" : 10000, // Maximum error before stopping. 5 | 6 | 7 | // Predefined globals whom JSHint will ignore. 8 | "browser" : true, // Standard browser globals e.g. `window`, `document`. 9 | 10 | "node" : true, 11 | "rhino" : false, 12 | "couch" : false, 13 | "wsh" : false, // Windows Scripting Host. 14 | 15 | "jquery" : true, 16 | "prototypejs" : false, 17 | "mootools" : false, 18 | "dojo" : false, 19 | 20 | "predef" : [ // Custom globals. 21 | //"exampleVar", 22 | //"anotherCoolGlobal", 23 | //"iLoveDouglas", 24 | "__exportName" 25 | ], 26 | 27 | 28 | // Development. 29 | "debug" : false, // Allow debugger statements e.g. browser breakpoints. 30 | "devel" : true, // Allow developments statements e.g. `console.log();`. 31 | 32 | 33 | // EcmaScript 5. 34 | "es5" : false, // Allow EcmaScript 5 syntax. 35 | "strict" : false, // Require `use strict` pragma in every file. 36 | "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). 37 | 38 | 39 | // The Good Parts. 40 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). 41 | "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. 42 | "bitwise" : true, // Prohibit bitwise operators (&, |, ^, etc.). 43 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. 44 | "curly" : true, // Require {} for every new block or scope. 45 | "eqeqeq" : true, // Require triple equals i.e. `===`. 46 | "eqnull" : false, // Tolerate use of `== null`. 47 | "evil" : false, // Tolerate use of `eval`. 48 | "expr" : false, // Tolerate `ExpressionStatement` as Programs. 49 | "forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`. 50 | "immed" : false, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 51 | "latedef" : true, // Prohipit variable use before definition. 52 | "loopfunc" : false, // Allow functions to be defined within loops. 53 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. 54 | "regexp" : false, // Prohibit `.` and `[^...]` in regular expressions. 55 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. 56 | "scripturl" : false, // Tolerate script-targeted URLs. 57 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. 58 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. 59 | "undef" : true, // Require all non-global variables be declared before they are used. 60 | 61 | 62 | // Personal styling prefrences. 63 | "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. 64 | "noempty" : true, // Prohibit use of empty blocks. 65 | "nonew" : true, // Prohibit use of constructors for side-effects. 66 | "nomen" : false, // Prohibit use of initial or trailing underbars in names. 67 | "onevar" : false, // Allow only one `var` statement per function. 68 | "plusplus" : false, // Prohibit use of `++` & `--`. 69 | "sub" : true, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 70 | "trailing" : true, // Prohibit trailing whitespaces. // TODO 71 | "white" : false, // Check against strict whitespace and indentation rules. 72 | "unused" : true, 73 | 74 | "globals" : { 75 | /* Mocha */ 76 | "describe" : false, 77 | "it" : false, 78 | "before" : false, 79 | "beforeEach" : false, 80 | "after" : false, 81 | "afterEach" : false 82 | } 83 | } -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea 2 | coverage 3 | examples 4 | docs 5 | test 6 | .jshintrc 7 | .jshintignore -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Splunk HTTP Event Collector Stream for Bunyan 2 | 3 | ## v0.11.0 4 | 5 | ### New features & APIs 6 | 7 | * Replaced client implementation to use `needle` in lieu of deprecated `request`. 8 | * Replaced client implementation to use `nyc` in lieu of deprecated `istanbul`. 9 | 10 | ### Minor changes 11 | 12 | * Converted CI from Travis to Github Actions and added CD 13 | * Upgrade version of bunyan to 1.8.15. 14 | * Upgrade version of splunk-logging to 0.11.1. 15 | * Upgrade version of jsdoc to 3.6.7. 16 | * Upgrade version of jshint to 2.12.0. 17 | * Upgrade version of mocha to 8.4.0. 18 | 19 | ## v0.10.1 20 | 21 | ### Breaking changes 22 | 23 | * Dropped support for Node.js versions older than 4.0.0. 24 | 25 | ## v0.9.3 26 | 27 | ### Minor changes 28 | 29 | * Update version of `splunk-logging` dependency, allowing new installs to receive security updates to the transitive `request` dependency. 30 | 31 | ## v0.9.2 32 | 33 | ### Bug Fixes 34 | 35 | * Workaround a urlencoding bug in the request library (GitHub issue [#6](https://github.com/splunk/splunk-javascript-logging/issues/6) for the underlying `splunk-logging` library). 36 | * Catch JSON parsing errors from the server without crashing (GitHub issue [#9](https://github.com/splunk/splunk-javascript-logging/issues/9) for the underlying `splunk-logging` library). 37 | 38 | 39 | ## v0.9.1 40 | 41 | ### Bug Fixes 42 | 43 | * Relax port validation for ports < 1000. 44 | 45 | ## v0.9.0 46 | 47 | ### New Features & APIs 48 | 49 | * Added the ability to configure automated batching with 3 settings: `batchInterval`, `maxBatchCount`, & `maxBatchSize`. 50 | * Added the ability to retry sending to Splunk in the case of network errors with the `maxRetries` configuration setting. 51 | * Added the ability to configure a custom Splunk event format by overriding `eventFormatter(message, severity)`. 52 | 53 | ### Breaking Changes 54 | 55 | * Removed the `autoFlush` configuration setting. To achieve the same effect, set `config.maxBatchCount` to `0`. 56 | * Removed support for middleware functions. 57 | * The `context` object has been simplified, `config` and `requestOptions` can no longer be specified there - please use those settings directly on the logger. 58 | 59 | ### Examples 60 | 61 | * Removed the `middleware.js` example. 62 | * Renamed the `batching.js` example to `manual_batching`. 63 | * Added the `all_batching.js`, `custom_format.js`, `retry.js` examples. 64 | 65 | ### Minor changes 66 | 67 | * Significant refactor of internal functions. 68 | 69 | ## v0.8.0 - beta 70 | 71 | * Beta release. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | ## How to contribute 4 | 5 | If you would like to contribute to this project, see [Contributions to Splunk][indivcontrib] for more information. 6 | 7 | ## Issues & Bug Reports 8 | 9 | If you're seeing some unexpected behavior with this project, please create an [issue on GitHub][issues] with the following information: 10 | 11 | 1. Version of this project you're using (ex: 0.8.0) 12 | 1. Platform version (ex: Windows Server 2012) 13 | 1. Framework version (ex: Node.js 0.10.37) 14 | 1. Splunk version (ex: 6.3.0) 15 | 1. Other relevant information (ex: local/remote environment, Splunk network configuration) 16 | 17 | Alternatively, if you have a Splunk question please ask on [Splunk Answers][answers] 18 | 19 | ## Pull requests 20 | 21 | We love to see pull requests! 22 | 23 | To create a pull request: 24 | 25 | 1. Fill out the [Individual Contributor Agreement][indivcontrib]. 26 | 1. Fork [the repository][repo]. 27 | 1. Make changes to the **`develop`** branch, preferably with tests. 28 | 1. Create a [pull request][pulls] against the **`develop`** branch. 29 | 30 | ## Contact us 31 | 32 | You can [contact support][contact] if you have Splunk related questions. 33 | 34 | You can reach the Developer Platform team at _devinfo@splunk.com_. 35 | 36 | [contributions]: http://dev.splunk.com/view/opensource/SP-CAAAEDM 37 | [indivcontrib]: http://dev.splunk.com/goto/individualcontributions 38 | [companycontrib]: http://dev.splunk.com/view/companycontributions/SP-CAAAEDR 39 | [answers]: http://answers.splunk.com/ 40 | [repo]: https://github.com/splunk/splunk-bunyan-logger 41 | [issues]: https://github.com/splunk/splunk-bunyan-logger/issues 42 | [pulls]: https://github.com/splunk/splunk-bunyan-logger/pulls 43 | [contact]: https://www.splunk.com/en_us/support-and-services.html 44 | -------------------------------------------------------------------------------- /CREDITS.md: -------------------------------------------------------------------------------- 1 | # Third-party software credits 2 | 3 | Some of the components included in the Splunk HTTP Event Collector Stream for Bunyan are licensed under free or open source licenses. We wish to thank the contributors to those projects. 4 | 5 | | Contributor | Description | License | 6 | |:----------- |:----------- |:------- | 7 | | [splunk-logging](https://github.com/splunk/splunk-javascript-logging) | This project provides a simple JavaScript interface for logging to HTTP Event Collector in Splunk Enterprise and Splunk Cloud. | [Apache](https://github.com/splunk/splunk-bunyan-logger/blob/master/licenses/LICENSE-SPLUNK-LOGGING) | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # text reset 2 | NO_COLOR=\033[0m 3 | # green 4 | OK_COLOR=\033[32;01m 5 | # red 6 | ERROR_COLOR=\033[31;01m 7 | # cyan 8 | WARN_COLOR=\033[36;01m 9 | # yellow 10 | ATTN_COLOR=\033[33;01m 11 | 12 | ROOT_DIR := $(shell git rev-parse --show-toplevel) 13 | 14 | VERSION := `git describe --tags --dirty 2>/dev/null` 15 | COMMITHASH := `git rev-parse --short HEAD 2>/dev/null` 16 | DATE := `date "+%FT%T%z"` 17 | 18 | .PHONY: all 19 | all: init test 20 | 21 | init: 22 | @echo "$(ATTN_COLOR)==> init $(NO_COLOR)" 23 | 24 | .PHONY: test 25 | test: 26 | @echo "$(ATTN_COLOR)==> test $(NO_COLOR)" 27 | @npm test 28 | 29 | .PHONY: test_specific 30 | test_specific: 31 | @echo "$(ATTN_COLOR)==> test_specific $(NO_COLOR)" 32 | @sh ./scripts/test_specific.sh 33 | 34 | .PHONY: up 35 | up: 36 | @echo "$(ATTN_COLOR)==> up $(NO_COLOR)" 37 | @docker-compose up -d 38 | 39 | .PHONY: wait_up 40 | wait_up: 41 | @echo "$(ATTN_COLOR)==> wait_up $(NO_COLOR)" 42 | @for i in `seq 0 180`; do if docker exec -it splunk /sbin/checkstate.sh &> /dev/null; then break; fi; printf "\rWaiting for Splunk for %s seconds..." $$i; sleep 1; done 43 | 44 | .PHONY: down 45 | down: 46 | @echo "$(ATTN_COLOR)==> down $(NO_COLOR)" 47 | @docker-compose stop 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Splunk HTTP Event Collector Stream for Bunyan 2 | 3 | #### Version 0.11.0 4 | 5 | This project provides a [Bunyan](https://www.npmjs.com/package/bunyan) stream for HTTP Event Collector in Splunk Enterprise and Splunk Cloud. 6 | 7 | ## Requirements 8 | 9 | * Node.js v4 or later. Splunk HTTP Event Collector Stream for Bunyan is tested with Node.js v10.0 and v14.0. 10 | * Splunk Enterprise 6.3.0 or later, or Splunk Cloud. Splunk HTTP Event Collector Stream for Bunyan is tested with Splunk Enterprise 8.0 and 8.2.0. 11 | * An HTTP Event Collector token from your Splunk Enterprise or Splunk Cloud server. 12 | * [Bunyan](https://www.npmjs.com/package/bunyan) (`npm install --save bunyan`). 13 | 14 | ## Installation 15 | 16 | First, update npm to the latest version by running: `sudo npm install npm -g`. 17 | 18 | Then run: `npm install --save splunk-bunyan-logger`. 19 | 20 | ## Usage 21 | 22 | See the `examples` folder for usage examples: 23 | 24 | * `all_batching.js`: Shows how to configure a Bunyan Stream with the 3 batching settings: `batchInterval`, `maxBatchCount`, & `maxBatchSize`. 25 | * `basic.js`: Shows how to configure a Bunyan stream and send a log message to Splunk. 26 | * `custom_format.js`: Shows how to configure a Bunyan Stream to log messages to Splunk using a custom format. 27 | * `manual_batching.js`: Shows how to queue log messages, and send them in batches by manually calling `flush()`. 28 | * `retry.js`: Shows how to configure retries on errors. 29 | 30 | ### Support 31 | 32 | The Splunk HTTP Event Collector Stream for Bunyan is community-supported. 33 | 34 | 1. You can find help through our community on [Splunk Answers](https://community.splunk.com/t5/tag/logging-library-javascript/tg-p) (use the `logging-library-javascript` tag to identify your questions). 35 | 2. File issues on [GitHub](https://github.com/splunk/splunk-bunyan-logger/issues). 36 | 37 | ### SSL 38 | 39 | Note: SSL certificate validation is diabled by default. 40 | To enable it, set `logger.requestOptions.strictSSL = true` on your `SplunkStream` instance: 41 | 42 | ```javascript 43 | var bunyan = require("bunyan"); 44 | var splunkBunyan = require("splunk-bunyan-logger"); 45 | 46 | var config = { 47 | token: "your-token-here", 48 | url: "https://splunk.local:8088" 49 | }; 50 | 51 | var splunkStream = splunkBunyan.createStream(config); 52 | // Enable SSL certificate validation 53 | stream.logger.requestOptions.strictSSL = true; 54 | 55 | // Note: splunkStream must be set to an element in the streams array 56 | var Logger = bunyan.createLogger({ 57 | name: "my logger", 58 | streams: [ 59 | splunkStream 60 | ] 61 | }); 62 | ``` 63 | 64 | ### Basic example 65 | 66 | ```javascript 67 | var bunyan = require("bunyan"); 68 | var splunkBunyan = require("splunk-bunyan-logger"); 69 | 70 | var config = { 71 | token: "your-token-here", 72 | url: "https://splunk.local:8088" 73 | }; 74 | 75 | var splunkStream = splunkBunyan.createStream(config); 76 | 77 | // Note: splunkStream must be set to an element in the streams array 78 | var Logger = bunyan.createLogger({ 79 | name: "my logger", 80 | streams: [ 81 | splunkStream 82 | ] 83 | }); 84 | 85 | var payload = { 86 | // Message can be anything; doesn't have to be an object 87 | message: { 88 | temperature: "70F", 89 | chickenCount: 500 90 | } 91 | }; 92 | 93 | console.log("Sending payload", payload); 94 | Logger.info(payload, "Chicken coup looks stable."); 95 | ``` 96 | 97 | ## Community 98 | 99 | Stay connected with other developers building on Splunk software. 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 111 | 112 | 113 | 114 | 116 | 117 | 118 | 119 | 121 | 122 | 123 | 124 | 126 | 127 | 128 |
Emaildevinfo@splunk.com
Issues 110 | https://github.com/splunk/splunk-bunyan-logger/issues/
Answers 115 | http://answers.splunk.com/
Blog 120 | http://blogs.splunk.com/dev/
Twitter 125 | @splunkdev
129 | 130 | ## License 131 | 132 | The Splunk HTTP Event Collector Stream for Bunyan is licensed under the Apache 133 | License 2.0. Details can be found in the LICENSE file. 134 | 135 | [contact]: https://www.splunk.com/en_us/support-and-services.html 136 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | splunk: 5 | image: "splunk/splunk:latest" 6 | container_name: splunk 7 | environment: 8 | - SPLUNK_START_ARGS=--accept-license 9 | - SPLUNK_PASSWORD=changed! 10 | ports: 11 | - 8000:8000 12 | - 8088:8088 13 | - 8089:8089 14 | healthcheck: 15 | test: ['CMD', 'curl', '-f', 'http://localhost:8000'] 16 | interval: 5s 17 | timeout: 5s 18 | retries: 20 19 | -------------------------------------------------------------------------------- /examples/all_batching.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * This example shows how to batch events with the 19 | * Splunk Bunyan logger with all available settings: 20 | * batchInterval, maxBatchCount, & maxBatchSize. 21 | */ 22 | 23 | // Change to require("splunk-bunyan-logger"); 24 | var splunkBunyan = require("../index"); 25 | var bunyan = require("bunyan"); 26 | 27 | /** 28 | * Only the token property is required. 29 | * 30 | * Here, batchInterval is set to flush every 1 second, when 31 | * 10 events are queued, or when the size of queued events totals 32 | * more than 1kb. 33 | */ 34 | var config = { 35 | token: "your-token-here", 36 | url: "https://localhost:8088", 37 | batchInterval: 1000, 38 | maxBatchCount: 10, 39 | maxBatchSize: 1024 // 1kb 40 | }; 41 | var splunkStream = splunkBunyan.createStream(config); 42 | 43 | splunkStream.on("error", function(err, context) { 44 | // Handle errors here 45 | console.log("Error", err, "Context", context); 46 | }); 47 | 48 | // Setup Bunyan, adding splunkStream to the array of streams 49 | var Logger = bunyan.createLogger({ 50 | name: "my logger", 51 | streams: [ 52 | splunkStream 53 | ] 54 | }); 55 | 56 | // Define the payload to send to Splunk's Event Collector 57 | var payload = { 58 | // Our important fields 59 | temperature: "70F", 60 | chickenCount: 500, 61 | 62 | // Special keys to specify metadata for Splunk's Event Collector 63 | source: "chicken coop", 64 | sourcetype: "httpevent", 65 | index: "main", 66 | host: "farm.local" 67 | }; 68 | 69 | // Send the payload 70 | console.log("Queuing payload", payload); 71 | Logger.info(payload, "Chicken coup looks stable."); 72 | 73 | var payload2 = { 74 | // Our important fields 75 | temperature: "75F", 76 | chickenCount: 600, 77 | 78 | // Special keys to specify metadata for Splunk's Event Collector 79 | source: "chicken coop", 80 | sourcetype: "httpevent", 81 | index: "main", 82 | host: "farm.local" 83 | }; 84 | 85 | // Send the payload 86 | console.log("Queuing second payload", payload2); 87 | Logger.info(payload2, "New chickens have arrived"); 88 | 89 | /** 90 | * Since we've configured batching, we don't need 91 | * to do anything at this point. Events will 92 | * will be sent to Splunk automatically based 93 | * on the batching settings above. 94 | */ 95 | 96 | // Kill the process 97 | setTimeout(function() { 98 | console.log("Events should be in Splunk! Exiting..."); 99 | process.exit(); 100 | }, 2000); -------------------------------------------------------------------------------- /examples/basic.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * This example shows basic usage of the Splunk 19 | * Bunyan logger. 20 | */ 21 | 22 | // Change to require("splunk-bunyan-logger"); 23 | var splunkBunyan = require("../index"); 24 | var bunyan = require("bunyan"); 25 | 26 | /** 27 | * Only the token property is required. 28 | */ 29 | var config = { 30 | token: "your-token-here", 31 | url: "https://localhost:8088" 32 | }; 33 | var splunkStream = splunkBunyan.createStream(config); 34 | 35 | splunkStream.on("error", function(err, context) { 36 | // Handle errors here 37 | console.log("Error", err, "Context", context); 38 | }); 39 | 40 | // Setup Bunyan, adding splunkStream to the array of streams 41 | var Logger = bunyan.createLogger({ 42 | name: "my logger", 43 | streams: [ 44 | splunkStream 45 | ] 46 | }); 47 | 48 | // Define the payload to send to HTTP Event Collector 49 | var payload = { 50 | // Our important fields 51 | temperature: "70F", 52 | chickenCount: 500, 53 | 54 | // Special keys to specify metadata for HTTP Event Collector 55 | source: "chicken coop", 56 | sourcetype: "httpevent", 57 | index: "main", 58 | host: "farm.local" 59 | }; 60 | 61 | /** 62 | * Since maxBatchCount is set to 1 by default, calling send 63 | * will immediately send the payload. 64 | * 65 | * The underlying HTTP POST request is made to 66 | * 67 | * https://localhost:8088/services/collector/event/1.0 68 | * 69 | * with the following body 70 | * 71 | * { 72 | * "source": "chicken coop", 73 | * "sourcetype": "httpevent", 74 | * "index": "main", 75 | * "host": "farm.local", 76 | * "event": { 77 | * "message": { 78 | * "chickenCount": 500 79 | * "msg": "Chicken coup looks stable.", 80 | * "name": "my logger", 81 | * "put": 98884, 82 | * "temperature": "70F", 83 | * "v": 0 84 | * }, 85 | * "severity": "info" 86 | * } 87 | * } 88 | * 89 | */ 90 | console.log("Sending payload", payload); 91 | Logger.info(payload, "Chicken coup looks stable."); -------------------------------------------------------------------------------- /examples/custom_format.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * This example shows how to use a custom event format 19 | * for the Splunk Bunyan logger. 20 | */ 21 | 22 | // Change to require("splunk-bunyan-logger"); 23 | var splunkBunyan = require("../index"); 24 | var bunyan = require("bunyan"); 25 | 26 | /** 27 | * Only the token property is required. 28 | */ 29 | var config = { 30 | token: "your-token-here", 31 | url: "https://localhost:8088" 32 | }; 33 | var splunkStream = splunkBunyan.createStream(config); 34 | 35 | splunkStream.on("error", function(err, context) { 36 | // Handle errors here 37 | console.log("Error", err, "Context", context); 38 | }); 39 | 40 | /** 41 | * Override the default eventFormatter() function, 42 | * which takes a message and severity, returning 43 | * any type; string or object are recommended. 44 | * 45 | * The message parameter can be any type. It will 46 | * be whatever was passed to Logger.send(). 47 | * Severity will always be a string. 48 | * 49 | * In this example, we're building up a string 50 | * of key=value pairs if message is an object, 51 | * otherwise the message value is as value for 52 | * the message key. 53 | * 54 | * This string is prefixed with the event 55 | * severity in square brackets. 56 | */ 57 | splunkStream.setEventFormatter(function(message, severity) { 58 | var event = "[" + severity + "]"; 59 | 60 | if (typeof message === "object") { 61 | for (var key in message) { 62 | event += key + "=" + message[key] + " "; 63 | } 64 | } 65 | else { 66 | event += "message=" + message; 67 | } 68 | 69 | return event; 70 | }); 71 | 72 | // Setup Bunyan, adding splunkStream to the array of streams 73 | var Logger = bunyan.createLogger({ 74 | name: "my logger", 75 | streams: [ 76 | splunkStream 77 | ] 78 | }); 79 | 80 | // Define the payload to send to HTTP Event Collector 81 | var payload = { 82 | // Our important fields 83 | temperature: "70F", 84 | chickenCount: 500, 85 | 86 | // Special keys to specify metadata for HTTP Event Collector 87 | source: "chicken coop", 88 | sourcetype: "httpevent", 89 | index: "main", 90 | host: "farm.local" 91 | }; 92 | 93 | /** 94 | * Since maxBatchCount is set to 1 by default, 95 | * calling send will immediately send the payload. 96 | * 97 | * The underlying HTTP POST request is made to 98 | * 99 | * https://localhost:8088/services/collector/event/1.0 100 | * 101 | * with the following body (the pid will be different) 102 | * 103 | * { 104 | * "source": "chicken coop", 105 | * "sourcetype": "httpevent", 106 | * "index": "main", 107 | * "host": "farm.local", 108 | * "event": "[info]name=my logger pid=35265 temperature=70F chickenCount=500 msg=Chicken coup looks stable. v=0" 109 | * } 110 | * 111 | */ 112 | console.log("Sending payload", payload); 113 | Logger.info(payload, "Chicken coup looks stable."); -------------------------------------------------------------------------------- /examples/manual_batching.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * This example shows how to batch events with the 19 | * the Splunk Bunyan logger by manually calling flush. 20 | * 21 | * By setting maxbatchCount=0, events will be queued 22 | * until flush() is called. 23 | */ 24 | 25 | // Change to require("splunk-bunyan-logger"); 26 | var splunkBunyan = require("../index"); 27 | var bunyan = require("bunyan"); 28 | 29 | /** 30 | * Only the token property is required. 31 | * 32 | * Here, maxBatchCount is set to 0. 33 | */ 34 | var config = { 35 | token: "your-token-here", 36 | url: "https://localhost:8088", 37 | maxBatchCount: 0 38 | }; 39 | var splunkStream = splunkBunyan.createStream(config); 40 | 41 | splunkStream.on("error", function(err, context) { 42 | // Handle errors here 43 | console.log("Error", err, "Context", context); 44 | }); 45 | 46 | // Setup Bunyan, adding splunkStream to the array of streams 47 | var Logger = bunyan.createLogger({ 48 | name: "my logger", 49 | streams: [ 50 | splunkStream 51 | ] 52 | }); 53 | 54 | // Define the payload to send to HTTP Event Collector 55 | var payload = { 56 | // Our important fields 57 | temperature: "70F", 58 | chickenCount: 500, 59 | 60 | // Special keys to specify metadata for HTTP Event Collector 61 | source: "chicken coop", 62 | sourcetype: "httpevent", 63 | index: "main", 64 | host: "farm.local" 65 | }; 66 | 67 | // Send the payload 68 | console.log("Queuing payload", payload); 69 | Logger.info(payload, "Chicken coup looks stable."); 70 | 71 | var payload2 = { 72 | // Our important fields 73 | temperature: "75F", 74 | chickenCount: 600, 75 | 76 | // Special keys to specify metadata for HTTP Event Collector 77 | source: "chicken coop", 78 | sourcetype: "httpevent", 79 | index: "main", 80 | host: "farm.local" 81 | }; 82 | 83 | // Send the payload 84 | console.log("Queuing second payload", payload2); 85 | Logger.info(payload2, "New chickens have arrived"); 86 | 87 | /** 88 | * Call flush manually. 89 | * This will send both payloads in a single 90 | * HTTP request. 91 | * 92 | * The callback for flush is optional. 93 | */ 94 | splunkStream.flush(function(err, resp, body) { 95 | // If successful, body will be { text: 'Success', code: 0 } 96 | console.log("Response from Splunk", body); 97 | }); 98 | -------------------------------------------------------------------------------- /examples/retry.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | /** 18 | * This example shows how to configure retries on errors 19 | * with the Splunk Bunyan logger. 20 | */ 21 | 22 | // Change to require("splunk-bunyan-logger"); 23 | var splunkBunyan = require("../index"); 24 | var bunyan = require("bunyan"); 25 | 26 | /** 27 | * Only the token property is required. 28 | * 29 | * Here we've set maxRetries to 10, 30 | * If there are any connection errors the request 31 | * to Splunk will be retried up to 10 times. 32 | * The default is 0. 33 | */ 34 | var config = { 35 | token: "your-token-here", 36 | url: "https://localhost:8088", 37 | maxRetries: 10 38 | }; 39 | var splunkStream = splunkBunyan.createStream(config); 40 | 41 | splunkStream.on("error", function(err, context) { 42 | // Handle errors here 43 | console.log("Error", err, "Context", context); 44 | }); 45 | 46 | // Setup Bunyan, adding splunkStream to the array of streams 47 | var Logger = bunyan.createLogger({ 48 | name: "my logger", 49 | streams: [ 50 | splunkStream 51 | ] 52 | }); 53 | 54 | // Define the payload to send to HTTP Event Collector 55 | var payload = { 56 | // Our important fields 57 | temperature: "70F", 58 | chickenCount: 500, 59 | 60 | // Special keys to specify metadata for HTTP Event Collector 61 | source: "chicken coop", 62 | sourcetype: "httpevent", 63 | index: "main", 64 | host: "farm.local" 65 | }; 66 | 67 | // Send the payload 68 | console.log("Sending payload", payload); 69 | Logger.info(payload, "Chicken coup looks stable."); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | var Stream = require("stream").Writable; 18 | var util = require("util"); 19 | 20 | var SplunkLogging = require("splunk-logging"); 21 | var SplunkLogger = SplunkLogging.Logger; 22 | 23 | /** 24 | * A class that implements a raw writable stream. 25 | * 26 | * @property {object} config - Configuration settings for this SplunkStream instance. 27 | * @property {object[]} contextQueue - Queue of context objects to be sent to Splunk. 28 | * @property {function} error - A callback function for errors: function(err, context). 29 | * Defaults to console.log both values; 30 | * 31 | * @param {object} config - Configuration settings for a new [SplunkLogger]{@link SplunkLogger}. 32 | * @param {string} config.token - HTTP Event Collector token, required. 33 | * @param {string} [config.name=splunk-javascript-logging/0.11.1] - Name for this logger. 34 | * @param {string} [config.host=localhost] - Hostname or IP address of Splunk server. 35 | * @param {string} [config.maxRetries=0] - How many times to retry when HTTP POST to Splunk fails. 36 | * @param {string} [config.path=/services/collector/event/1.0] - URL path to send data to on the Splunk server. 37 | * @param {string} [config.protocol=https] - Protocol used to communicate with the Splunk server, http or https. 38 | * @param {number} [config.port=8088] - HTTP Event Collector port on the Splunk server. 39 | * @param {string} [config.url] - URL string to pass to {@link https://nodejs.org/api/url.html#url_url_parsing|url.parse}. This will try to set 40 | * host, path, protocol, port, url. Any of these values will be overwritten if 41 | * the corresponding property is set on config. 42 | * @param {string} [config.level=info] - Logging level to use, will show up as the severity field of an event, see 43 | * [SplunkLogger.levels]{@link SplunkLogger#levels} for common levels. 44 | * @param {number} [config.batchInterval=0] - Automatically flush events after this many milliseconds. 45 | * When set to a non-positive value, events will be sent one by one. This setting is ignored when non-positive. 46 | * @param {number} [config.maxBatchSize=0] - Automatically flush events after the size of queued 47 | * events exceeds this many bytes. This setting is ignored when non-positive. 48 | * @param {number} [config.maxBatchCount=1] - Automatically flush events after this many 49 | * events have been queued. Defaults to flush immediately on sending an event. This setting is ignored when non-positive. 50 | * @constructor 51 | * @implements {@link https://nodejs.org/api/stream.html#stream_class_stream_writable|Stream.Writable} 52 | */ 53 | var SplunkStream = function (config) { 54 | /** @type {SplunkLogger} */ 55 | this.logger = new SplunkLogger(config); 56 | 57 | // If using the common logger's default name, change it 58 | if (this.logger.config.name.match("splunk-javascript-logging/\\d+\\.\\d+\\.\\d+")) { 59 | this.logger.config.name = "splunk-bunyan-logger/0.11.0"; 60 | } 61 | 62 | // Overwrite the common library's error callback 63 | var that = this; 64 | this.logger.error = function(err, context) { 65 | try { 66 | that.emit("error", err, context); 67 | } catch(e) { 68 | console.log(e); 69 | } 70 | }; 71 | 72 | /* jshint unused:false */ 73 | /** 74 | * A callback function called after sending a request to Splunk: 75 | * function(err, response, body). Defaults 76 | * to an empty function. 77 | * 78 | * @type {function} 79 | */ 80 | this.send = function(err, resp, body) {}; 81 | }; 82 | util.inherits(SplunkStream, Stream); 83 | 84 | /** 85 | * Returns the configuration for this logger. 86 | * See {@link SplunkStream}. 87 | * 88 | * @returns {Object} Configuration for this logger. 89 | * @public 90 | */ 91 | SplunkStream.prototype.config = function() { 92 | return this.logger.config; 93 | }; 94 | 95 | /** 96 | * The write() function for SplunkStream. 97 | * 98 | * Bunyan will call this function when a user logs a message. 99 | * See [Bunyan raw streams]{@link https://github.com/trentm/node-bunyan#stream-type-raw}. 100 | * 101 | * @param {object} data - The data object is provided by Bunyan. 102 | * @public 103 | */ 104 | SplunkStream.prototype.write = function (data) { 105 | if (!data) { 106 | this.emit("error", new Error("Must pass a parameter to write.")); 107 | return; 108 | } 109 | 110 | var context = { 111 | message: data, 112 | severity: module.exports.severityFromLevel(data.level), 113 | metadata: { 114 | time: data.time, 115 | host: data.hostname 116 | } 117 | }; 118 | 119 | // Remove properties already added to the context 120 | delete context.message.level; 121 | delete context.message.time; 122 | delete context.message.hostname; 123 | 124 | // Clean up any existing metadata 125 | if (data.hasOwnProperty("host")) { 126 | context.metadata.host = data.host; 127 | delete data.host; 128 | } 129 | if (data.hasOwnProperty("source")) { 130 | context.metadata.source = data.source; 131 | delete data.source; 132 | } 133 | if (data.hasOwnProperty("sourcetype")) { 134 | context.metadata.sourcetype = data.sourcetype; 135 | delete data.sourcetype; 136 | } 137 | if (data.hasOwnProperty("index")) { 138 | context.metadata.index = data.index; 139 | delete data.index; 140 | } 141 | 142 | var that = this; 143 | this.logger.send(context, that.send); 144 | }; 145 | 146 | /** 147 | * Splunk Bunyan Logger module, to be used with [Bunyan]{@link https://www.npmjs.com/package/bunyan}. 148 | * 149 | * See {@link https://github.com/splunk/splunk-bunyan-logger/tree/master/examples|examples} for usage. 150 | * 151 | * @module SplunkBunyanLogger 152 | * @namespace SplunkBunyanLogger 153 | */ 154 | module.exports = { 155 | /** 156 | * Bunyan's logging levels. 157 | * 158 | * @default info 159 | * @readonly 160 | * @enum {string} 161 | * @memberof SplunkBunyanLogger 162 | */ 163 | levels: { 164 | TRACE: "trace", 165 | DEBUG: "debug", 166 | INFO: "info", 167 | WARN: "warn", 168 | ERROR: "error", 169 | FATAL: "fatal" 170 | }, 171 | /** 172 | * Translates a Bunyan logging level number to the name of the level. 173 | * 174 | * @param {number} level - A Bunyan logging level integer. See {@link SplunkBunyanLogger.levels} 175 | * @returns {string} 176 | * @memberof SplunkBunyanLogger 177 | */ 178 | severityFromLevel: function (level) { 179 | switch(level) { 180 | case 10: 181 | return module.exports.levels.TRACE; 182 | case 20: 183 | return module.exports.levels.DEBUG; 184 | case 40: 185 | return module.exports.levels.WARN; 186 | case 50: 187 | return module.exports.levels.ERROR; 188 | case 60: 189 | return module.exports.levels.FATAL; 190 | default: 191 | return module.exports.levels.INFO; 192 | } 193 | }, 194 | 195 | /** 196 | * Creates a Bunyan Stream object with the provided config. 197 | * 198 | * @param {object} config - See {@link SplunkStream}. 199 | * @returns {SplunkBunyanStream} A Bunyan Stream object. 200 | * @memberof SplunkBunyanLogger 201 | */ 202 | createStream: function (config) { 203 | var stream = new SplunkStream(config); 204 | /** 205 | * @typedef SplunkBunyanStream 206 | * @property {string} level The logging level for Bunyan. 207 | * @property {string} type Always raw. 208 | * @property {function} on Takes an event string, and a callback function. 209 | * The most useful event to listen for is error. 210 | * See {@link https://nodejs.org/api/events.html#events_emitter_on_event_listener|Node.js events} documentation. 211 | * @property {function} setEventFormatter Overrides the eventFormatter for the underlying SplunkLogger. 212 | * Takes a callback function parameter: function(message, severity), where message 213 | * is an object, and severity is a string. 214 | * @property {function} on Adds a listener to to the SplunkStream object, typically used for the error event. 215 | * @property {function} flush Manually sends all queued events to Splunk in a single HTTP request. 216 | * Takes a callback function parameter: function(err, response, body). 217 | * @property {SplunkStream} stream See {@link SplunkStream} 218 | */ 219 | return { 220 | level: config.level || module.exports.levels.INFO, 221 | type: "raw", 222 | setEventFormatter: function(formatter) { 223 | this.stream.logger.eventFormatter = formatter; 224 | }, 225 | on: function(event, callback) { 226 | this.stream.on(event, callback); 227 | }, 228 | flush: function(callback) { 229 | // If flush is called with no param, use the send() callback 230 | callback = callback || this.stream.send; 231 | this.stream.logger.flush(callback); 232 | }, 233 | stream: stream 234 | }; 235 | } 236 | }; -------------------------------------------------------------------------------- /licenses/LICENSE-SPLUNK-LOGGING: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "splunk-bunyan-logger", 3 | "version": "0.10.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abbrev": { 8 | "version": "1.0.9", 9 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", 10 | "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=", 11 | "dev": true 12 | }, 13 | "ajv": { 14 | "version": "5.5.2", 15 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 16 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 17 | "requires": { 18 | "co": "^4.6.0", 19 | "fast-deep-equal": "^1.0.0", 20 | "fast-json-stable-stringify": "^2.0.0", 21 | "json-schema-traverse": "^0.3.0" 22 | } 23 | }, 24 | "amdefine": { 25 | "version": "1.0.1", 26 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 27 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 28 | "dev": true, 29 | "optional": true 30 | }, 31 | "argparse": { 32 | "version": "1.0.10", 33 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", 34 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", 35 | "dev": true, 36 | "requires": { 37 | "sprintf-js": "~1.0.2" 38 | } 39 | }, 40 | "asn1": { 41 | "version": "0.2.4", 42 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", 43 | "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", 44 | "requires": { 45 | "safer-buffer": "~2.1.0" 46 | } 47 | }, 48 | "assert-plus": { 49 | "version": "1.0.0", 50 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 51 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 52 | }, 53 | "async": { 54 | "version": "1.5.2", 55 | "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", 56 | "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", 57 | "dev": true 58 | }, 59 | "asynckit": { 60 | "version": "0.4.0", 61 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 62 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 63 | }, 64 | "aws-sign2": { 65 | "version": "0.7.0", 66 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 67 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 68 | }, 69 | "aws4": { 70 | "version": "1.8.0", 71 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", 72 | "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" 73 | }, 74 | "babylon": { 75 | "version": "7.0.0-beta.19", 76 | "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", 77 | "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==", 78 | "dev": true 79 | }, 80 | "balanced-match": { 81 | "version": "0.4.2", 82 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", 83 | "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", 84 | "dev": true 85 | }, 86 | "bcrypt-pbkdf": { 87 | "version": "1.0.2", 88 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", 89 | "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", 90 | "requires": { 91 | "tweetnacl": "^0.14.3" 92 | } 93 | }, 94 | "bluebird": { 95 | "version": "3.5.2", 96 | "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", 97 | "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", 98 | "dev": true 99 | }, 100 | "brace-expansion": { 101 | "version": "1.1.7", 102 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", 103 | "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", 104 | "dev": true, 105 | "requires": { 106 | "balanced-match": "^0.4.1", 107 | "concat-map": "0.0.1" 108 | } 109 | }, 110 | "browser-stdout": { 111 | "version": "1.3.1", 112 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 113 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 114 | "dev": true 115 | }, 116 | "buffer-from": { 117 | "version": "1.1.1", 118 | "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", 119 | "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", 120 | "dev": true, 121 | "optional": true 122 | }, 123 | "bunyan": { 124 | "version": "1.4.0", 125 | "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.4.0.tgz", 126 | "integrity": "sha1-tuZqzntt14gPQUpbje7H4nPuxec=", 127 | "dev": true, 128 | "requires": { 129 | "dtrace-provider": "~0.5", 130 | "mv": "~2", 131 | "safe-json-stringify": "~1" 132 | } 133 | }, 134 | "caseless": { 135 | "version": "0.12.0", 136 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 137 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 138 | }, 139 | "catharsis": { 140 | "version": "0.8.9", 141 | "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", 142 | "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", 143 | "dev": true, 144 | "requires": { 145 | "underscore-contrib": "~0.3.0" 146 | } 147 | }, 148 | "cli": { 149 | "version": "1.0.1", 150 | "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", 151 | "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", 152 | "dev": true, 153 | "requires": { 154 | "exit": "0.1.2", 155 | "glob": "^7.1.1" 156 | }, 157 | "dependencies": { 158 | "glob": { 159 | "version": "7.1.3", 160 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", 161 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", 162 | "dev": true, 163 | "requires": { 164 | "fs.realpath": "^1.0.0", 165 | "inflight": "^1.0.4", 166 | "inherits": "2", 167 | "minimatch": "^3.0.4", 168 | "once": "^1.3.0", 169 | "path-is-absolute": "^1.0.0" 170 | } 171 | }, 172 | "minimatch": { 173 | "version": "3.0.4", 174 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 175 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 176 | "dev": true, 177 | "requires": { 178 | "brace-expansion": "^1.1.7" 179 | } 180 | } 181 | } 182 | }, 183 | "co": { 184 | "version": "4.6.0", 185 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 186 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 187 | }, 188 | "colors": { 189 | "version": "1.0.3", 190 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", 191 | "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", 192 | "dev": true, 193 | "optional": true 194 | }, 195 | "combined-stream": { 196 | "version": "1.0.7", 197 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", 198 | "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", 199 | "requires": { 200 | "delayed-stream": "~1.0.0" 201 | } 202 | }, 203 | "commander": { 204 | "version": "2.15.1", 205 | "resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 206 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 207 | "dev": true 208 | }, 209 | "concat-map": { 210 | "version": "0.0.1", 211 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 212 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 213 | "dev": true 214 | }, 215 | "concat-stream": { 216 | "version": "1.6.2", 217 | "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", 218 | "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", 219 | "dev": true, 220 | "optional": true, 221 | "requires": { 222 | "buffer-from": "^1.0.0", 223 | "inherits": "^2.0.3", 224 | "readable-stream": "^2.2.2", 225 | "typedarray": "^0.0.6" 226 | }, 227 | "dependencies": { 228 | "isarray": { 229 | "version": "1.0.0", 230 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 231 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 232 | "dev": true, 233 | "optional": true 234 | }, 235 | "readable-stream": { 236 | "version": "2.3.6", 237 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", 238 | "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", 239 | "dev": true, 240 | "optional": true, 241 | "requires": { 242 | "core-util-is": "~1.0.0", 243 | "inherits": "~2.0.3", 244 | "isarray": "~1.0.0", 245 | "process-nextick-args": "~2.0.0", 246 | "safe-buffer": "~5.1.1", 247 | "string_decoder": "~1.1.1", 248 | "util-deprecate": "~1.0.1" 249 | } 250 | }, 251 | "string_decoder": { 252 | "version": "1.1.1", 253 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 254 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 255 | "dev": true, 256 | "optional": true, 257 | "requires": { 258 | "safe-buffer": "~5.1.0" 259 | } 260 | } 261 | } 262 | }, 263 | "console-browserify": { 264 | "version": "1.1.0", 265 | "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", 266 | "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", 267 | "dev": true, 268 | "requires": { 269 | "date-now": "^0.1.4" 270 | } 271 | }, 272 | "core-util-is": { 273 | "version": "1.0.2", 274 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 275 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 276 | }, 277 | "cycle": { 278 | "version": "1.0.3", 279 | "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", 280 | "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=", 281 | "dev": true, 282 | "optional": true 283 | }, 284 | "dashdash": { 285 | "version": "1.14.1", 286 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 287 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 288 | "requires": { 289 | "assert-plus": "^1.0.0" 290 | } 291 | }, 292 | "date-now": { 293 | "version": "0.1.4", 294 | "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", 295 | "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", 296 | "dev": true 297 | }, 298 | "debug": { 299 | "version": "3.1.0", 300 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 301 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 302 | "dev": true, 303 | "requires": { 304 | "ms": "2.0.0" 305 | } 306 | }, 307 | "deep-is": { 308 | "version": "0.1.3", 309 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 310 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 311 | "dev": true 312 | }, 313 | "delayed-stream": { 314 | "version": "1.0.0", 315 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 316 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 317 | }, 318 | "diff": { 319 | "version": "3.5.0", 320 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 321 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 322 | "dev": true 323 | }, 324 | "dom-serializer": { 325 | "version": "0.1.0", 326 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.0.tgz", 327 | "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", 328 | "dev": true, 329 | "requires": { 330 | "domelementtype": "~1.1.1", 331 | "entities": "~1.1.1" 332 | }, 333 | "dependencies": { 334 | "domelementtype": { 335 | "version": "1.1.3", 336 | "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", 337 | "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", 338 | "dev": true 339 | }, 340 | "entities": { 341 | "version": "1.1.2", 342 | "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", 343 | "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", 344 | "dev": true 345 | } 346 | } 347 | }, 348 | "domelementtype": { 349 | "version": "1.2.1", 350 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.2.1.tgz", 351 | "integrity": "sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA==", 352 | "dev": true 353 | }, 354 | "domhandler": { 355 | "version": "2.3.0", 356 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", 357 | "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", 358 | "dev": true, 359 | "requires": { 360 | "domelementtype": "1" 361 | } 362 | }, 363 | "domutils": { 364 | "version": "1.5.1", 365 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", 366 | "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", 367 | "dev": true, 368 | "requires": { 369 | "dom-serializer": "0", 370 | "domelementtype": "1" 371 | } 372 | }, 373 | "dtrace-provider": { 374 | "version": "0.5.0", 375 | "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.5.0.tgz", 376 | "integrity": "sha1-P9263Rga7YP8iJpTXFcD9U5Vn+o=", 377 | "dev": true, 378 | "optional": true, 379 | "requires": { 380 | "nan": "~1.8.4" 381 | } 382 | }, 383 | "ecc-jsbn": { 384 | "version": "0.1.2", 385 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", 386 | "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", 387 | "requires": { 388 | "jsbn": "~0.1.0", 389 | "safer-buffer": "^2.1.0" 390 | } 391 | }, 392 | "entities": { 393 | "version": "1.0.0", 394 | "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", 395 | "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", 396 | "dev": true 397 | }, 398 | "es6-promise": { 399 | "version": "4.2.5", 400 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", 401 | "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", 402 | "dev": true, 403 | "optional": true 404 | }, 405 | "escape-string-regexp": { 406 | "version": "1.0.5", 407 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 408 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 409 | "dev": true 410 | }, 411 | "escodegen": { 412 | "version": "1.8.1", 413 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", 414 | "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", 415 | "dev": true, 416 | "requires": { 417 | "esprima": "^2.7.1", 418 | "estraverse": "^1.9.1", 419 | "esutils": "^2.0.2", 420 | "optionator": "^0.8.1", 421 | "source-map": "~0.2.0" 422 | } 423 | }, 424 | "esprima": { 425 | "version": "2.7.3", 426 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", 427 | "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", 428 | "dev": true 429 | }, 430 | "estraverse": { 431 | "version": "1.9.3", 432 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", 433 | "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", 434 | "dev": true 435 | }, 436 | "esutils": { 437 | "version": "2.0.2", 438 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 439 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 440 | "dev": true 441 | }, 442 | "exit": { 443 | "version": "0.1.2", 444 | "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", 445 | "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", 446 | "dev": true 447 | }, 448 | "extend": { 449 | "version": "3.0.2", 450 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 451 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" 452 | }, 453 | "extract-zip": { 454 | "version": "1.6.7", 455 | "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.7.tgz", 456 | "integrity": "sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k=", 457 | "dev": true, 458 | "optional": true, 459 | "requires": { 460 | "concat-stream": "1.6.2", 461 | "debug": "2.6.9", 462 | "mkdirp": "0.5.1", 463 | "yauzl": "2.4.1" 464 | }, 465 | "dependencies": { 466 | "debug": { 467 | "version": "2.6.9", 468 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 469 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 470 | "dev": true, 471 | "optional": true, 472 | "requires": { 473 | "ms": "2.0.0" 474 | } 475 | }, 476 | "ms": { 477 | "version": "2.0.0", 478 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 479 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 480 | "dev": true, 481 | "optional": true 482 | } 483 | } 484 | }, 485 | "extsprintf": { 486 | "version": "1.3.0", 487 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 488 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 489 | }, 490 | "eyes": { 491 | "version": "0.1.8", 492 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 493 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=", 494 | "dev": true, 495 | "optional": true 496 | }, 497 | "fast-deep-equal": { 498 | "version": "1.1.0", 499 | "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 500 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" 501 | }, 502 | "fast-json-stable-stringify": { 503 | "version": "2.0.0", 504 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 505 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 506 | }, 507 | "fast-levenshtein": { 508 | "version": "2.0.6", 509 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 510 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 511 | "dev": true 512 | }, 513 | "fd-slicer": { 514 | "version": "1.0.1", 515 | "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", 516 | "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", 517 | "dev": true, 518 | "optional": true, 519 | "requires": { 520 | "pend": "~1.2.0" 521 | } 522 | }, 523 | "forever-agent": { 524 | "version": "0.6.1", 525 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 526 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 527 | }, 528 | "form-data": { 529 | "version": "2.3.3", 530 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", 531 | "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", 532 | "requires": { 533 | "asynckit": "^0.4.0", 534 | "combined-stream": "^1.0.6", 535 | "mime-types": "^2.1.12" 536 | } 537 | }, 538 | "fs-extra": { 539 | "version": "1.0.0", 540 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", 541 | "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", 542 | "dev": true, 543 | "optional": true, 544 | "requires": { 545 | "graceful-fs": "^4.1.2", 546 | "jsonfile": "^2.1.0", 547 | "klaw": "^1.0.0" 548 | }, 549 | "dependencies": { 550 | "graceful-fs": { 551 | "version": "4.1.11", 552 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 553 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 554 | "dev": true, 555 | "optional": true 556 | } 557 | } 558 | }, 559 | "fs.realpath": { 560 | "version": "1.0.0", 561 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 562 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 563 | "dev": true 564 | }, 565 | "getpass": { 566 | "version": "0.1.7", 567 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 568 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 569 | "requires": { 570 | "assert-plus": "^1.0.0" 571 | } 572 | }, 573 | "glob": { 574 | "version": "6.0.4", 575 | "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", 576 | "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", 577 | "dev": true, 578 | "optional": true, 579 | "requires": { 580 | "inflight": "^1.0.4", 581 | "inherits": "2", 582 | "minimatch": "2 || 3", 583 | "once": "^1.3.0", 584 | "path-is-absolute": "^1.0.0" 585 | } 586 | }, 587 | "graceful-fs": { 588 | "version": "4.1.11", 589 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 590 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 591 | "dev": true 592 | }, 593 | "growl": { 594 | "version": "1.10.5", 595 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 596 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 597 | "dev": true 598 | }, 599 | "handlebars": { 600 | "version": "4.0.12", 601 | "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.12.tgz", 602 | "integrity": "sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==", 603 | "dev": true, 604 | "requires": { 605 | "async": "^2.5.0", 606 | "optimist": "^0.6.1", 607 | "source-map": "^0.6.1", 608 | "uglify-js": "^3.1.4" 609 | }, 610 | "dependencies": { 611 | "async": { 612 | "version": "2.6.1", 613 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", 614 | "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", 615 | "dev": true, 616 | "requires": { 617 | "lodash": "^4.17.10" 618 | } 619 | }, 620 | "source-map": { 621 | "version": "0.6.1", 622 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 623 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 624 | "dev": true 625 | } 626 | } 627 | }, 628 | "har-schema": { 629 | "version": "2.0.0", 630 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 631 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 632 | }, 633 | "har-validator": { 634 | "version": "5.1.0", 635 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", 636 | "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", 637 | "requires": { 638 | "ajv": "^5.3.0", 639 | "har-schema": "^2.0.0" 640 | } 641 | }, 642 | "has-flag": { 643 | "version": "1.0.0", 644 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 645 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 646 | "dev": true 647 | }, 648 | "hasha": { 649 | "version": "2.2.0", 650 | "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", 651 | "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", 652 | "dev": true, 653 | "optional": true, 654 | "requires": { 655 | "is-stream": "^1.0.1", 656 | "pinkie-promise": "^2.0.0" 657 | } 658 | }, 659 | "he": { 660 | "version": "1.1.1", 661 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 662 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 663 | "dev": true 664 | }, 665 | "htmlparser2": { 666 | "version": "3.8.3", 667 | "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", 668 | "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", 669 | "dev": true, 670 | "requires": { 671 | "domelementtype": "1", 672 | "domhandler": "2.3", 673 | "domutils": "1.5", 674 | "entities": "1.0", 675 | "readable-stream": "1.1" 676 | } 677 | }, 678 | "http-signature": { 679 | "version": "1.2.0", 680 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 681 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 682 | "requires": { 683 | "assert-plus": "^1.0.0", 684 | "jsprim": "^1.2.2", 685 | "sshpk": "^1.7.0" 686 | } 687 | }, 688 | "inflight": { 689 | "version": "1.0.6", 690 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 691 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 692 | "dev": true, 693 | "requires": { 694 | "once": "^1.3.0", 695 | "wrappy": "1" 696 | } 697 | }, 698 | "inherits": { 699 | "version": "2.0.3", 700 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 701 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 702 | "dev": true 703 | }, 704 | "is-stream": { 705 | "version": "1.1.0", 706 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", 707 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", 708 | "dev": true, 709 | "optional": true 710 | }, 711 | "is-typedarray": { 712 | "version": "1.0.0", 713 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 714 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 715 | }, 716 | "isarray": { 717 | "version": "0.0.1", 718 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 719 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 720 | "dev": true 721 | }, 722 | "isexe": { 723 | "version": "2.0.0", 724 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 725 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", 726 | "dev": true 727 | }, 728 | "isstream": { 729 | "version": "0.1.2", 730 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 731 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 732 | }, 733 | "istanbul": { 734 | "version": "0.4.5", 735 | "resolved": "https://registry.npmjs.org/istanbul/-/istanbul-0.4.5.tgz", 736 | "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", 737 | "dev": true, 738 | "requires": { 739 | "abbrev": "1.0.x", 740 | "async": "1.x", 741 | "escodegen": "1.8.x", 742 | "esprima": "2.7.x", 743 | "glob": "^5.0.15", 744 | "handlebars": "^4.0.1", 745 | "js-yaml": "3.x", 746 | "mkdirp": "0.5.x", 747 | "nopt": "3.x", 748 | "once": "1.x", 749 | "resolve": "1.1.x", 750 | "supports-color": "^3.1.0", 751 | "which": "^1.1.1", 752 | "wordwrap": "^1.0.0" 753 | }, 754 | "dependencies": { 755 | "glob": { 756 | "version": "5.0.15", 757 | "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", 758 | "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", 759 | "dev": true, 760 | "requires": { 761 | "inflight": "^1.0.4", 762 | "inherits": "2", 763 | "minimatch": "2 || 3", 764 | "once": "^1.3.0", 765 | "path-is-absolute": "^1.0.0" 766 | } 767 | } 768 | } 769 | }, 770 | "js-yaml": { 771 | "version": "3.12.0", 772 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", 773 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", 774 | "dev": true, 775 | "requires": { 776 | "argparse": "^1.0.7", 777 | "esprima": "^4.0.0" 778 | }, 779 | "dependencies": { 780 | "esprima": { 781 | "version": "4.0.1", 782 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", 783 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", 784 | "dev": true 785 | } 786 | } 787 | }, 788 | "js2xmlparser": { 789 | "version": "3.0.0", 790 | "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", 791 | "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", 792 | "dev": true, 793 | "requires": { 794 | "xmlcreate": "^1.0.1" 795 | } 796 | }, 797 | "jsbn": { 798 | "version": "0.1.1", 799 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 800 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" 801 | }, 802 | "jsdoc": { 803 | "version": "3.5.5", 804 | "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", 805 | "integrity": "sha1-SEUhsSboGQTWMv+D7JqqCWcI+k0=", 806 | "dev": true, 807 | "requires": { 808 | "babylon": "7.0.0-beta.19", 809 | "bluebird": "~3.5.0", 810 | "catharsis": "~0.8.9", 811 | "escape-string-regexp": "~1.0.5", 812 | "js2xmlparser": "~3.0.0", 813 | "klaw": "~2.0.0", 814 | "marked": "~0.3.6", 815 | "mkdirp": "~0.5.1", 816 | "requizzle": "~0.2.1", 817 | "strip-json-comments": "~2.0.1", 818 | "taffydb": "2.6.2", 819 | "underscore": "~1.8.3" 820 | }, 821 | "dependencies": { 822 | "klaw": { 823 | "version": "2.0.0", 824 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", 825 | "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", 826 | "dev": true, 827 | "requires": { 828 | "graceful-fs": "^4.1.9" 829 | } 830 | }, 831 | "strip-json-comments": { 832 | "version": "2.0.1", 833 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 834 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", 835 | "dev": true 836 | } 837 | } 838 | }, 839 | "jshint": { 840 | "version": "2.9.6", 841 | "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.9.6.tgz", 842 | "integrity": "sha1-GbNOV4CVo0ko/gBhNabLcBN7nAg=", 843 | "dev": true, 844 | "requires": { 845 | "cli": "~1.0.0", 846 | "console-browserify": "1.1.x", 847 | "exit": "0.1.x", 848 | "htmlparser2": "3.8.x", 849 | "lodash": "~4.17.10", 850 | "minimatch": "~3.0.2", 851 | "phantom": "~4.0.1", 852 | "phantomjs-prebuilt": "~2.1.7", 853 | "shelljs": "0.3.x", 854 | "strip-json-comments": "1.0.x", 855 | "unicode-5.2.0": "^0.7.5" 856 | } 857 | }, 858 | "json-schema": { 859 | "version": "0.2.3", 860 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 861 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 862 | }, 863 | "json-schema-traverse": { 864 | "version": "0.3.1", 865 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 866 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 867 | }, 868 | "json-stringify-safe": { 869 | "version": "5.0.1", 870 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 871 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 872 | }, 873 | "jsonfile": { 874 | "version": "2.4.0", 875 | "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", 876 | "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", 877 | "dev": true, 878 | "optional": true, 879 | "requires": { 880 | "graceful-fs": "^4.1.6" 881 | }, 882 | "dependencies": { 883 | "graceful-fs": { 884 | "version": "4.1.11", 885 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 886 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 887 | "dev": true, 888 | "optional": true 889 | } 890 | } 891 | }, 892 | "jsprim": { 893 | "version": "1.4.1", 894 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 895 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 896 | "requires": { 897 | "assert-plus": "1.0.0", 898 | "extsprintf": "1.3.0", 899 | "json-schema": "0.2.3", 900 | "verror": "1.10.0" 901 | } 902 | }, 903 | "kew": { 904 | "version": "0.7.0", 905 | "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", 906 | "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", 907 | "dev": true, 908 | "optional": true 909 | }, 910 | "klaw": { 911 | "version": "1.3.1", 912 | "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", 913 | "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", 914 | "dev": true, 915 | "optional": true, 916 | "requires": { 917 | "graceful-fs": "^4.1.9" 918 | }, 919 | "dependencies": { 920 | "graceful-fs": { 921 | "version": "4.1.11", 922 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", 923 | "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", 924 | "dev": true, 925 | "optional": true 926 | } 927 | } 928 | }, 929 | "levn": { 930 | "version": "0.3.0", 931 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 932 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 933 | "dev": true, 934 | "requires": { 935 | "prelude-ls": "~1.1.2", 936 | "type-check": "~0.3.2" 937 | } 938 | }, 939 | "lodash": { 940 | "version": "4.17.11", 941 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", 942 | "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", 943 | "dev": true 944 | }, 945 | "marked": { 946 | "version": "0.3.19", 947 | "resolved": "http://registry.npmjs.org/marked/-/marked-0.3.19.tgz", 948 | "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", 949 | "dev": true 950 | }, 951 | "mime-db": { 952 | "version": "1.37.0", 953 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", 954 | "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" 955 | }, 956 | "mime-types": { 957 | "version": "2.1.21", 958 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", 959 | "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", 960 | "requires": { 961 | "mime-db": "~1.37.0" 962 | } 963 | }, 964 | "minimatch": { 965 | "version": "3.0.3", 966 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz", 967 | "integrity": "sha1-Kk5AkLlrLbBqnX3wEFWmKnfJt3Q=", 968 | "dev": true, 969 | "requires": { 970 | "brace-expansion": "^1.0.0" 971 | } 972 | }, 973 | "minimist": { 974 | "version": "0.0.8", 975 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 976 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 977 | "dev": true 978 | }, 979 | "mkdirp": { 980 | "version": "0.5.1", 981 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 982 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 983 | "dev": true, 984 | "requires": { 985 | "minimist": "0.0.8" 986 | } 987 | }, 988 | "mocha": { 989 | "version": "5.2.0", 990 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 991 | "integrity": "sha1-bYrlCPWRZ/lA8rWzxKYSrlDJCuY=", 992 | "dev": true, 993 | "requires": { 994 | "browser-stdout": "1.3.1", 995 | "commander": "2.15.1", 996 | "debug": "3.1.0", 997 | "diff": "3.5.0", 998 | "escape-string-regexp": "1.0.5", 999 | "glob": "7.1.2", 1000 | "growl": "1.10.5", 1001 | "he": "1.1.1", 1002 | "minimatch": "3.0.4", 1003 | "mkdirp": "0.5.1", 1004 | "supports-color": "5.4.0" 1005 | }, 1006 | "dependencies": { 1007 | "glob": { 1008 | "version": "7.1.2", 1009 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 1010 | "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", 1011 | "dev": true, 1012 | "requires": { 1013 | "fs.realpath": "^1.0.0", 1014 | "inflight": "^1.0.4", 1015 | "inherits": "2", 1016 | "minimatch": "^3.0.4", 1017 | "once": "^1.3.0", 1018 | "path-is-absolute": "^1.0.0" 1019 | } 1020 | }, 1021 | "has-flag": { 1022 | "version": "3.0.0", 1023 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 1024 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 1025 | "dev": true 1026 | }, 1027 | "minimatch": { 1028 | "version": "3.0.4", 1029 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 1030 | "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", 1031 | "dev": true, 1032 | "requires": { 1033 | "brace-expansion": "^1.1.7" 1034 | } 1035 | }, 1036 | "supports-color": { 1037 | "version": "5.4.0", 1038 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 1039 | "integrity": "sha1-HGszdALCE3YF7+GfEP7DkPb6q1Q=", 1040 | "dev": true, 1041 | "requires": { 1042 | "has-flag": "^3.0.0" 1043 | } 1044 | } 1045 | } 1046 | }, 1047 | "ms": { 1048 | "version": "2.0.0", 1049 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1050 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 1051 | "dev": true 1052 | }, 1053 | "mv": { 1054 | "version": "2.1.1", 1055 | "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", 1056 | "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", 1057 | "dev": true, 1058 | "optional": true, 1059 | "requires": { 1060 | "mkdirp": "~0.5.1", 1061 | "ncp": "~2.0.0", 1062 | "rimraf": "~2.4.0" 1063 | } 1064 | }, 1065 | "nan": { 1066 | "version": "1.8.4", 1067 | "resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz", 1068 | "integrity": "sha1-PHa1OC6rM+RLdY0oE8qdkuk0LzQ=", 1069 | "dev": true, 1070 | "optional": true 1071 | }, 1072 | "ncp": { 1073 | "version": "2.0.0", 1074 | "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", 1075 | "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", 1076 | "dev": true, 1077 | "optional": true 1078 | }, 1079 | "nopt": { 1080 | "version": "3.0.6", 1081 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", 1082 | "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", 1083 | "dev": true, 1084 | "requires": { 1085 | "abbrev": "1" 1086 | } 1087 | }, 1088 | "oauth-sign": { 1089 | "version": "0.9.0", 1090 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", 1091 | "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" 1092 | }, 1093 | "once": { 1094 | "version": "1.4.0", 1095 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1096 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 1097 | "dev": true, 1098 | "requires": { 1099 | "wrappy": "1" 1100 | } 1101 | }, 1102 | "optimist": { 1103 | "version": "0.6.1", 1104 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", 1105 | "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", 1106 | "dev": true, 1107 | "requires": { 1108 | "minimist": "~0.0.1", 1109 | "wordwrap": "~0.0.2" 1110 | }, 1111 | "dependencies": { 1112 | "wordwrap": { 1113 | "version": "0.0.3", 1114 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", 1115 | "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", 1116 | "dev": true 1117 | } 1118 | } 1119 | }, 1120 | "optionator": { 1121 | "version": "0.8.2", 1122 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 1123 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 1124 | "dev": true, 1125 | "requires": { 1126 | "deep-is": "~0.1.3", 1127 | "fast-levenshtein": "~2.0.4", 1128 | "levn": "~0.3.0", 1129 | "prelude-ls": "~1.1.2", 1130 | "type-check": "~0.3.2", 1131 | "wordwrap": "~1.0.0" 1132 | } 1133 | }, 1134 | "path-is-absolute": { 1135 | "version": "1.0.1", 1136 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1137 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 1138 | "dev": true 1139 | }, 1140 | "pend": { 1141 | "version": "1.2.0", 1142 | "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", 1143 | "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", 1144 | "dev": true, 1145 | "optional": true 1146 | }, 1147 | "performance-now": { 1148 | "version": "2.1.0", 1149 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 1150 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 1151 | }, 1152 | "phantom": { 1153 | "version": "4.0.12", 1154 | "resolved": "https://registry.npmjs.org/phantom/-/phantom-4.0.12.tgz", 1155 | "integrity": "sha512-Tz82XhtPmwCk1FFPmecy7yRGZG2btpzY2KI9fcoPT7zT9det0CcMyfBFPp1S8DqzsnQnm8ZYEfdy528mwVtksA==", 1156 | "dev": true, 1157 | "optional": true, 1158 | "requires": { 1159 | "phantomjs-prebuilt": "^2.1.16", 1160 | "split": "^1.0.1", 1161 | "winston": "^2.4.0" 1162 | } 1163 | }, 1164 | "phantomjs-prebuilt": { 1165 | "version": "2.1.16", 1166 | "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz", 1167 | "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", 1168 | "dev": true, 1169 | "optional": true, 1170 | "requires": { 1171 | "es6-promise": "^4.0.3", 1172 | "extract-zip": "^1.6.5", 1173 | "fs-extra": "^1.0.0", 1174 | "hasha": "^2.2.0", 1175 | "kew": "^0.7.0", 1176 | "progress": "^1.1.8", 1177 | "request": "^2.81.0", 1178 | "request-progress": "^2.0.1", 1179 | "which": "^1.2.10" 1180 | } 1181 | }, 1182 | "pinkie": { 1183 | "version": "2.0.4", 1184 | "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", 1185 | "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", 1186 | "dev": true, 1187 | "optional": true 1188 | }, 1189 | "pinkie-promise": { 1190 | "version": "2.0.1", 1191 | "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", 1192 | "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", 1193 | "dev": true, 1194 | "optional": true, 1195 | "requires": { 1196 | "pinkie": "^2.0.0" 1197 | } 1198 | }, 1199 | "prelude-ls": { 1200 | "version": "1.1.2", 1201 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 1202 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 1203 | "dev": true 1204 | }, 1205 | "process-nextick-args": { 1206 | "version": "2.0.0", 1207 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", 1208 | "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", 1209 | "dev": true, 1210 | "optional": true 1211 | }, 1212 | "progress": { 1213 | "version": "1.1.8", 1214 | "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", 1215 | "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", 1216 | "dev": true, 1217 | "optional": true 1218 | }, 1219 | "psl": { 1220 | "version": "1.1.29", 1221 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", 1222 | "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" 1223 | }, 1224 | "punycode": { 1225 | "version": "1.4.1", 1226 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1227 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 1228 | }, 1229 | "qs": { 1230 | "version": "6.5.2", 1231 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 1232 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 1233 | }, 1234 | "readable-stream": { 1235 | "version": "1.1.14", 1236 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", 1237 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", 1238 | "dev": true, 1239 | "requires": { 1240 | "core-util-is": "~1.0.0", 1241 | "inherits": "~2.0.1", 1242 | "isarray": "0.0.1", 1243 | "string_decoder": "~0.10.x" 1244 | } 1245 | }, 1246 | "request": { 1247 | "version": "2.88.0", 1248 | "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", 1249 | "integrity": "sha1-nC/KT301tZLv5Xx/ClXoEFIST+8=", 1250 | "requires": { 1251 | "aws-sign2": "~0.7.0", 1252 | "aws4": "^1.8.0", 1253 | "caseless": "~0.12.0", 1254 | "combined-stream": "~1.0.6", 1255 | "extend": "~3.0.2", 1256 | "forever-agent": "~0.6.1", 1257 | "form-data": "~2.3.2", 1258 | "har-validator": "~5.1.0", 1259 | "http-signature": "~1.2.0", 1260 | "is-typedarray": "~1.0.0", 1261 | "isstream": "~0.1.2", 1262 | "json-stringify-safe": "~5.0.1", 1263 | "mime-types": "~2.1.19", 1264 | "oauth-sign": "~0.9.0", 1265 | "performance-now": "^2.1.0", 1266 | "qs": "~6.5.2", 1267 | "safe-buffer": "^5.1.2", 1268 | "tough-cookie": "~2.4.3", 1269 | "tunnel-agent": "^0.6.0", 1270 | "uuid": "^3.3.2" 1271 | } 1272 | }, 1273 | "request-progress": { 1274 | "version": "2.0.1", 1275 | "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", 1276 | "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", 1277 | "dev": true, 1278 | "optional": true, 1279 | "requires": { 1280 | "throttleit": "^1.0.0" 1281 | } 1282 | }, 1283 | "requizzle": { 1284 | "version": "0.2.1", 1285 | "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", 1286 | "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", 1287 | "dev": true, 1288 | "requires": { 1289 | "underscore": "~1.6.0" 1290 | }, 1291 | "dependencies": { 1292 | "underscore": { 1293 | "version": "1.6.0", 1294 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", 1295 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", 1296 | "dev": true 1297 | } 1298 | } 1299 | }, 1300 | "resolve": { 1301 | "version": "1.1.7", 1302 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", 1303 | "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", 1304 | "dev": true 1305 | }, 1306 | "rimraf": { 1307 | "version": "2.4.5", 1308 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", 1309 | "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", 1310 | "dev": true, 1311 | "optional": true, 1312 | "requires": { 1313 | "glob": "^6.0.1" 1314 | } 1315 | }, 1316 | "safe-buffer": { 1317 | "version": "5.1.2", 1318 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1319 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1320 | }, 1321 | "safe-json-stringify": { 1322 | "version": "1.0.4", 1323 | "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.0.4.tgz", 1324 | "integrity": "sha1-gaCY9Efku8P/MxKiQ1IbwGDvWRE=", 1325 | "dev": true, 1326 | "optional": true 1327 | }, 1328 | "safer-buffer": { 1329 | "version": "2.1.2", 1330 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1331 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1332 | }, 1333 | "shelljs": { 1334 | "version": "0.3.0", 1335 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 1336 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 1337 | "dev": true 1338 | }, 1339 | "source-map": { 1340 | "version": "0.2.0", 1341 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", 1342 | "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", 1343 | "dev": true, 1344 | "optional": true, 1345 | "requires": { 1346 | "amdefine": ">=0.0.4" 1347 | } 1348 | }, 1349 | "split": { 1350 | "version": "1.0.1", 1351 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 1352 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 1353 | "dev": true, 1354 | "optional": true, 1355 | "requires": { 1356 | "through": "2" 1357 | } 1358 | }, 1359 | "splunk-logging": { 1360 | "version": "0.10.1", 1361 | "resolved": "https://registry.npmjs.org/splunk-logging/-/splunk-logging-0.10.1.tgz", 1362 | "integrity": "sha512-7j2vvwbLWWSPm9yHEg1XuOt4vN5zHQ6i7JMMMQd9H3nww8JKqnhavXPYv7gZ/FGcpkcmY6JwASn6I6Cbe/jO1w==", 1363 | "requires": { 1364 | "request": "^2.88.0" 1365 | } 1366 | }, 1367 | "sprintf-js": { 1368 | "version": "1.0.3", 1369 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", 1370 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", 1371 | "dev": true 1372 | }, 1373 | "sshpk": { 1374 | "version": "1.15.2", 1375 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", 1376 | "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", 1377 | "requires": { 1378 | "asn1": "~0.2.3", 1379 | "assert-plus": "^1.0.0", 1380 | "bcrypt-pbkdf": "^1.0.0", 1381 | "dashdash": "^1.12.0", 1382 | "ecc-jsbn": "~0.1.1", 1383 | "getpass": "^0.1.1", 1384 | "jsbn": "~0.1.0", 1385 | "safer-buffer": "^2.0.2", 1386 | "tweetnacl": "~0.14.0" 1387 | } 1388 | }, 1389 | "stack-trace": { 1390 | "version": "0.0.10", 1391 | "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", 1392 | "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", 1393 | "dev": true, 1394 | "optional": true 1395 | }, 1396 | "string_decoder": { 1397 | "version": "0.10.31", 1398 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 1399 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 1400 | "dev": true 1401 | }, 1402 | "strip-json-comments": { 1403 | "version": "1.0.4", 1404 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", 1405 | "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", 1406 | "dev": true 1407 | }, 1408 | "supports-color": { 1409 | "version": "3.2.3", 1410 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", 1411 | "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", 1412 | "dev": true, 1413 | "requires": { 1414 | "has-flag": "^1.0.0" 1415 | } 1416 | }, 1417 | "taffydb": { 1418 | "version": "2.6.2", 1419 | "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", 1420 | "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", 1421 | "dev": true 1422 | }, 1423 | "throttleit": { 1424 | "version": "1.0.0", 1425 | "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", 1426 | "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", 1427 | "dev": true, 1428 | "optional": true 1429 | }, 1430 | "through": { 1431 | "version": "2.3.8", 1432 | "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", 1433 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", 1434 | "dev": true, 1435 | "optional": true 1436 | }, 1437 | "tough-cookie": { 1438 | "version": "2.4.3", 1439 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", 1440 | "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", 1441 | "requires": { 1442 | "psl": "^1.1.24", 1443 | "punycode": "^1.4.1" 1444 | } 1445 | }, 1446 | "tunnel-agent": { 1447 | "version": "0.6.0", 1448 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1449 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1450 | "requires": { 1451 | "safe-buffer": "^5.0.1" 1452 | } 1453 | }, 1454 | "tweetnacl": { 1455 | "version": "0.14.5", 1456 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1457 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" 1458 | }, 1459 | "type-check": { 1460 | "version": "0.3.2", 1461 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1462 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1463 | "dev": true, 1464 | "requires": { 1465 | "prelude-ls": "~1.1.2" 1466 | } 1467 | }, 1468 | "typedarray": { 1469 | "version": "0.0.6", 1470 | "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", 1471 | "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", 1472 | "dev": true, 1473 | "optional": true 1474 | }, 1475 | "uglify-js": { 1476 | "version": "3.4.9", 1477 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz", 1478 | "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==", 1479 | "dev": true, 1480 | "optional": true, 1481 | "requires": { 1482 | "commander": "~2.17.1", 1483 | "source-map": "~0.6.1" 1484 | }, 1485 | "dependencies": { 1486 | "commander": { 1487 | "version": "2.17.1", 1488 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", 1489 | "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", 1490 | "dev": true, 1491 | "optional": true 1492 | }, 1493 | "source-map": { 1494 | "version": "0.6.1", 1495 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1496 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1497 | "dev": true, 1498 | "optional": true 1499 | } 1500 | } 1501 | }, 1502 | "underscore": { 1503 | "version": "1.8.3", 1504 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", 1505 | "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", 1506 | "dev": true 1507 | }, 1508 | "underscore-contrib": { 1509 | "version": "0.3.0", 1510 | "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", 1511 | "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", 1512 | "dev": true, 1513 | "requires": { 1514 | "underscore": "1.6.0" 1515 | }, 1516 | "dependencies": { 1517 | "underscore": { 1518 | "version": "1.6.0", 1519 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", 1520 | "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", 1521 | "dev": true 1522 | } 1523 | } 1524 | }, 1525 | "unicode-5.2.0": { 1526 | "version": "0.7.5", 1527 | "resolved": "https://registry.npmjs.org/unicode-5.2.0/-/unicode-5.2.0-0.7.5.tgz", 1528 | "integrity": "sha512-KVGLW1Bri30x00yv4HNM8kBxoqFXr0Sbo55735nvrlsx4PYBZol3UtoWgO492fSwmsetzPEZzy73rbU8OGXJcA==", 1529 | "dev": true 1530 | }, 1531 | "util-deprecate": { 1532 | "version": "1.0.2", 1533 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 1534 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 1535 | "dev": true, 1536 | "optional": true 1537 | }, 1538 | "uuid": { 1539 | "version": "3.3.2", 1540 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 1541 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 1542 | }, 1543 | "verror": { 1544 | "version": "1.10.0", 1545 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1546 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1547 | "requires": { 1548 | "assert-plus": "^1.0.0", 1549 | "core-util-is": "1.0.2", 1550 | "extsprintf": "^1.2.0" 1551 | } 1552 | }, 1553 | "which": { 1554 | "version": "1.3.1", 1555 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", 1556 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", 1557 | "dev": true, 1558 | "requires": { 1559 | "isexe": "^2.0.0" 1560 | } 1561 | }, 1562 | "winston": { 1563 | "version": "2.4.4", 1564 | "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", 1565 | "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", 1566 | "dev": true, 1567 | "optional": true, 1568 | "requires": { 1569 | "async": "~1.0.0", 1570 | "colors": "1.0.x", 1571 | "cycle": "1.0.x", 1572 | "eyes": "0.1.x", 1573 | "isstream": "0.1.x", 1574 | "stack-trace": "0.0.x" 1575 | }, 1576 | "dependencies": { 1577 | "async": { 1578 | "version": "1.0.0", 1579 | "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", 1580 | "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", 1581 | "dev": true, 1582 | "optional": true 1583 | } 1584 | } 1585 | }, 1586 | "wordwrap": { 1587 | "version": "1.0.0", 1588 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1589 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1590 | "dev": true 1591 | }, 1592 | "wrappy": { 1593 | "version": "1.0.2", 1594 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1595 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1596 | "dev": true 1597 | }, 1598 | "xmlcreate": { 1599 | "version": "1.0.2", 1600 | "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", 1601 | "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=", 1602 | "dev": true 1603 | }, 1604 | "yauzl": { 1605 | "version": "2.4.1", 1606 | "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", 1607 | "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", 1608 | "dev": true, 1609 | "optional": true, 1610 | "requires": { 1611 | "fd-slicer": "~1.0.1" 1612 | } 1613 | } 1614 | } 1615 | } 1616 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "splunk-bunyan-logger", 3 | "version": "0.11.0", 4 | "description": "Splunk HTTP Event Collector Stream for Bunyan", 5 | "homepage": "http://dev.splunk.com", 6 | "main": "index.js", 7 | "scripts": { 8 | "docs": "jsdoc -d docs .", 9 | "jshint": "jshint *.js test examples", 10 | "pretest": "npm run jshint && npm run docs", 11 | "test": "nyc --reporter=lcov --reporter=text-summary _mocha -- -R spec --exit", 12 | "test-specific-file": "_mocha -- -R spec --exit ", 13 | "test-specific": "_mocha -- -R spec --exit -g " 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/splunk/splunk-bunyan-logger.git" 18 | }, 19 | "keywords": [ 20 | "splunk", 21 | "HTTP", 22 | "event", 23 | "collector", 24 | "logging", 25 | "stream", 26 | "bunyan" 27 | ], 28 | "author": { 29 | "name": "Splunk", 30 | "email": "devinfo@splunk.com", 31 | "url": "http://dev.splunk.com" 32 | }, 33 | "license": "Apache-2.0", 34 | "engine": { 35 | "node": ">=4.0.0" 36 | }, 37 | "dependencies": { 38 | "splunk-logging": "0.11.1" 39 | }, 40 | "devDependencies": { 41 | "bunyan": "1.8.15", 42 | "jsdoc": "^3.6.7", 43 | "jshint": "^2.12.0", 44 | "mocha": "^8.4.0", 45 | "needle": "^2.6.0", 46 | "nyc": "^15.1.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /scripts/test_specific.sh: -------------------------------------------------------------------------------- 1 | echo "To run a specific test file:" 2 | echo " npm run test-specific-file [test/ test/ ..]" 3 | echo "To run a specific test case:" 4 | echo " npm run test-specific ''" -------------------------------------------------------------------------------- /test/test_bunyan.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | var SplunkBunyan = require("../index"); 18 | var assert = require("assert"); 19 | var bunyan = require("bunyan"); 20 | var needle = require("needle"); 21 | 22 | var TOKEN; 23 | 24 | var successBody = { 25 | text: "Success", 26 | code: 0 27 | }; 28 | 29 | var invalidTokenBody = { 30 | text: "Invalid token", 31 | code: 4 32 | }; 33 | 34 | // TODO: test unsuccessfully sending to another index with specific index token settings 35 | // var incorrectIndexBody = { 36 | // text: "Incorrect index", 37 | // code: 7, 38 | // "invalid-event-number": 1 39 | // }; 40 | 41 | var ____consoleLog = console.log; 42 | function mute() { 43 | console.log = function(){}; 44 | } 45 | function unmute() { 46 | console.log = ____consoleLog; 47 | } 48 | 49 | // this method returns the body based on type of response.body for assertion 50 | function responseBodyCheck(resp, body){ 51 | return typeof resp.body === "string"? JSON.stringify(body): body; 52 | } 53 | 54 | describe("Setup Splunk on localhost:8089 HEC", function() { 55 | it("should be enabled", function(done) { 56 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http/http/enable?output_mode=json", null, {rejectUnauthorized: false}, function(err) { 57 | assert.ok(!err); 58 | done(); 59 | }); 60 | }); 61 | it("should create a token in test/config.json", function(done) { 62 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http?output_mode=json", { name : "splunk_logging" + Date.now()}, {rejectUnauthorized: false}, function(err, resp, body) { 63 | assert.ok(!err); 64 | body = JSON.stringify(body); 65 | var tokenStart = body.indexOf("\"token\":\""); 66 | var tokenEnd = tokenStart + 36; // 36 = guid length 67 | var token = body.substring(tokenStart + 9, tokenEnd + 9); // 9 = prefix length of \"token\":\" 68 | assert.strictEqual(token.length, 36); 69 | TOKEN = token; 70 | done(); 71 | }); 72 | }); 73 | it("should have the env variable set", function() { 74 | assert.ok(TOKEN); 75 | assert.strictEqual(TOKEN.length, 36); 76 | }); 77 | }); 78 | 79 | describe("Bunyan", function() { 80 | describe("Setup Splunk on localhost:8089 HEC", function() { 81 | it("should be enabled", function(done) { 82 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http/http/enable?output_mode=json", null, {rejectUnauthorized: false}, function(err) { 83 | assert.ok(!err); 84 | done(); 85 | }); 86 | }); 87 | it("should create a token in test/config.json", function(done) { 88 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http?output_mode=json", { name : "splunk_logging" + Date.now()}, {rejectUnauthorized: false}, function(err, resp, body) { 89 | assert.ok(!err); 90 | body = JSON.stringify(body); 91 | var tokenStart = body.indexOf("\"token\":\""); 92 | var tokenEnd = tokenStart + 36; // 36 = guid length 93 | var token = body.substring(tokenStart + 9, tokenEnd + 9); // 9 = prefix length of \"token\":\" 94 | assert.strictEqual(token.length, 36); 95 | TOKEN = token; 96 | done(); 97 | }); 98 | }); 99 | it("should have the env variable set", function() { 100 | assert.ok(TOKEN); 101 | assert.strictEqual(TOKEN.length, 36); 102 | }); 103 | }); 104 | 105 | it("should create logger with SplunkStream", function() { 106 | var splunkBunyanStream = SplunkBunyan.createStream({token: TOKEN}); 107 | 108 | assert.ok(splunkBunyanStream); 109 | 110 | var Logger = bunyan.createLogger({ 111 | name: "a bunyan logger", 112 | streams: [ 113 | splunkBunyanStream 114 | ] 115 | }); 116 | 117 | assert.ok(Logger); 118 | assert.strictEqual("a bunyan logger", Logger.fields.name); 119 | assert.strictEqual(bunyan.resolveLevel("info"), Logger.level()); 120 | assert.strictEqual(1, Logger.streams.length); 121 | assert.strictEqual(splunkBunyanStream.stream, Logger.streams[0].stream); 122 | }); 123 | it("should error sending data to invalid url, caught by custom stream.error", function(done) { 124 | var config = { 125 | url: "https://invalid.server:8088/services/collector/invalid/1.0", 126 | token: "does-not-matter" 127 | }; 128 | var splunkBunyanStream = SplunkBunyan.createStream(config); 129 | 130 | splunkBunyanStream.on("error", function(err, context) { 131 | assert.ok(err); 132 | assert.ok(context); 133 | assert.strictEqual(err.code, "ENOTFOUND"); 134 | assert.ok((err.errno === -3008) || (err.errno === "ENOTFOUND")); 135 | unmute(); 136 | done(); 137 | }); 138 | 139 | var Logger = bunyan.createLogger({ 140 | name: "a bunyan logger", 141 | streams: [ 142 | splunkBunyanStream 143 | ] 144 | }); 145 | 146 | mute(); 147 | Logger.info("this is a test statement"); 148 | }); 149 | it("should error sending data with invalid token", function(done) { 150 | var config = { 151 | token: "bad-token" 152 | }; 153 | var splunkBunyanStream = SplunkBunyan.createStream(config); 154 | 155 | var run = false; 156 | 157 | // Override the default send function 158 | splunkBunyanStream.stream.send = function(err, resp, body) { 159 | assert.ok(!err); 160 | assert.ok(run); 161 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 162 | assert.strictEqual(body.text, invalidTokenBody.text); 163 | assert.strictEqual(body.code, invalidTokenBody.code); 164 | unmute(); 165 | done(); 166 | }; 167 | 168 | splunkBunyanStream.on("error", function(err, context) { 169 | run = true; 170 | assert.ok(err); 171 | assert.strictEqual(err.message, invalidTokenBody.text); 172 | assert.strictEqual(err.code, invalidTokenBody.code); 173 | assert.ok(context); 174 | 175 | var body = JSON.parse(context.message); 176 | assert.ok(body); 177 | assert.ok(body.hasOwnProperty("host")); 178 | assert.ok(body.hasOwnProperty("time")); 179 | assert.ok(body.hasOwnProperty("event")); 180 | assert.ok(body.event.hasOwnProperty("message")); 181 | assert.strictEqual(body.event.message.msg, "this is a test statement"); 182 | assert.strictEqual(body.event.severity, "info"); 183 | }); 184 | 185 | var Logger = bunyan.createLogger({ 186 | name: "a bunyan logger", 187 | streams: [ 188 | splunkBunyanStream 189 | ] 190 | }); 191 | 192 | mute(); 193 | Logger.info("this is a test statement"); 194 | }); 195 | it("should succeed in sending data as trace with valid token", function(done) { 196 | var config = { 197 | token: TOKEN, 198 | level: "trace" 199 | }; 200 | var splunkBunyanStream = SplunkBunyan.createStream(config); 201 | 202 | var run = false; 203 | 204 | // Override the default send function 205 | splunkBunyanStream.stream.send = function(err, resp, body) { 206 | assert.ok(!err); 207 | assert.ok(run); 208 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 209 | assert.strictEqual(body.text, successBody.text); 210 | assert.strictEqual(body.code, successBody.code); 211 | done(); 212 | }; 213 | 214 | // Wrap the write function to test that the level works 215 | var write = splunkBunyanStream.stream.write; 216 | splunkBunyanStream.stream.write = function(data) { 217 | run = true; 218 | assert.ok(data); 219 | assert.strictEqual(data.level, 10); 220 | splunkBunyanStream.stream.write = write; 221 | this.write(data); 222 | }; 223 | 224 | var Logger = bunyan.createLogger({ 225 | name: "a bunyan logger", 226 | streams: [ 227 | splunkBunyanStream 228 | ] 229 | }); 230 | 231 | Logger.trace("this is a test statement"); 232 | }); 233 | it("should succeed in sending data as debug with valid token", function(done) { 234 | var config = { 235 | token: TOKEN, 236 | level: "debug" 237 | }; 238 | var splunkBunyanStream = SplunkBunyan.createStream(config); 239 | 240 | var run = false; 241 | 242 | // Override the default send function 243 | splunkBunyanStream.stream.send = function(err, resp, body) { 244 | assert.ok(!err); 245 | assert.ok(run); 246 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 247 | assert.strictEqual(body.text, successBody.text); 248 | assert.strictEqual(body.code, successBody.code); 249 | done(); 250 | }; 251 | 252 | // Wrap the write function to test that the level works 253 | var write = splunkBunyanStream.stream.write; 254 | splunkBunyanStream.stream.write = function(data) { 255 | run = true; 256 | assert.ok(data); 257 | assert.strictEqual(data.level, 20); 258 | splunkBunyanStream.stream.write = write; 259 | this.write(data); 260 | }; 261 | 262 | var Logger = bunyan.createLogger({ 263 | name: "a bunyan logger", 264 | streams: [ 265 | splunkBunyanStream 266 | ], 267 | level: "debug" 268 | }); 269 | 270 | Logger.debug("this is a test statement"); 271 | }); 272 | it("should succeed in sending data as info with valid token", function(done) { 273 | var config = { 274 | token: TOKEN 275 | }; 276 | var splunkBunyanStream = SplunkBunyan.createStream(config); 277 | 278 | var run = false; 279 | 280 | // Override the default send function 281 | splunkBunyanStream.stream.send = function(err, resp, body) { 282 | assert.ok(!err); 283 | assert.ok(run); 284 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 285 | assert.strictEqual(body.text, successBody.text); 286 | assert.strictEqual(body.code, successBody.code); 287 | done(); 288 | }; 289 | 290 | // Wrap the write function to test that the level works 291 | var write = splunkBunyanStream.stream.write; 292 | splunkBunyanStream.stream.write = function(data) { 293 | run = true; 294 | assert.ok(data); 295 | assert.strictEqual(data.level, 30); 296 | splunkBunyanStream.stream.write = write; 297 | this.write(data); 298 | }; 299 | 300 | var Logger = bunyan.createLogger({ 301 | name: "a bunyan logger", 302 | streams: [ 303 | splunkBunyanStream 304 | ] 305 | }); 306 | 307 | Logger.info("this is a test statement"); 308 | }); 309 | it("should succeed in sending data as warn with valid token", function(done) { 310 | var config = { 311 | token: TOKEN 312 | }; 313 | var splunkBunyanStream = SplunkBunyan.createStream(config); 314 | 315 | var run = false; 316 | 317 | // Override the default send function 318 | splunkBunyanStream.stream.send = function(err, resp, body) { 319 | assert.ok(!err); 320 | assert.ok(run); 321 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 322 | assert.strictEqual(body.text, successBody.text); 323 | assert.strictEqual(body.code, successBody.code); 324 | done(); 325 | }; 326 | 327 | // Wrap the write function to test that the level works 328 | var write = splunkBunyanStream.stream.write; 329 | splunkBunyanStream.stream.write = function(data) { 330 | run = true; 331 | assert.ok(data); 332 | assert.strictEqual(data.level, 40); 333 | splunkBunyanStream.stream.write = write; 334 | this.write(data); 335 | }; 336 | 337 | 338 | var Logger = bunyan.createLogger({ 339 | name: "a bunyan logger", 340 | streams: [ 341 | splunkBunyanStream 342 | ] 343 | }); 344 | 345 | Logger.warn("this is a test statement"); 346 | }); 347 | it("should succeed in sending data as error with valid token", function(done) { 348 | var config = { 349 | token: TOKEN 350 | }; 351 | var splunkBunyanStream = SplunkBunyan.createStream(config); 352 | 353 | var run = false; 354 | 355 | // Override the default send function 356 | splunkBunyanStream.stream.send = function(err, resp, body) { 357 | assert.ok(!err); 358 | assert.ok(run); 359 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 360 | assert.strictEqual(body.text, successBody.text); 361 | assert.strictEqual(body.code, successBody.code); 362 | done(); 363 | }; 364 | 365 | // Wrap the write function to test that the level works 366 | var write = splunkBunyanStream.stream.write; 367 | splunkBunyanStream.stream.write = function(data) { 368 | run = true; 369 | assert.ok(data); 370 | assert.strictEqual(data.level, 50); 371 | splunkBunyanStream.stream.write = write; 372 | this.write(data); 373 | }; 374 | 375 | var Logger = bunyan.createLogger({ 376 | name: "a bunyan logger", 377 | streams: [ 378 | splunkBunyanStream 379 | ], 380 | level: "error" 381 | }); 382 | 383 | Logger.error("this is a test statement"); 384 | }); 385 | it("should succeed in sending data as fatal with valid token", function(done) { 386 | var config = { 387 | token: TOKEN 388 | }; 389 | var splunkBunyanStream = SplunkBunyan.createStream(config); 390 | 391 | var run = false; 392 | 393 | // Override the default send function 394 | splunkBunyanStream.stream.send = function(err, resp, body) { 395 | assert.ok(!err); 396 | assert.ok(run); 397 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 398 | assert.strictEqual(body.text, successBody.text); 399 | assert.strictEqual(body.code, successBody.code); 400 | done(); 401 | }; 402 | 403 | // Wrap the write function to test that the level works 404 | var write = splunkBunyanStream.stream.write; 405 | splunkBunyanStream.stream.write = function(data) { 406 | run = true; 407 | assert.ok(data); 408 | assert.strictEqual(data.level, 60); 409 | splunkBunyanStream.stream.write = write; 410 | this.write(data); 411 | }; 412 | 413 | var Logger = bunyan.createLogger({ 414 | name: "a bunyan logger", 415 | streams: [ 416 | splunkBunyanStream 417 | ], 418 | level: "fatal" 419 | }); 420 | 421 | Logger.fatal("this is a test statement"); 422 | }); 423 | it("should succeed in sending data with valid token using custom time", function(done) { 424 | var config = { 425 | token: TOKEN 426 | }; 427 | var splunkBunyanStream = SplunkBunyan.createStream(config); 428 | 429 | // Override the default send function 430 | splunkBunyanStream.stream.send = function(err, resp, body) { 431 | assert.ok(!err); 432 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 433 | assert.strictEqual(body.text, successBody.text); 434 | assert.strictEqual(body.code, successBody.code); 435 | done(); 436 | }; 437 | 438 | var Logger = bunyan.createLogger({ 439 | name: "a bunyan logger", 440 | streams: [ 441 | splunkBunyanStream 442 | ] 443 | }); 444 | 445 | Logger.info({time: Date.parse("Jan 01, 2015")}, "custom time"); 446 | }); 447 | it("should succeed in sending data with valid token using custom host", function(done) { 448 | var config = { 449 | token: TOKEN 450 | }; 451 | var splunkBunyanStream = SplunkBunyan.createStream(config); 452 | 453 | // Override the default send function 454 | splunkBunyanStream.stream.send = function(err, resp, body) { 455 | assert.ok(!err); 456 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 457 | assert.strictEqual(body.text, successBody.text); 458 | assert.strictEqual(body.code, successBody.code); 459 | done(); 460 | }; 461 | 462 | var Logger = bunyan.createLogger({ 463 | name: "a bunyan logger", 464 | streams: [ 465 | splunkBunyanStream 466 | ] 467 | }); 468 | 469 | Logger.info({host: "different.host.local"}, "custom host"); 470 | }); 471 | it("should succeed in sending data with valid token using custom source", function(done) { 472 | var config = { 473 | token: TOKEN 474 | }; 475 | var splunkBunyanStream = SplunkBunyan.createStream(config); 476 | 477 | // Override the default send function 478 | splunkBunyanStream.stream.send = function(err, resp, body) { 479 | assert.ok(!err); 480 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 481 | assert.strictEqual(body.text, successBody.text); 482 | assert.strictEqual(body.code, successBody.code); 483 | done(); 484 | }; 485 | 486 | var Logger = bunyan.createLogger({ 487 | name: "a bunyan logger", 488 | streams: [ 489 | splunkBunyanStream 490 | ] 491 | }); 492 | 493 | Logger.info({source: "different_source"}, "custom source"); 494 | }); 495 | it("should succeed in sending data with valid token using custom sourcetype", function(done) { 496 | var config = { 497 | token: TOKEN 498 | }; 499 | var splunkBunyanStream = SplunkBunyan.createStream(config); 500 | 501 | // Override the default send function 502 | splunkBunyanStream.stream.send = function(err, resp, body) { 503 | assert.ok(!err); 504 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 505 | assert.strictEqual(body.text, successBody.text); 506 | assert.strictEqual(body.code, successBody.code); 507 | done(); 508 | }; 509 | 510 | var Logger = bunyan.createLogger({ 511 | name: "a bunyan logger", 512 | streams: [ 513 | splunkBunyanStream 514 | ] 515 | }); 516 | 517 | Logger.info({sourcetype: "different_sourcetype"}, "custom sourcetype"); 518 | }); 519 | it("should succeed in sending data with valid token to any index", function(done) { 520 | var config = { 521 | token: TOKEN 522 | }; 523 | var splunkBunyanStream = SplunkBunyan.createStream(config); 524 | 525 | // Override the default send function 526 | splunkBunyanStream.stream.send = function(err, resp, body) { 527 | assert.ok(!err); 528 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 529 | assert.strictEqual(body.text, successBody.text); 530 | assert.strictEqual(body.code, successBody.code); 531 | done(); 532 | }; 533 | 534 | var Logger = bunyan.createLogger({ 535 | name: "a bunyan logger", 536 | streams: [ 537 | splunkBunyanStream 538 | ] 539 | }); 540 | 541 | Logger.info({index: "_____different_index"}, "custom index"); 542 | }); 543 | // TODO: test successfully sending to another index 544 | it("should succeed in sending array data with valid token", function(done) { 545 | var config = { 546 | token: TOKEN 547 | }; 548 | var splunkBunyanStream = SplunkBunyan.createStream(config); 549 | 550 | // Override the default send function 551 | splunkBunyanStream.stream.send = function(err, resp, body) { 552 | assert.ok(!err); 553 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 554 | assert.strictEqual(body.text, successBody.text); 555 | assert.strictEqual(body.code, successBody.code); 556 | done(); 557 | }; 558 | 559 | var Logger = bunyan.createLogger({ 560 | name: "a bunyan logger", 561 | streams: [ 562 | splunkBunyanStream 563 | ] 564 | }); 565 | 566 | Logger.info([1, 2, 3]); 567 | }); 568 | it("should succeed in sending data as object with valid token", function(done) { 569 | var config = { 570 | token: TOKEN 571 | }; 572 | var splunkBunyanStream = SplunkBunyan.createStream(config); 573 | 574 | // Override the default send function 575 | splunkBunyanStream.stream.send = function(err, resp, body) { 576 | assert.ok(!err); 577 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 578 | assert.strictEqual(body.text, successBody.text); 579 | assert.strictEqual(body.code, successBody.code); 580 | done(); 581 | }; 582 | 583 | var Logger = bunyan.createLogger({ 584 | name: "a bunyan logger", 585 | streams: [ 586 | splunkBunyanStream 587 | ] 588 | }); 589 | 590 | var data = { 591 | something: "1233312124" 592 | }; 593 | Logger.info(data); 594 | }); 595 | it("should succeed in sending data twice with valid token", function(done) { 596 | var config = { 597 | token: TOKEN 598 | }; 599 | var splunkBunyanStream = SplunkBunyan.createStream(config); 600 | 601 | var count = 0; 602 | 603 | // Override the default send function 604 | splunkBunyanStream.stream.send = function(err, resp, body) { 605 | count++; 606 | assert.ok(!err); 607 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 608 | assert.strictEqual(body.text, successBody.text); 609 | assert.strictEqual(body.code, successBody.code); 610 | if (count === 2) { 611 | done(); 612 | } 613 | }; 614 | 615 | var Logger = bunyan.createLogger({ 616 | name: "a bunyan logger", 617 | streams: [ 618 | splunkBunyanStream 619 | ] 620 | }); 621 | 622 | Logger.info("this is a test statement"); 623 | Logger.info("this is a test statement"); 624 | }); 625 | it("should succeed in sending data using custom eventFormatter with valid token", function(done) { 626 | var config = { 627 | token: TOKEN 628 | }; 629 | var splunkBunyanStream = SplunkBunyan.createStream(config); 630 | 631 | splunkBunyanStream.setEventFormatter(function(message, severity) { 632 | var event = "[" + severity + "]"; 633 | 634 | if (typeof message === "object" && message.hasOwnProperty("msg")) { 635 | event += "message=" + message.msg; 636 | } 637 | else { 638 | event += "message=" + message; 639 | } 640 | 641 | return event; 642 | }); 643 | 644 | var post = splunkBunyanStream.stream.logger._post; 645 | splunkBunyanStream.stream.logger._post = function(opts, callback) { 646 | var expected = "[info]message=this is a test statement"; 647 | 648 | assert.ok(opts); 649 | assert.ok(opts.hasOwnProperty("body")); 650 | var body = JSON.parse(opts.body); 651 | assert.ok(body.hasOwnProperty("event")); 652 | assert.ok(body.hasOwnProperty("time")); 653 | 654 | assert.strictEqual(body.event, expected); 655 | 656 | post(opts, callback); 657 | }; 658 | 659 | var count = 0; 660 | 661 | // Override the default send function 662 | splunkBunyanStream.stream.send = function(err, resp, body) { 663 | count++; 664 | assert.ok(!err); 665 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 666 | assert.strictEqual(body.text, successBody.text); 667 | assert.strictEqual(body.code, successBody.code); 668 | if (count === 2) { 669 | done(); 670 | } 671 | }; 672 | 673 | var Logger = bunyan.createLogger({ 674 | name: "a bunyan logger", 675 | streams: [ 676 | splunkBunyanStream 677 | ] 678 | }); 679 | 680 | Logger.info("this is a test statement"); 681 | Logger.info("this is a test statement"); 682 | }); 683 | it("should succeed in sending data twice with valid token with batching off, flush(err, resp, body)", function(done) { 684 | var config = { 685 | token: TOKEN, 686 | maxBatchCount: 0 687 | }; 688 | var splunkBunyanStream = SplunkBunyan.createStream(config); 689 | 690 | var run = false; 691 | 692 | // Wrap the default send function, it should never be executed 693 | var send = splunkBunyanStream.stream.send; 694 | splunkBunyanStream.stream.send = function(err, resp, body) { 695 | run = true; 696 | assert.ok(false); 697 | send(err, resp, body); 698 | }; 699 | 700 | var Logger = bunyan.createLogger({ 701 | name: "a bunyan logger", 702 | streams: [ 703 | splunkBunyanStream 704 | ] 705 | }); 706 | 707 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 708 | Logger.info("this is a test statement"); 709 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 710 | Logger.info("this is a test statement"); 711 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 2); 712 | 713 | splunkBunyanStream.flush(function(err, resp, body) { 714 | assert.ok(!err); 715 | assert.ok(!run); // Shouldn't execute the stream.send() above 716 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 717 | assert.strictEqual(body.text, successBody.text); 718 | assert.strictEqual(body.code, successBody.code); 719 | done(); 720 | }); 721 | }); 722 | it("should succeed in sending data twice with valid token with batching off, flush()", function(done) { 723 | var config = { 724 | token: TOKEN, 725 | maxBatchCount: 0 726 | }; 727 | var splunkBunyanStream = SplunkBunyan.createStream(config); 728 | 729 | // Wrap the default send function 730 | var send = splunkBunyanStream.stream.send; 731 | splunkBunyanStream.stream.send = function(err, resp, body) { 732 | assert.strictEqual(resp.body, responseBodyCheck(resp, body)); 733 | assert.strictEqual(body.text, successBody.text); 734 | assert.strictEqual(body.code, successBody.code); 735 | send(err, resp, body); 736 | done(); 737 | }; 738 | 739 | var Logger = bunyan.createLogger({ 740 | name: "a bunyan logger", 741 | streams: [ 742 | splunkBunyanStream 743 | ] 744 | }); 745 | 746 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 747 | Logger.info("this is a test statement"); 748 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 749 | Logger.info("this is a test statement"); 750 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 2); 751 | 752 | // Call flush without a callback, falls back to stream.send() above 753 | splunkBunyanStream.flush(); 754 | }); 755 | it("should not retry on Splunk error", function(done) { 756 | var config = { 757 | token: "bad-token", 758 | maxRetries: 5 759 | }; 760 | var splunkBunyanStream = SplunkBunyan.createStream(config); 761 | 762 | var retryCount = 0; 763 | 764 | // Wrap the _post so we can verify retries 765 | var post = splunkBunyanStream.stream.logger._post; 766 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 767 | retryCount++; 768 | post(requestOptions, callback); 769 | }; 770 | 771 | splunkBunyanStream.stream.on("error", function(err) { 772 | assert.ok(err); 773 | assert.strictEqual(invalidTokenBody.code, err.code); 774 | assert.strictEqual(invalidTokenBody.text, err.message); 775 | assert.strictEqual(1, retryCount); 776 | unmute(); 777 | done(); 778 | }); 779 | 780 | var Logger = bunyan.createLogger({ 781 | name: "a bunyan logger", 782 | streams: [ 783 | splunkBunyanStream 784 | ] 785 | }); 786 | mute(); 787 | Logger.info("this is a test statement"); 788 | }); 789 | it("should retry on network error", function(done) { 790 | var config = { 791 | token: TOKEN, 792 | maxRetries: 5, 793 | host: "splunk.invalid" 794 | }; 795 | var splunkBunyanStream = SplunkBunyan.createStream(config); 796 | 797 | var retryCount = 0; 798 | 799 | // Wrap the _post so we can verify retries 800 | var post = splunkBunyanStream.stream.logger._post; 801 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 802 | retryCount++; 803 | post(requestOptions, callback); 804 | }; 805 | 806 | splunkBunyanStream.stream.on("error", function(err) { 807 | assert.ok(err); 808 | assert.strictEqual(true,["ENOTFOUND","EAI_AGAIN"].includes(err.code)); 809 | assert.strictEqual(config.maxRetries + 1, retryCount); 810 | unmute(); 811 | done(); 812 | }); 813 | 814 | var Logger = bunyan.createLogger({ 815 | name: "a bunyan logger", 816 | streams: [ 817 | splunkBunyanStream 818 | ] 819 | }); 820 | 821 | mute(); 822 | Logger.info("this is a test statement"); 823 | }); 824 | it("should be noop when nothing to flush", function(done) { 825 | var config = { 826 | token: TOKEN, 827 | batchInterval: 100 828 | }; 829 | var splunkBunyanStream = SplunkBunyan.createStream(config); 830 | 831 | var postCount = 0; 832 | 833 | // Wrap the _post so we can verify retries 834 | var post = splunkBunyanStream.stream.logger._post; 835 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 836 | postCount++; 837 | post(requestOptions, callback); 838 | }; 839 | 840 | var flushCount = 0; 841 | var responses = 0; 842 | 843 | // Wrap flush so we can verify flushing is attempted 844 | var flush = splunkBunyanStream.stream.logger.flush; 845 | splunkBunyanStream.stream.logger.flush = function() { 846 | flushCount++; 847 | flush(function() { 848 | responses++; 849 | }); 850 | }; 851 | 852 | var Logger = bunyan.createLogger({ 853 | name: "a bunyan logger", 854 | streams: [ 855 | splunkBunyanStream 856 | ] 857 | }); 858 | 859 | Logger.info(); 860 | Logger.info(); 861 | Logger.info(); 862 | Logger.info(); 863 | Logger.info(); 864 | 865 | setTimeout(function() { 866 | assert.strictEqual(0, postCount); 867 | assert.strictEqual(0, flushCount); 868 | assert.strictEqual(0, responses); 869 | done(); 870 | }, 350); 871 | }); 872 | it("should post once for 1 event", function(done) { 873 | var config = { 874 | token: TOKEN, 875 | batchInterval: 100 876 | }; 877 | var splunkBunyanStream = SplunkBunyan.createStream(config); 878 | 879 | var postCount = 0; 880 | 881 | // Wrap the _post so we can verify retries 882 | var post = splunkBunyanStream.stream.logger._post; 883 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 884 | postCount++; 885 | post(requestOptions, callback); 886 | }; 887 | 888 | var flushCount = 0; 889 | var responses = 0; 890 | 891 | // Wrap flush so we can verify flushing is attempted 892 | var flush = splunkBunyanStream.stream.logger.flush; 893 | splunkBunyanStream.stream.logger.flush = function() { 894 | flushCount++; 895 | flush(function(err, resp, body) { 896 | responses++; 897 | assert.ok(!err); 898 | assert.strictEqual(body.code, successBody.code); 899 | assert.strictEqual(body.text, successBody.text); 900 | }); 901 | }; 902 | 903 | var Logger = bunyan.createLogger({ 904 | name: "a bunyan logger", 905 | streams: [ 906 | splunkBunyanStream 907 | ] 908 | }); 909 | 910 | Logger.info("valid event"); 911 | Logger.info(); 912 | Logger.info(); 913 | Logger.info(); 914 | Logger.info(); 915 | 916 | setTimeout(function() { 917 | assert.strictEqual(1, postCount); 918 | assert.strictEqual(1, flushCount); 919 | assert.strictEqual(1, responses); 920 | done(); 921 | }, 350); 922 | }); 923 | it("should post once for 2 events", function(done) { 924 | var config = { 925 | token: TOKEN, 926 | batchInterval: 100, 927 | maxBatchCount: 2 928 | }; 929 | var splunkBunyanStream = SplunkBunyan.createStream(config); 930 | 931 | var postCount = 0; 932 | 933 | // Wrap the _post so we can verify retries 934 | var post = splunkBunyanStream.stream.logger._post; 935 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 936 | postCount++; 937 | post(requestOptions, callback); 938 | }; 939 | 940 | var flushCount = 0; 941 | var responses = 0; 942 | 943 | // Wrap flush so we can verify flushing is attempted 944 | var flush = splunkBunyanStream.stream.logger.flush; 945 | splunkBunyanStream.stream.logger.flush = function() { 946 | flushCount++; 947 | flush(function(err, resp, body) { 948 | responses++; 949 | assert.ok(!err); 950 | assert.strictEqual(body.code, successBody.code); 951 | assert.strictEqual(body.text, successBody.text); 952 | }); 953 | }; 954 | 955 | var Logger = bunyan.createLogger({ 956 | name: "a bunyan logger", 957 | streams: [ 958 | splunkBunyanStream 959 | ] 960 | }); 961 | 962 | Logger.info("valid event"); 963 | setTimeout(function(){ 964 | Logger.info("valid event"); 965 | }, 80); 966 | Logger.info(); 967 | Logger.info(); 968 | Logger.info(); 969 | 970 | setTimeout(function() { 971 | assert.strictEqual(1, postCount); 972 | assert.strictEqual(1, flushCount); 973 | assert.strictEqual(1, responses); 974 | done(); 975 | }, 350); 976 | }); 977 | it("should post once for 5 events", function(done) { 978 | var config = { 979 | token: TOKEN, 980 | batchInterval: 100, 981 | maxBatchCount: 5 982 | }; 983 | var splunkBunyanStream = SplunkBunyan.createStream(config); 984 | 985 | var postCount = 0; 986 | 987 | // Wrap the _post so we can verify retries 988 | var post = splunkBunyanStream.stream.logger._post; 989 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 990 | postCount++; 991 | post(requestOptions, callback); 992 | }; 993 | 994 | var flushCount = 0; 995 | var responses = 0; 996 | 997 | // Wrap flush so we can verify flushing is attempted 998 | var flush = splunkBunyanStream.stream.logger.flush; 999 | splunkBunyanStream.stream.logger.flush = function() { 1000 | flushCount++; 1001 | flush(function(err, resp, body) { 1002 | responses++; 1003 | assert.ok(!err); 1004 | assert.strictEqual(body.code, successBody.code); 1005 | assert.strictEqual(body.text, successBody.text); 1006 | }); 1007 | }; 1008 | 1009 | var Logger = bunyan.createLogger({ 1010 | name: "a bunyan logger", 1011 | streams: [ 1012 | splunkBunyanStream 1013 | ] 1014 | }); 1015 | 1016 | Logger.info("valid event"); 1017 | Logger.info("valid event"); 1018 | Logger.info("valid event"); 1019 | 1020 | setTimeout(function(){ 1021 | Logger.info("valid event"); 1022 | Logger.info("valid event"); 1023 | }, 80); 1024 | 1025 | Logger.info(); 1026 | 1027 | setTimeout(function() { 1028 | assert.strictEqual(1, postCount); 1029 | assert.strictEqual(1, flushCount); 1030 | assert.strictEqual(1, responses); 1031 | done(); 1032 | }, 350); 1033 | }); 1034 | it("should flush a stale event after enabling batching & setting batchInterval", function(done) { 1035 | var config = { 1036 | token: TOKEN, 1037 | maxBatchCount: 0 1038 | }; 1039 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1040 | 1041 | var postCount = 0; 1042 | 1043 | // Wrap the _post so we can verify retries 1044 | var post = splunkBunyanStream.stream.logger._post; 1045 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1046 | postCount++; 1047 | post(requestOptions, callback); 1048 | }; 1049 | 1050 | var flushCount = 0; 1051 | var responses = 0; 1052 | 1053 | // Wrap flush so we can verify flushing is attempted 1054 | var flush = splunkBunyanStream.stream.logger.flush; 1055 | splunkBunyanStream.stream.logger.flush = function() { 1056 | flushCount++; 1057 | flush(function(err, resp, body) { 1058 | responses++; 1059 | assert.ok(!err); 1060 | assert.strictEqual(body.code, successBody.code); 1061 | assert.strictEqual(body.text, successBody.text); 1062 | }); 1063 | }; 1064 | 1065 | var Logger = bunyan.createLogger({ 1066 | name: "a bunyan logger", 1067 | streams: [ 1068 | splunkBunyanStream 1069 | ] 1070 | }); 1071 | 1072 | Logger.info("valid event"); 1073 | assert.strictEqual(0, postCount); 1074 | assert.strictEqual(0, flushCount); 1075 | assert.strictEqual(0, responses); 1076 | 1077 | splunkBunyanStream.stream.logger.config = splunkBunyanStream.stream.logger._initializeConfig({ 1078 | batchInterval: 100 1079 | }); 1080 | 1081 | setTimeout(function() { 1082 | assert.strictEqual(1, postCount); 1083 | assert.strictEqual(1, flushCount); 1084 | assert.strictEqual(1, responses); 1085 | done(); 1086 | }, 350); 1087 | }); 1088 | it("should flush first event immediately with maxBatchSize=1", function(done) { 1089 | var config = { 1090 | token: TOKEN 1091 | }; 1092 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1093 | 1094 | var postCount = 0; 1095 | 1096 | // Wrap the _post so we can verify retries 1097 | var post = splunkBunyanStream.stream.logger._post; 1098 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1099 | postCount++; 1100 | post(requestOptions, callback); 1101 | }; 1102 | 1103 | var flushCount = 0; 1104 | var responses = 0; 1105 | 1106 | // Wrap flush so we can verify flushing is attempted 1107 | var flush = splunkBunyanStream.stream.logger.flush; 1108 | splunkBunyanStream.stream.logger.flush = function() { 1109 | flushCount++; 1110 | flush(function(err, resp, body) { 1111 | responses++; 1112 | assert.ok(!err); 1113 | assert.strictEqual(body.code, successBody.code); 1114 | assert.strictEqual(body.text, successBody.text); 1115 | }); 1116 | }; 1117 | 1118 | var Logger = bunyan.createLogger({ 1119 | name: "a bunyan logger", 1120 | streams: [ 1121 | splunkBunyanStream 1122 | ] 1123 | }); 1124 | 1125 | Logger.info("more than 1 byte"); 1126 | 1127 | setTimeout(function() { 1128 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1129 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1130 | assert.strictEqual(postCount, 1); 1131 | assert.strictEqual(flushCount, 1); 1132 | assert.strictEqual(responses, 1); 1133 | done(); 1134 | }, 100); 1135 | }); 1136 | it("should flush first 2 events after maxBatchSize>200", function(done) { 1137 | var config = { 1138 | token: TOKEN, 1139 | maxBatchSize: 200, 1140 | maxBatchCount: 2 1141 | }; 1142 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1143 | 1144 | var postCount = 0; 1145 | 1146 | // Wrap the _post so we can verify retries 1147 | var post = splunkBunyanStream.stream.logger._post; 1148 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1149 | postCount++; 1150 | post(requestOptions, callback); 1151 | }; 1152 | 1153 | var flushCount = 0; 1154 | var responses = 0; 1155 | 1156 | // Wrap flush so we can verify flushing is attempted 1157 | var flush = splunkBunyanStream.stream.logger.flush; 1158 | splunkBunyanStream.stream.logger.flush = function() { 1159 | flushCount++; 1160 | flush(function(err, resp, body) { 1161 | responses++; 1162 | assert.ok(!err); 1163 | assert.strictEqual(body.code, successBody.code); 1164 | assert.strictEqual(body.text, successBody.text); 1165 | }); 1166 | }; 1167 | 1168 | var Logger = bunyan.createLogger({ 1169 | name: "a bunyan logger", 1170 | streams: [ 1171 | splunkBunyanStream 1172 | ] 1173 | }); 1174 | 1175 | Logger.info("more than 1 byte"); 1176 | 1177 | setTimeout(function() { 1178 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1179 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 1180 | 1181 | assert.strictEqual(postCount, 0); 1182 | assert.strictEqual(flushCount, 0); 1183 | assert.strictEqual(responses, 0); 1184 | 1185 | Logger.info("more than 1 byte"); 1186 | }, 300); 1187 | 1188 | setTimeout(function() { 1189 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1190 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1191 | assert.strictEqual(postCount, 1); 1192 | assert.strictEqual(flushCount, 1); 1193 | assert.strictEqual(responses, 1); 1194 | done(); 1195 | }, 400); 1196 | }); 1197 | it("should flush first event after 200ms, with maxBatchSize=200", function(done) { 1198 | var config = { 1199 | token: TOKEN, 1200 | maxBatchSize: 200, 1201 | batchInterval: 200, 1202 | maxBatchCount: 0 1203 | }; 1204 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1205 | 1206 | var postCount = 0; 1207 | 1208 | // Wrap the _post so we can verify retries 1209 | var post = splunkBunyanStream.stream.logger._post; 1210 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1211 | postCount++; 1212 | post(requestOptions, callback); 1213 | }; 1214 | 1215 | var flushCount = 0; 1216 | var responses = 0; 1217 | 1218 | // Wrap flush so we can verify flushing is attempted 1219 | var flush = splunkBunyanStream.stream.logger.flush; 1220 | splunkBunyanStream.stream.logger.flush = function() { 1221 | flushCount++; 1222 | flush(function(err, resp, body) { 1223 | responses++; 1224 | assert.ok(!err); 1225 | assert.strictEqual(body.code, successBody.code); 1226 | assert.strictEqual(body.text, successBody.text); 1227 | }); 1228 | }; 1229 | 1230 | var Logger = bunyan.createLogger({ 1231 | name: "a bunyan logger", 1232 | streams: [ 1233 | splunkBunyanStream 1234 | ] 1235 | }); 1236 | 1237 | Logger.info("more than 1 byte"); 1238 | 1239 | setTimeout(function() { 1240 | assert.ok(splunkBunyanStream.stream.logger._timerID); 1241 | assert.strictEqual(splunkBunyanStream.stream.logger._timerDuration, 200); 1242 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 1243 | 1244 | assert.strictEqual(postCount, 0); 1245 | assert.strictEqual(flushCount, 0); 1246 | assert.strictEqual(responses, 0); 1247 | 1248 | Logger.info("more than 1 byte"); 1249 | }, 150); 1250 | 1251 | setTimeout(function() { 1252 | assert.ok(splunkBunyanStream.stream.logger._timerID); 1253 | assert.strictEqual(splunkBunyanStream.stream.logger._timerDuration, 200); 1254 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1255 | 1256 | assert.strictEqual(postCount, 1); 1257 | assert.strictEqual(flushCount, 1); 1258 | assert.strictEqual(responses, 1); 1259 | done(); 1260 | }, 250); 1261 | }); 1262 | it("should flush first event immediately with maxBatchCount=1", function(done) { 1263 | var config = { 1264 | token: TOKEN, 1265 | maxBatchSize: 123456 1266 | }; 1267 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1268 | 1269 | var postCount = 0; 1270 | 1271 | // Wrap the _post so we can verify retries 1272 | var post = splunkBunyanStream.stream.logger._post; 1273 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1274 | postCount++; 1275 | post(requestOptions, callback); 1276 | }; 1277 | 1278 | var flushCount = 0; 1279 | var responses = 0; 1280 | 1281 | // Wrap flush so we can verify flushing is attempted 1282 | var flush = splunkBunyanStream.stream.logger.flush; 1283 | splunkBunyanStream.stream.logger.flush = function() { 1284 | flushCount++; 1285 | flush(function(err, resp, body) { 1286 | responses++; 1287 | assert.ok(!err); 1288 | assert.strictEqual(body.code, successBody.code); 1289 | assert.strictEqual(body.text, successBody.text); 1290 | }); 1291 | }; 1292 | 1293 | var Logger = bunyan.createLogger({ 1294 | name: "a bunyan logger", 1295 | streams: [ 1296 | splunkBunyanStream 1297 | ] 1298 | }); 1299 | 1300 | Logger.info("more than 1 byte"); 1301 | 1302 | setTimeout(function() { 1303 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1304 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1305 | assert.strictEqual(postCount, 1); 1306 | assert.strictEqual(flushCount, 1); 1307 | assert.strictEqual(responses, 1); 1308 | 1309 | Logger.info("more than 1 byte"); 1310 | }, 300); 1311 | 1312 | setTimeout(function() { 1313 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1314 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1315 | assert.strictEqual(postCount, 2); 1316 | assert.strictEqual(flushCount, 2); 1317 | assert.strictEqual(responses, 2); 1318 | done(); 1319 | }, 400); 1320 | }); 1321 | it("should flush first 2 events after maxBatchSize>200", function(done) { 1322 | var config = { 1323 | token: TOKEN, 1324 | maxBatchSize: 200, 1325 | maxBatchCount: 2 1326 | }; 1327 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1328 | 1329 | var postCount = 0; 1330 | 1331 | // Wrap the _post so we can verify retries 1332 | var post = splunkBunyanStream.stream.logger._post; 1333 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1334 | postCount++; 1335 | post(requestOptions, callback); 1336 | }; 1337 | 1338 | var flushCount = 0; 1339 | var responses = 0; 1340 | 1341 | // Wrap flush so we can verify flushing is attempted 1342 | var flush = splunkBunyanStream.stream.logger.flush; 1343 | splunkBunyanStream.stream.logger.flush = function() { 1344 | flushCount++; 1345 | flush(function(err, resp, body) { 1346 | responses++; 1347 | assert.ok(!err); 1348 | assert.strictEqual(body.code, successBody.code); 1349 | assert.strictEqual(body.text, successBody.text); 1350 | }); 1351 | }; 1352 | 1353 | var Logger = bunyan.createLogger({ 1354 | name: "a bunyan logger", 1355 | streams: [ 1356 | splunkBunyanStream 1357 | ] 1358 | }); 1359 | 1360 | Logger.info("more than 1 byte"); 1361 | 1362 | setTimeout(function() { 1363 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1364 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 1365 | 1366 | assert.strictEqual(postCount, 0); 1367 | assert.strictEqual(flushCount, 0); 1368 | assert.strictEqual(responses, 0); 1369 | 1370 | Logger.info("more than 1 byte"); 1371 | }, 300); 1372 | 1373 | setTimeout(function() { 1374 | assert.ok(!splunkBunyanStream.stream.logger._timerID); 1375 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1376 | assert.strictEqual(postCount, 1); 1377 | assert.strictEqual(flushCount, 1); 1378 | assert.strictEqual(responses, 1); 1379 | done(); 1380 | }, 400); 1381 | }); 1382 | it("should flush first event after 200ms, with maxBatchSize=200", function(done) { 1383 | var config = { 1384 | token: TOKEN, 1385 | maxBatchSize: 200, 1386 | batchInterval: 200, 1387 | maxBatchCount: 10 1388 | }; 1389 | var splunkBunyanStream = SplunkBunyan.createStream(config); 1390 | 1391 | var postCount = 0; 1392 | 1393 | // Wrap the _post so we can verify retries 1394 | var post = splunkBunyanStream.stream.logger._post; 1395 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 1396 | postCount++; 1397 | post(requestOptions, callback); 1398 | }; 1399 | 1400 | var flushCount = 0; 1401 | var responses = 0; 1402 | 1403 | // Wrap flush so we can verify flushing is attempted 1404 | var flush = splunkBunyanStream.stream.logger.flush; 1405 | splunkBunyanStream.stream.logger.flush = function() { 1406 | flushCount++; 1407 | flush(function(err, resp, body) { 1408 | responses++; 1409 | assert.ok(!err); 1410 | assert.strictEqual(body.code, successBody.code); 1411 | assert.strictEqual(body.text, successBody.text); 1412 | }); 1413 | }; 1414 | 1415 | var Logger = bunyan.createLogger({ 1416 | name: "a bunyan logger", 1417 | streams: [ 1418 | splunkBunyanStream 1419 | ] 1420 | }); 1421 | 1422 | Logger.info("more than 1 byte"); 1423 | 1424 | setTimeout(function() { 1425 | assert.ok(splunkBunyanStream.stream.logger._timerID); 1426 | assert.strictEqual(splunkBunyanStream.stream.logger._timerDuration, 200); 1427 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 1); 1428 | 1429 | assert.strictEqual(postCount, 0); 1430 | assert.strictEqual(flushCount, 0); 1431 | assert.strictEqual(responses, 0); 1432 | 1433 | Logger.info("more than 1 byte"); 1434 | }, 150); 1435 | 1436 | setTimeout(function() { 1437 | assert.ok(splunkBunyanStream.stream.logger._timerID); 1438 | assert.strictEqual(splunkBunyanStream.stream.logger._timerDuration, 200); 1439 | assert.strictEqual(splunkBunyanStream.stream.logger.serializedContextQueue.length, 0); 1440 | 1441 | assert.strictEqual(postCount, 1); 1442 | assert.strictEqual(flushCount, 1); 1443 | assert.strictEqual(responses, 1); 1444 | done(); 1445 | }, 250); 1446 | }); 1447 | }); 1448 | -------------------------------------------------------------------------------- /test/test_config.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | var splunkBunyan = require("../index"); 17 | var assert = require("assert"); 18 | 19 | /** Unit tests **/ 20 | 21 | describe("createStream", function() { 22 | it("should error without config", function() { 23 | try { 24 | splunkBunyan.createStream(); 25 | assert.ok(false, "Expected an error."); 26 | } 27 | catch(err) { 28 | assert.ok(err); 29 | assert.strictEqual(err.message, "Config is required."); 30 | } 31 | }); 32 | it("should error without config", function() { 33 | var config = {}; 34 | try { 35 | splunkBunyan.createStream(config); 36 | assert.ok(false, "Expected an error."); 37 | } 38 | catch(err) { 39 | assert.ok(err); 40 | assert.strictEqual(err.message, "Config object must have a token."); 41 | } 42 | }); 43 | it("should create logger with minimal config", function() { 44 | var config = { 45 | token: "a-token-goes-here-usually" 46 | }; 47 | var splunkBunyanStream = splunkBunyan.createStream(config); 48 | 49 | assert.ok(splunkBunyanStream); 50 | assert.strictEqual("info", splunkBunyanStream.level); 51 | assert.strictEqual("raw", splunkBunyanStream.type); 52 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 53 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 54 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 55 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 56 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 57 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 58 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 59 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 60 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 61 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 62 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 63 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 64 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxBatchSize); 65 | assert.strictEqual(1, splunkBunyanStream.stream.config().maxBatchCount); 66 | }); 67 | it("should create logger with manual batching enabled", function() { 68 | var config = { 69 | token: "a-token-goes-here-usually" 70 | }; 71 | var splunkBunyanStream = splunkBunyan.createStream(config); 72 | 73 | assert.ok(splunkBunyanStream); 74 | assert.strictEqual("info", splunkBunyanStream.level); 75 | assert.strictEqual("raw", splunkBunyanStream.type); 76 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 77 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 78 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 79 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 80 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 81 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 82 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 83 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 84 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 85 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 86 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 87 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 88 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxBatchSize); 89 | assert.strictEqual(1, splunkBunyanStream.stream.config().maxBatchCount); 90 | }); 91 | it("should create logger with non-default name", function() { 92 | var config = { 93 | token: "a-token-goes-here-usually", 94 | name: "different name" 95 | }; 96 | var splunkBunyanStream = splunkBunyan.createStream(config); 97 | 98 | assert.ok(splunkBunyanStream); 99 | assert.strictEqual("info", splunkBunyanStream.level); 100 | assert.strictEqual("raw", splunkBunyanStream.type); 101 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 102 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 103 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 104 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 105 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 106 | assert.strictEqual(config.name, splunkBunyanStream.stream.config().name); 107 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 108 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 109 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 110 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 111 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 112 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 113 | }); 114 | it("should create logger with non-default path", function() { 115 | var config = { 116 | token: "a-token-goes-here-usually", 117 | path: "/services/collector/different/1.0" 118 | }; 119 | var splunkBunyanStream = splunkBunyan.createStream(config); 120 | 121 | assert.ok(splunkBunyanStream); 122 | assert.strictEqual("info", splunkBunyanStream.level); 123 | assert.strictEqual("raw", splunkBunyanStream.type); 124 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 125 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 126 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 127 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 128 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 129 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 130 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 131 | assert.strictEqual(config.path, splunkBunyanStream.stream.config().path); 132 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 133 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 134 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 135 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 136 | }); 137 | it("should create logger with maxRetries=5", function() { 138 | var config = { 139 | token: "a-token-goes-here-usually", 140 | maxRetries: 5 141 | }; 142 | var splunkBunyanStream = splunkBunyan.createStream(config); 143 | 144 | assert.ok(splunkBunyanStream); 145 | assert.strictEqual("info", splunkBunyanStream.level); 146 | assert.strictEqual("raw", splunkBunyanStream.type); 147 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 148 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 149 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 150 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 151 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 152 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 153 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 154 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 155 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 156 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 157 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 158 | assert.strictEqual(5, splunkBunyanStream.stream.config().maxRetries); 159 | }); 160 | it("should create logger with maxBatchSize=100", function() { 161 | var config = { 162 | token: "a-token-goes-here-usually", 163 | maxBatchSize: 100 164 | }; 165 | var splunkBunyanStream = splunkBunyan.createStream(config); 166 | 167 | assert.ok(splunkBunyanStream); 168 | assert.strictEqual("info", splunkBunyanStream.level); 169 | assert.strictEqual("raw", splunkBunyanStream.type); 170 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 171 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 172 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 173 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 174 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 175 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 176 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 177 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 178 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 179 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 180 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 181 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 182 | assert.strictEqual(100, splunkBunyanStream.stream.config().maxBatchSize); 183 | }); 184 | it("should create logger with maxBatchCount=10", function() { 185 | var config = { 186 | token: "a-token-goes-here-usually", 187 | maxBatchCount: 10 188 | }; 189 | var splunkBunyanStream = splunkBunyan.createStream(config); 190 | 191 | assert.ok(splunkBunyanStream); 192 | assert.strictEqual("info", splunkBunyanStream.level); 193 | assert.strictEqual("raw", splunkBunyanStream.type); 194 | assert.ok(splunkBunyanStream.hasOwnProperty("stream")); 195 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("logger")); 196 | assert.ok(splunkBunyanStream.stream.logger.hasOwnProperty("config")); 197 | assert.ok(splunkBunyanStream.stream.hasOwnProperty("send")); 198 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 199 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 200 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 201 | assert.strictEqual("/services/collector/event/1.0", splunkBunyanStream.stream.config().path); 202 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 203 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 204 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 205 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 206 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxBatchSize); 207 | assert.strictEqual(10, splunkBunyanStream.stream.config().maxBatchCount); 208 | }); 209 | }); -------------------------------------------------------------------------------- /test/test_stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Splunk, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"): you may 5 | * not use this file except in compliance with the License. You may obtain 6 | * a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | 17 | var splunkBunyan = require("../index"); 18 | var assert = require("assert"); 19 | var needle = require("needle"); 20 | 21 | /** Integration Tests **/ 22 | 23 | var TOKEN; 24 | 25 | var successBody = { 26 | text: "Success", 27 | code: 0 28 | }; 29 | 30 | var invalidTokenBody = { 31 | text: "Invalid token", 32 | code: 4 33 | }; 34 | 35 | var ____consoleLog = console.log; 36 | function mute() { 37 | console.log = function(){}; 38 | } 39 | function unmute() { 40 | console.log = ____consoleLog; 41 | } 42 | 43 | function formatForBunyan(data) { 44 | var ret = { 45 | name: "a bunyan logger", 46 | hostname: "Shaqbook-15.local", 47 | pid: 37509, 48 | level: 30, 49 | msg: "", 50 | time: Date.now() / 1000, // The / 1000 part is for Splunk 51 | v: 0 52 | }; 53 | 54 | if (typeof data === "string") { 55 | ret.msg = data; 56 | } 57 | else { 58 | for (var key in data) { 59 | ret[key] = data[key]; 60 | } 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | describe("Setup Splunk on localhost:8089 HEC", function() { 67 | it("should be enabled", function(done) { 68 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http/http/enable?output_mode=json", null, {rejectUnauthorized: false}, function(err) { 69 | assert.ok(!err); 70 | done(); 71 | }); 72 | }); 73 | it("should create a token in test/config.json", function(done) { 74 | needle.post("https://admin:changed!@localhost:8089/servicesNS/admin/splunk_httpinput/data/inputs/http?output_mode=json", { name : "splunk_logging" + Date.now()}, {rejectUnauthorized: false}, function(err, resp, body) { 75 | assert.ok(!err); 76 | body = JSON.stringify(body); 77 | var tokenStart = body.indexOf("\"token\":\""); 78 | var tokenEnd = tokenStart + 36; // 36 = guid length 79 | var token = body.substring(tokenStart + 9, tokenEnd + 9); // 9 = prefix length of \"token\":\" 80 | assert.strictEqual(token.length, 36); 81 | TOKEN = token; 82 | done(); 83 | }); 84 | }); 85 | it("should have the env variable set", function() { 86 | assert.ok(TOKEN); 87 | assert.strictEqual(TOKEN.length, 36); 88 | }); 89 | }); 90 | 91 | describe("SplunkStream", function() { 92 | 93 | it("should write a string", function(done) { 94 | var config = { 95 | token: TOKEN, }; 96 | 97 | var splunkBunyanStream = splunkBunyan.createStream(config); 98 | 99 | assert.ok(splunkBunyanStream); 100 | assert.strictEqual("info", splunkBunyanStream.level); 101 | assert.strictEqual("raw", splunkBunyanStream.type); 102 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 103 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 104 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 105 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 106 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 107 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 108 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxRetries); 109 | assert.strictEqual(1, splunkBunyanStream.stream.config().maxBatchCount); 110 | assert.strictEqual(0, splunkBunyanStream.stream.config().maxBatchSize); 111 | 112 | var sendCallback = splunkBunyanStream.stream.send; 113 | splunkBunyanStream.stream.send = function(err, resp, body) { 114 | assert.strictEqual(body.text, successBody.text); 115 | assert.strictEqual(body.code, successBody.code); 116 | sendCallback(err, resp, body); 117 | done(); 118 | }; 119 | 120 | var data = "something"; 121 | 122 | splunkBunyanStream.stream.write(formatForBunyan(data)); 123 | }); 124 | it("should error when writing a string with bad token", function(done) { 125 | var config = { 126 | token: "bad-token", 127 | maxBatchCount: 1 128 | }; 129 | 130 | var splunkBunyanStream = splunkBunyan.createStream(config); 131 | 132 | assert.ok(splunkBunyanStream); 133 | assert.strictEqual("info", splunkBunyanStream.level); 134 | assert.strictEqual("raw", splunkBunyanStream.type); 135 | assert.strictEqual(config.token, splunkBunyanStream.stream.config().token); 136 | assert.strictEqual("splunk-bunyan-logger/0.11.0", splunkBunyanStream.stream.config().name); 137 | assert.strictEqual("localhost", splunkBunyanStream.stream.config().host); 138 | assert.strictEqual("https", splunkBunyanStream.stream.config().protocol); 139 | assert.strictEqual("info", splunkBunyanStream.stream.config().level); 140 | assert.strictEqual(8088, splunkBunyanStream.stream.config().port); 141 | 142 | var run = false; 143 | 144 | splunkBunyanStream.on("error", function(err, errContext) { 145 | run = true; 146 | assert.ok(err); 147 | assert.strictEqual(err.message, invalidTokenBody.text); 148 | assert.strictEqual(err.code, invalidTokenBody.code); 149 | assert.ok(errContext); 150 | 151 | var body = JSON.parse(errContext.message).event; 152 | assert.strictEqual(body.message.msg, "something"); 153 | assert.strictEqual(body.severity, "info"); 154 | }); 155 | 156 | var sendCallback = splunkBunyanStream.stream.send; 157 | splunkBunyanStream.stream.send = function(err, resp, body) { 158 | assert.ok(!err); 159 | assert.ok(run); 160 | assert.strictEqual(body.text, invalidTokenBody.text); 161 | assert.strictEqual(body.code, invalidTokenBody.code); 162 | sendCallback(err, resp, body); 163 | unmute(); 164 | done(); 165 | }; 166 | 167 | var data = "something"; 168 | 169 | mute(); 170 | splunkBunyanStream.stream.write(formatForBunyan(data)); 171 | }); 172 | it("should emit error when writing without args", function(done) { 173 | var config = { 174 | token: TOKEN, 175 | maxBatchCount: 1 176 | }; 177 | var splunkBunyanStream = splunkBunyan.createStream(config); 178 | 179 | splunkBunyanStream.stream.on("error", function(err) { 180 | assert.ok(err); 181 | assert.strictEqual(err.message, "Must pass a parameter to write."); 182 | done(); 183 | }); 184 | splunkBunyanStream.stream.write(); 185 | }); 186 | it("should not retry on Splunk error", function(done) { 187 | var config = { 188 | token: "bad-token", 189 | maxRetries: 5, 190 | maxBatchCount: 1 191 | }; 192 | var splunkBunyanStream = splunkBunyan.createStream(config); 193 | 194 | var retryCount = 0; 195 | 196 | // Wrap the _post so we can verify retries 197 | var post = splunkBunyanStream.stream.logger._post; 198 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 199 | retryCount++; 200 | post(requestOptions, callback); 201 | }; 202 | 203 | splunkBunyanStream.stream.on("error", function(err) { 204 | assert.ok(err); 205 | assert.strictEqual(invalidTokenBody.code, err.code); 206 | assert.strictEqual(invalidTokenBody.text, err.message); 207 | assert.strictEqual(1, retryCount); 208 | unmute(); 209 | done(); 210 | }); 211 | 212 | mute(); 213 | splunkBunyanStream.stream.write("something"); 214 | }); 215 | it("should retry on network error, bad host", function(done) { 216 | this.timeout(3 * 1000); 217 | var config = { 218 | token: TOKEN, 219 | maxRetries: 3, 220 | host: "splunk.invalid", 221 | maxBatchCount: 1 222 | }; 223 | var splunkBunyanStream = splunkBunyan.createStream(config); 224 | 225 | var retryCount = 0; 226 | 227 | // Wrap the _post so we can verify retries 228 | var post = splunkBunyanStream.stream.logger._post; 229 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 230 | retryCount++; 231 | post(requestOptions, callback); 232 | }; 233 | 234 | splunkBunyanStream.stream.on("error", function(err) { 235 | assert.ok(err); 236 | assert.strictEqual(true,["ENOTFOUND","EAI_AGAIN"].includes(err.code)); 237 | assert.strictEqual(config.maxRetries + 1, retryCount); 238 | unmute(); 239 | done(); 240 | }); 241 | 242 | mute(); 243 | splunkBunyanStream.stream.write("something"); 244 | }); 245 | it("should retry on network error, wrong port", function(done) { 246 | this.timeout(3 * 1000); 247 | var config = { 248 | token: TOKEN, 249 | maxRetries: 3, 250 | port: 1075, 251 | maxBatchCount: 1 252 | }; 253 | var splunkBunyanStream = splunkBunyan.createStream(config); 254 | 255 | var retryCount = 0; 256 | 257 | // Wrap the _post so we can verify retries 258 | var post = splunkBunyanStream.stream.logger._post; 259 | splunkBunyanStream.stream.logger._post = function(requestOptions, callback) { 260 | retryCount++; 261 | post(requestOptions, callback); 262 | }; 263 | 264 | splunkBunyanStream.stream.on("error", function(err) { 265 | assert.ok(err); 266 | assert.strictEqual("ECONNREFUSED", err.code); 267 | assert.strictEqual(config.maxRetries + 1, retryCount); 268 | unmute(); 269 | done(); 270 | }); 271 | 272 | mute(); 273 | splunkBunyanStream.stream.write("something"); 274 | }); 275 | // TODO: add some tests for batching (interval, size, count) 276 | }); --------------------------------------------------------------------------------