├── .github └── workflows │ ├── ci.yml │ ├── commitlint.yml │ └── release.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .reuse └── dep5 ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── examples ├── README.md ├── extension-wrapper │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── src │ │ ├── commands.ts │ │ ├── extension.ts │ │ └── logger.ts │ └── tsconfig.json ├── extension │ ├── .gitignore │ ├── .vscode │ │ ├── launch.json │ │ ├── settings.json │ │ └── tasks.json │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── commands.js │ │ ├── extension.js │ │ ├── logger-wrapper.js │ │ ├── passing-logger-to-library.js │ │ ├── settings-changes-handler.js │ │ └── settings.js │ └── package.json └── library │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ └── package.json ├── lerna.json ├── package.json ├── packages ├── logger │ ├── .mocharc.js │ ├── .npmignore │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── README.md │ ├── api.d.ts │ ├── lib │ │ ├── api.js │ │ ├── format.js │ │ ├── levels.js │ │ ├── logger.js │ │ └── transports │ │ │ ├── console-output.js │ │ │ ├── rolling-file.js │ │ │ └── vscode-out-channel.js │ ├── package.json │ ├── test │ │ ├── api-spec.js │ │ ├── child-logger-spec.js │ │ ├── console-output-spec.js │ │ ├── levels-spec.js │ │ ├── log-methods-apis-spec.js │ │ ├── out-channel-spec.js │ │ ├── output-channel-spec.ts │ │ ├── rolling-file-spec.js │ │ └── stubs │ │ │ ├── stream-roller-stub.js │ │ │ └── vscode-stub.js │ └── tsconfig.json ├── types │ ├── .npmignore │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── README.md │ ├── api.d.ts │ ├── package.json │ └── tsconfig.json └── wrapper │ ├── .npmignore │ ├── CHANGELOG.md │ ├── CONTRIBUTING.md │ ├── README.md │ ├── api.d.ts │ ├── nyc.config.js │ ├── package.json │ ├── src │ ├── api.ts │ ├── configure-logger.ts │ ├── helper-types.d.ts │ ├── noop-logger.ts │ ├── settings-changes-handler.ts │ └── settings.ts │ ├── test │ ├── configure-logger-spec.ts │ ├── noop-logger-spec.ts │ ├── settings-changes-handler-spec.ts │ └── settings-spec.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── scripts └── merge-coverage.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the master branch 5 | # See: https://github.community/t/duplicate-checks-on-push-and-pull-request-simultaneous-event/18012 6 | push: 7 | branches: 8 | - master* 9 | pull_request: 10 | branches: 11 | - master* 12 | 13 | jobs: 14 | build: 15 | name: Full Build (node ${{ matrix.node_version }}) 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | node_version: 20 | - 18.x 21 | - 20.x 22 | # https://stackoverflow.com/questions/61070925/github-actions-disable-auto-cancel-when-job-fails 23 | fail-fast: false 24 | 25 | steps: 26 | # using `v1` because of: https://github.com/actions/checkout/issues/246 27 | - uses: actions/checkout@v1 28 | - uses: actions/setup-node@v4 29 | with: 30 | node-version: ${{ matrix.node_version }} 31 | 32 | - uses: pnpm/action-setup@v2.4.0 33 | with: 34 | version: 8.14.0 35 | 36 | - name: Install dependencies 37 | run: pnpm install 38 | 39 | - name: Build 40 | run: pnpm run ci 41 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: 3 | # Trigger the workflow on push or pull request, 4 | # but only for the main branch 5 | # See: https://github.community/t/duplicate-checks-on-push-and-pull-request-simultaneous-event/18012 6 | push: 7 | branches: 8 | - master 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | commitlint: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | # TODO: v5 requires lerna 5.0.0 21 | - uses: wagoid/commitlint-github-action@v4 22 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | tags: 5 | - "v*.*.*" 6 | jobs: 7 | release: 8 | runs-on: ubuntu-latest 9 | steps: 10 | # using `v1` because of: https://github.com/actions/checkout/issues/246 11 | - uses: actions/checkout@v1 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: lts/* 15 | 16 | - uses: pnpm/action-setup@v2.4.0 17 | with: 18 | version: 8.14.0 19 | 20 | - name: Install dependencies 21 | run: pnpm install 22 | 23 | - name: Build 24 | run: pnpm run ci 25 | 26 | - name: npm auth setup 27 | # we are using `lerna` to publish which does not use `pnpm` 28 | # so the auth setup is done with `npm` commands instead of `pnpm` 29 | # note we are using `>` instead of `>>` and replace the whole `.npmrc` contents 30 | run: | 31 | npm logout || true 32 | echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc 33 | npm whoami 34 | env: 35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | 37 | - name: Publish to NPM 38 | run: pnpm run release:publish 39 | 40 | - name: Create Github Release 41 | uses: softprops/action-gh-release@v1 42 | 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # sub-packages files for https://reuse.software (avoid duplication) 2 | .reuse 3 | LICENSES 4 | 5 | # root files for https://reuse.software 6 | !/.reuse 7 | !/LICENSES 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | 68 | # next.js build output 69 | .next 70 | 71 | # IDE 72 | .idea 73 | 74 | # NYC 75 | .nyc_output 76 | coverage 77 | 78 | # we are using yarn instead of npm 79 | package-lock.json 80 | 81 | # test related artifacts 82 | test/.log-out 83 | test/.log-out/* 84 | 85 | # compiled sources 86 | dist 87 | tsconfig.tsbuildinfo 88 | 89 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit "$1" 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install lint-staged -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: vscode-logging 3 | Upstream-Contact: Eliran Azuri 4 | Source: https://github.com/SAP/vscode-logging 5 | 6 | Files: * 7 | Copyright: 2021-2024 SAP SE or an SAP affiliate company. All rights reserved. 8 | License: Apache-2.0 9 | 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/SAP/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) 11 | - convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/SAP/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) 12 | - source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/SAP/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/SAP/vscode-logging/issues/15) 13 | - typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) 14 | - update yarn.lock ([#144](https://github.com/SAP/vscode-logging/issues/144)) ([33ec5ac](https://github.com/SAP/vscode-logging/commit/33ec5acfda275b9717a5986012667b8e854c6c5e)) 15 | - upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/SAP/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) 16 | - **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/SAP/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) 17 | 18 | ### Features 19 | 20 | - **wrapper:** remove dep to @types/vscode ([#167](https://github.com/SAP/vscode-logging/issues/167)) ([b362e5c](https://github.com/SAP/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) 21 | - logger wrapper package ([#163](https://github.com/SAP/vscode-logging/issues/163)) ([fc6abc5](https://github.com/SAP/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) 22 | - support console as log target ([#109](https://github.com/SAP/vscode-logging/issues/109)) ([ea16211](https://github.com/SAP/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) 23 | - **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/SAP/vscode-logging/issues/76)) ([482c708](https://github.com/SAP/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) 24 | - aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/SAP/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) 25 | - initial Commit ([ee780af](https://github.com/SAP/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 26 | 27 | ### Reverts 28 | 29 | - Revert "chore: include release number in release commit" ([d9d9c68](https://github.com/SAP/vscode-logging/commit/d9d9c68471476be517a06b3dd8712d573d3f7fa6)) 30 | 31 | * Make OutputChannel optional. (#55) ([b7fa564](https://github.com/SAP/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/SAP/vscode-logging/issues/55) [#55](https://github.com/SAP/vscode-logging/issues/55) 32 | 33 | ### BREAKING CHANGES 34 | 35 | - **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties 36 | were removed from the `configureLogger` public API 37 | - OutputChannel will not be created inside the logger as today. Alternatively, it 38 | will be added as an optional parameter. The VS Code Extension will be able to create the 39 | OutputChannel and send it to the logger as a parameter. 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | This is the common top level contribution guide for this mono-repo. 4 | A sub-package **may** have an additional CONTRIBUTING.md file if needed. 5 | 6 | ## Legal 7 | 8 | All contributors must sign the Developer Certificate of Origin (DCO) 9 | 10 | - https://cla-assistant.io/SAP/vscode-logging 11 | 12 | SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). 13 | 14 | ## [Guideline for AI-generated code contributions to SAP Open Source Software Projects](https://github.com/SAP/.github/blob/main/CONTRIBUTING_USING_GENAI.md) 15 | 16 | ## Pull Request Guidelines 17 | 18 | When **contributing a New Feature** make sure to: 19 | 20 | - use the `feat:` prefix in the [commit message header](#committing-changes). 21 | - Add relevant test cases to the PR. 22 | - Describe the reason for this new feature in the PR (The **Why**, not just **How**...) 23 | - For significant changes it is recommended to first open an issue to discuss the proposed feature 24 | and ensure it fits the project's goals. 25 | 26 | When **Fixing a Bug**: 27 | 28 | - use the `fix:` prefix in the [commit message header](#committing-changes). 29 | - Add a detailed description of the bug to the PR or open a new issue with the bug's description (if none exists). 30 | - Link the PR and Commit message to the bug using 31 | 32 | In general: 33 | 34 | - Keep your PRs focused on a single topic. 35 | - It is fine if there are multiple commits in a PR, but they must all be related to a single issue 36 | and then be squashed into a single commit when merged to master. 37 | - Remember to Sign the [DCO](#legal) in the PR 38 | - You will be automatically asked this the first time you contribute to this project via a PR comment. 39 | 40 | ## Development Environment 41 | 42 | ### pre-requisites 43 | 44 | - [pnpm](https://pnpm.io/installation) >= 8 45 | - An [LTS version](https://nodejs.org/en/about/releases/) of node.js 46 | - This package is targeted and tested on modern/supported versions of node.js only. 47 | 48 | ### Initial Setup 49 | 50 | The initial setup is trivial: 51 | 52 | - clone this repo 53 | - `pnpm i` 54 | 55 | ### Committing Changes 56 | 57 | This project enforces [Angular style](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit) commit message conventions 58 | using a pre-commit hook. 59 | 60 | ### Formatting. 61 | 62 | [Prettier](https://prettier.io/) is used to ensure consistent code formatting in this repository. 63 | This is normally transparent as it automatically activated in a pre-commit hook using [lint-staged](https://github.com/okonet/lint-staged). 64 | However this does mean that dev flows that do not use a full dev env (e.g editing directly on github) 65 | may result in voter failures due to formatting errors. 66 | 67 | ### Testing 68 | 69 | [Mocha][mocha] is used for unit-testing and [Istanbul/Nyc][istanbul] for coverage reports. 70 | Jest was avoided due to increased total tests execution time due to running the tests in multiple processes, 71 | as the Parser initialization (which happens once per process) can take 10-20ms. 72 | 73 | [mocha]: https://mochajs.org/ 74 | [istanbul]: https://istanbul.js.org/ 75 | 76 | - To run the tests run `pnpm test` in a specific subpackage. 77 | 78 | ### Test Coverage 79 | 80 | 100%\* Test Coverage is enforced for all productive code in this mono repo. 81 | 82 | - Specific statements/functions may be [excluded][ignore_coverage] from the report but the reason for that must 83 | specified in the source code. 84 | 85 | [ignore_coverage]: https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md 86 | 87 | ### Full Build 88 | 89 | This project does not use any compilation step (Babel/TypeScript), this means that the full build 90 | does not generate any artifacts for runtime. 91 | 92 | - To run the full **C**ontinuous **I**ntegration build run `pnpm ci` in in either the top level package or a specific subpackage. 93 | 94 | ### Release Life-Cycle. 95 | 96 | This monorepo uses Lerna's [fixed / locked][lerna-mode] mode 97 | and automatically generates the changelog by adhering to [Conventional Commits][cc] 98 | 99 | [lerna-mode]: https://lerna.js.org/docs/features/version-and-publish#fixedlocked-mode-default 100 | [cc]: https://www.conventionalcommits.org/en/v1.0.0/ 101 | 102 | ### Release Process 103 | 104 | Performing a release requires push permissions to this repository. 105 | 106 | - Ensure you are on `master` branch and synced with origin. 107 | - `pnpm run release:version` 108 | - Follow the lerna CLI instructions. 109 | - Track the `release` build on github actions 110 | - Once the `release` build has finished successfully inspect the npm registry to see the new versions 111 | for all the **changed** packages of this mono-repo. 112 | - `npm view [package-name] version` 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 SAP SE or an SAP affiliate company. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, 6 | AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, and distribution 13 | as defined by Sections 1 through 9 of this document. 14 | 15 | 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 18 | owner that is granting the License. 19 | 20 | 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all other entities 23 | that control, are controlled by, or are under common control with that entity. 24 | For the purposes of this definition, "control" means (i) the power, direct 25 | or indirect, to cause the direction or management of such entity, whether 26 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more 27 | of the outstanding shares, or (iii) beneficial ownership of such entity. 28 | 29 | 30 | 31 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions 32 | granted by this License. 33 | 34 | 35 | 36 | "Source" form shall mean the preferred form for making modifications, including 37 | but not limited to software source code, documentation source, and configuration 38 | files. 39 | 40 | 41 | 42 | "Object" form shall mean any form resulting from mechanical transformation 43 | or translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, and conversions to other media types. 45 | 46 | 47 | 48 | "Work" shall mean the work of authorship, whether in Source or Object form, 49 | made available under the License, as indicated by a copyright notice that 50 | is included in or attached to the work (an example is provided in the Appendix 51 | below). 52 | 53 | 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object form, 56 | that is based on (or derived from) the Work and for which the editorial revisions, 57 | annotations, elaborations, or other modifications represent, as a whole, an 58 | original work of authorship. For the purposes of this License, Derivative 59 | Works shall not include works that remain separable from, or merely link (or 60 | bind by name) to the interfaces of, the Work and Derivative Works thereof. 61 | 62 | 63 | 64 | "Contribution" shall mean any work of authorship, including the original version 65 | of the Work and any modifications or additions to that Work or Derivative 66 | Works thereof, that is intentionally submitted to Licensor for inclusion in 67 | the Work by the copyright owner or by an individual or Legal Entity authorized 68 | to submit on behalf of the copyright owner. For the purposes of this definition, 69 | "submitted" means any form of electronic, verbal, or written communication 70 | sent to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, and 72 | issue tracking systems that are managed by, or on behalf of, the Licensor 73 | for the purpose of discussing and improving the Work, but excluding communication 74 | that is conspicuously marked or otherwise designated in writing by the copyright 75 | owner as "Not a Contribution." 76 | 77 | 78 | 79 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 80 | of whom a Contribution has been received by Licensor and subsequently incorporated 81 | within the Work. 82 | 83 | 2. Grant of Copyright License. Subject to the terms and conditions of this 84 | License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 85 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare 86 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute 87 | the Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 90 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 91 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 92 | license to make, have made, use, offer to sell, sell, import, and otherwise 93 | transfer the Work, where such license applies only to those patent claims 94 | licensable by such Contributor that are necessarily infringed by their Contribution(s) 95 | alone or by combination of their Contribution(s) with the Work to which such 96 | Contribution(s) was submitted. If You institute patent litigation against 97 | any entity (including a cross-claim or counterclaim in a lawsuit) alleging 98 | that the Work or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses granted to You 100 | under this License for that Work shall terminate as of the date such litigation 101 | is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the Work or 104 | Derivative Works thereof in any medium, with or without modifications, and 105 | in Source or Object form, provided that You meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or Derivative Works a copy 108 | of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices stating that 111 | You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works that You distribute, 114 | all copyright, patent, trademark, and attribution notices from the Source 115 | form of the Work, excluding those notices that do not pertain to any part 116 | of the Derivative Works; and 117 | 118 | (d) If the Work includes a "NOTICE" text file as part of its distribution, 119 | then any Derivative Works that You distribute must include a readable copy 120 | of the attribution notices contained within such NOTICE file, excluding those 121 | notices that do not pertain to any part of the Derivative Works, in at least 122 | one of the following places: within a NOTICE text file distributed as part 123 | of the Derivative Works; within the Source form or documentation, if provided 124 | along with the Derivative Works; or, within a display generated by the Derivative 125 | Works, if and wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and do not modify the 127 | License. You may add Your own attribution notices within Derivative Works 128 | that You distribute, alongside or as an addendum to the NOTICE text from the 129 | Work, provided that such additional attribution notices cannot be construed 130 | as modifying the License. 131 | 132 | You may add Your own copyright statement to Your modifications and may provide 133 | additional or different license terms and conditions for use, reproduction, 134 | or distribution of Your modifications, or for any such Derivative Works as 135 | a whole, provided Your use, reproduction, and distribution of the Work otherwise 136 | complies with the conditions stated in this License. 137 | 138 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 139 | Contribution intentionally submitted for inclusion in the Work by You to the 140 | Licensor shall be under the terms and conditions of this License, without 141 | any additional terms or conditions. Notwithstanding the above, nothing herein 142 | shall supersede or modify the terms of any separate license agreement you 143 | may have executed with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade names, 146 | trademarks, service marks, or product names of the Licensor, except as required 147 | for reasonable and customary use in describing the origin of the Work and 148 | reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to 151 | in writing, Licensor provides the Work (and each Contributor provides its 152 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 153 | KIND, either express or implied, including, without limitation, any warranties 154 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 155 | A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness 156 | of using or redistributing the Work and assume any risks associated with Your 157 | exercise of permissions under this License. 158 | 159 | 8. Limitation of Liability. In no event and under no legal theory, whether 160 | in tort (including negligence), contract, or otherwise, unless required by 161 | applicable law (such as deliberate and grossly negligent acts) or agreed to 162 | in writing, shall any Contributor be liable to You for damages, including 163 | any direct, indirect, special, incidental, or consequential damages of any 164 | character arising as a result of this License or out of the use or inability 165 | to use the Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all other commercial 167 | damages or losses), even if such Contributor has been advised of the possibility 168 | of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing the Work 171 | or Derivative Works thereof, You may choose to offer, and charge a fee for, 172 | acceptance of support, warranty, indemnity, or other liability obligations 173 | and/or rights consistent with this License. However, in accepting such obligations, 174 | You may act only on Your own behalf and on Your sole responsibility, not on 175 | behalf of any other Contributor, and only if You agree to indemnify, defend, 176 | and hold each Contributor harmless for any liability incurred by, or claims 177 | asserted against, such Contributor by reason of your accepting any such warranty 178 | or additional liability. END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following boilerplate 183 | notice, with the fields enclosed by brackets "[]" replaced with your own identifying 184 | information. (Don't include the brackets!) The text should be enclosed in 185 | the appropriate comment syntax for the file format. We also recommend that 186 | a file or class name and description of purpose be included on the same "printed 187 | page" as the copyright notice for easier identification within third-party 188 | archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | 194 | you may not use this file except in compliance with the License. 195 | 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | 204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 205 | 206 | See the License for the specific language governing permissions and 207 | 208 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Continuous Integration](https://github.com/SAP/vscode-logging/actions/workflows/ci.yml/badge.svg)](https://github.com/SAP/vscode-logging/actions/workflows/ci.yml) 2 | [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) 3 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP/vscode-logging)](https://api.reuse.software/info/github.com/SAP/vscode-logging) 4 | 5 | # VSCode-Logging 6 | 7 | VSCode-Logging is a [mono-repo][mono-repo] containing a set of libraries to be used when implementing logging functionality 8 | in [VSCode Extensions][vscode-ext]. 9 | 10 | It currently contains the following packages: 11 | 12 | - [![npm-vscode-logging-logger][npm-vscode-logging-logger-image]][npm-vscode-logging-logger-url] [@vscode-logging/logger](./packages/logger) A logging library for VSCode Extensions. 13 | - [![npm-vscode-logging-types][npm-vscode-logging-types-image]][npm-vscode-logging-types-url] [@vscode-logging/types](./packages/types) Common Logger Type Signatures to be used in [Dependency Injection][di] Scenarios. 14 | - [![npm-vscode-logging-types][npm-vscode-logging-wrapper-image]][npm-vscode-logging-wrapper-url] [@vscode-logging/wrapper](./packages/wrapper) An optional wrapper and utilities to reduce boiler plate when consuming @vscode-logging/logger. 15 | 16 | [npm-vscode-logging-logger-image]: https://img.shields.io/npm/v/@vscode-logging/logger.svg 17 | [npm-vscode-logging-logger-url]: https://www.npmjs.com/package/@vscode-logging/logger 18 | [npm-vscode-logging-types-image]: https://img.shields.io/npm/v/@vscode-logging/types.svg 19 | [npm-vscode-logging-types-url]: https://www.npmjs.com/package/@vscode-logging/types 20 | [npm-vscode-logging-wrapper-image]: https://img.shields.io/npm/v/@vscode-logging/wrapper.svg 21 | [npm-vscode-logging-wrapper-url]: https://www.npmjs.com/package/@vscode-logging/wrapper 22 | [mono-repo]: https://github.com/babel/babel/blob/master/doc/design/monorepo.md 23 | [vscode-ext]: https://code.visualstudio.com/api/get-started/your-first-extension 24 | [di]: https://en.wikipedia.org/wiki/Dependency_injection 25 | 26 | ## Support 27 | 28 | Please open [issues](https://github.com/SAP/vscode-logging/issues) on github. 29 | 30 | ## Contributing 31 | 32 | See [CONTRIBUTING.md](./CONTRIBUTING.md). 33 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging/examples 2 | 3 | This directory contains examples of using @vscode-logging packages 4 | 5 | The Examples: 6 | 7 | - [Integration into a VSCode Extension Example](./extension) 8 | - [Using a @vscode-logging/logger in a "regular" npm library consumed by a VSCode Extension](./library) 9 | -------------------------------------------------------------------------------- /examples/extension-wrapper/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.vsix 3 | -------------------------------------------------------------------------------- /examples/extension-wrapper/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/dist/src/**/*.js"] 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /examples/extension-wrapper/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": false 3 | } 4 | -------------------------------------------------------------------------------- /examples/extension-wrapper/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/extension-wrapper/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) 11 | 12 | ### Features 13 | 14 | - **wrapper:** remove dep to @types/vscode ([#167](https://github.com/SAP/vscode-logging/issues/167)) ([b362e5c](https://github.com/SAP/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) 15 | - logger wrapper package ([#163](https://github.com/SAP/vscode-logging/issues/163)) ([fc6abc5](https://github.com/SAP/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) 16 | 17 | ### BREAKING CHANGES 18 | 19 | - **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties 20 | were removed from the `configureLogger` public API 21 | 22 | ## [1.0.2](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-wrapper-example@1.0.1...vscode-logging-extension-wrapper-example@1.0.2) (2021-09-12) 23 | 24 | **Note:** Version bump only for package vscode-logging-extension-wrapper-example 25 | 26 | ## [1.0.1](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-wrapper-example@1.0.0...vscode-logging-extension-wrapper-example@1.0.1) (2021-04-13) 27 | 28 | ### Bug Fixes 29 | 30 | - typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) 31 | 32 | # [1.0.0](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-wrapper-example@0.2.0...vscode-logging-extension-wrapper-example@1.0.0) (2021-02-10) 33 | 34 | ### Features 35 | 36 | - **wrapper:** remove dep to @types/vscode ([#167](https://github.com/SAP/vscode-logging/issues/167)) ([b362e5c](https://github.com/SAP/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) 37 | 38 | ### BREAKING CHANGES 39 | 40 | - **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties 41 | were removed from the `configureLogger` public API 42 | 43 | # 0.2.0 (2021-02-07) 44 | 45 | ### Features 46 | 47 | - logger wrapper package ([#163](https://github.com/SAP/vscode-logging/issues/163)) ([fc6abc5](https://github.com/SAP/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) 48 | -------------------------------------------------------------------------------- /examples/extension-wrapper/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging Extension-Wrapper Example 2 | 3 | This folder contains a sample VS code extension that demonstrates 4 | how to integrate @vscode-logging/**wrapper** in a VSCode extension. 5 | 6 | ## Running the Sample 7 | 8 | ### Initial setup (once) 9 | 10 | - `yarn` in this repository's **root**. 11 | 12 | ### Run/Debug the Example Extension in VSCode 13 | 14 | - Open this example folder in VSCode. 15 | - Press F5 in VSCode to start the example Extension. 16 | 17 | ### Basic logging flow 18 | 19 | - Activate the **Hello World Command**: `View Menu -> Command Palette -> Hello World` 20 | - Open the **Output Panel**: `View Menu -> Output` 21 | - Switch to the `logging-wrapper-example` output Channel using the dropdown. 22 | - Inspect the output channel log and note that the location of the log **Files** was written. 23 | - Inspect the contents of the log files using your favorite file system explorer. 24 | 25 | ### Changing logging Configuration. 26 | 27 | - Open the **Settings Page**: `File -> Preferences -> Settings` 28 | - Locate the **Example_Logging: Logging Level** configuration. 29 | - Play with these settings and inspect how the logging output changes. 30 | 31 | ## Points of Interest in this Example: 32 | 33 | - [extension.ts](./src/extension.ts) - `configureLogger` API invocation (inside `activate` function). 34 | - [logger.ts](./src/logger.ts) - State management for the logger instance. 35 | - [commands.ts](./src/commands.ts) - The implementation of the `Hello World` Command. 36 | -------------------------------------------------------------------------------- /examples/extension-wrapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-logging-extension-wrapper-example", 3 | "private": true, 4 | "displayName": "logging-wrapper-example", 5 | "description": "Example using @vscode-logging/logger and @vscode-logging/wrapper in a VSCode Extension", 6 | "version": "2.0.0", 7 | "publisher": "SAP", 8 | "engines": { 9 | "vscode": "^1.52.0" 10 | }, 11 | "activationEvents": [ 12 | "onCommand:extension.helloWorld" 13 | ], 14 | "main": "./dist/src/extension.js", 15 | "scripts": { 16 | "ci": "npm-run-all clean compile", 17 | "clean": "rimraf dist", 18 | "compile": "tsc" 19 | }, 20 | "contributes": { 21 | "commands": [ 22 | { 23 | "command": "extension.helloWorld", 24 | "title": "Hello World" 25 | } 26 | ], 27 | "configuration": { 28 | "type": "object", 29 | "title": "Example_Logging", 30 | "properties": { 31 | "Example_Logging.loggingLevel": { 32 | "type": "string", 33 | "enum": [ 34 | "off", 35 | "fatal", 36 | "error", 37 | "warn", 38 | "info", 39 | "debug", 40 | "trace" 41 | ], 42 | "default": "error", 43 | "description": "The verbosity of logging. The Order is off < fatal < error < warn < info < debug < trace.", 44 | "scope": "window" 45 | }, 46 | "Example_Logging.sourceLocationTracking": { 47 | "type": "boolean", 48 | "default": false, 49 | "description": "Should Source Code Location Info be added to log entries, DANGER - May be very slow, only use in debugging scenarios", 50 | "scope": "window" 51 | } 52 | } 53 | } 54 | }, 55 | "dependencies": { 56 | "@vscode-logging/wrapper": "^2.0.0" 57 | }, 58 | "devDependencies": { 59 | "@types/vscode": "^1.52.0", 60 | "@vscode-logging/types": "^2.0.0" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /examples/extension-wrapper/src/commands.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext, commands, window } from "vscode"; 2 | import { getLogger } from "./logger"; 3 | 4 | export function registerCommands( 5 | subscriptions: ExtensionContext["subscriptions"] 6 | ): void { 7 | let logMessagesCounter = 1; 8 | const commandSubscription = commands.registerCommand( 9 | "extension.helloWorld", 10 | // By providing a real function name rather than a `fat arrow` ( ()=>{} ) 11 | // The `sourceLocationTracking` can provide a meaningful output. 12 | function registerCallback() { 13 | window.showInformationMessage("Hello Cruel World!"); 14 | getLogger().error( 15 | `Hip Hip Hurray, the Command was executed! counter: <${logMessagesCounter++}>` 16 | ); 17 | } 18 | ); 19 | subscriptions.push(commandSubscription); 20 | } 21 | -------------------------------------------------------------------------------- /examples/extension-wrapper/src/extension.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext } from "vscode"; 2 | import { getLogger, initLogger } from "./logger"; 3 | import { registerCommands } from "./commands"; 4 | 5 | export async function activate(context: ExtensionContext): Promise { 6 | await initLogger(context); 7 | registerCommands(context.subscriptions); 8 | getLogger().info("extension is active, hip hip hurray!"); 9 | } 10 | -------------------------------------------------------------------------------- /examples/extension-wrapper/src/logger.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file manages the logger's state. 3 | */ 4 | import { readFile as readFileCallback } from "fs"; 5 | import { promisify } from "util"; 6 | import { resolve } from "path"; 7 | import { ok } from "assert"; 8 | import { ExtensionContext, window } from "vscode"; 9 | import { IChildLogger, IVSCodeExtLogger } from "@vscode-logging/types"; 10 | import { configureLogger, NOOP_LOGGER } from "@vscode-logging/wrapper"; 11 | 12 | const readFile = promisify(readFileCallback); 13 | 14 | // On file load we initialize our logger to `NOOP_LOGGER` 15 | // this is done because the "real" logger cannot be initialized during file load. 16 | // only once the `activate` function has been called in extension.ts 17 | // as the `ExtensionContext` argument to `activate` contains the required `logPath` 18 | let loggerImpel: IVSCodeExtLogger = NOOP_LOGGER; 19 | 20 | export function getLogger(): IChildLogger { 21 | return loggerImpel; 22 | } 23 | 24 | function setLogger(newLogger: IVSCodeExtLogger): void { 25 | loggerImpel = newLogger; 26 | } 27 | 28 | const LOGGING_LEVEL_PROP = "Example_Logging.loggingLevel"; 29 | const SOURCE_LOCATION_PROP = "Example_Logging.sourceLocationTracking"; 30 | 31 | export async function initLogger(context: ExtensionContext): Promise { 32 | const meta = JSON.parse( 33 | await readFile(resolve(context.extensionPath, "package.json"), "utf8") 34 | ); 35 | 36 | // By asserting the existence of the properties in the package.json 37 | // at runtime, we avoid many copy-pasta mistakes... 38 | const configProps = meta?.contributes?.configuration?.properties; 39 | ok(configProps?.[LOGGING_LEVEL_PROP]); 40 | ok(configProps?.[SOURCE_LOCATION_PROP]); 41 | 42 | const extLogger = configureLogger({ 43 | extName: meta.displayName, 44 | logPath: context.logPath, 45 | logOutputChannel: window.createOutputChannel(meta.displayName), 46 | // set to `true` if you also want your VSCode extension to log to the console. 47 | logConsole: false, 48 | loggingLevelProp: LOGGING_LEVEL_PROP, 49 | sourceLocationProp: SOURCE_LOCATION_PROP, 50 | subscriptions: context.subscriptions 51 | }); 52 | 53 | setLogger(extLogger); 54 | } 55 | -------------------------------------------------------------------------------- /examples/extension-wrapper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": ".", 7 | "module": "commonjs", 8 | "lib": ["es7"], 9 | "target": "es2017", 10 | "declaration": true, 11 | "sourceMap": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "strict": true 14 | }, 15 | "include": ["./src/**/*.ts", "./api.d.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /examples/extension/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.vsix 3 | -------------------------------------------------------------------------------- /examples/extension/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": ["--extensionDevelopmentPath=${workspaceFolder}"], 14 | "outFiles": ["${workspaceFolder}/lib/**/*.js"] 15 | }, 16 | { 17 | "name": "Run Extension Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "runtimeExecutable": "${execPath}", 21 | "args": [ 22 | "--extensionDevelopmentPath=${workspaceFolder}", 23 | "--extensionTestsPath=${workspaceFolder}/test" 24 | ], 25 | "outFiles": ["${workspaceFolder}/test/**/*.js"] 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /examples/extension/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.insertSpaces": false 3 | } 4 | -------------------------------------------------------------------------------- /examples/extension/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /examples/extension/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) 11 | 12 | ### Features 13 | 14 | - initial Commit ([ee780af](https://github.com/SAP/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 15 | - support console as log target ([#109](https://github.com/SAP/vscode-logging/issues/109)) ([ea16211](https://github.com/SAP/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) 16 | 17 | * Make OutputChannel optional. (#55) ([b7fa564](https://github.com/SAP/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/SAP/vscode-logging/issues/55) [#55](https://github.com/SAP/vscode-logging/issues/55) 18 | 19 | ### BREAKING CHANGES 20 | 21 | - OutputChannel will not be created inside the logger as today. Alternatively, it 22 | will be added as an optional parameter. The VS Code Extension will be able to create the 23 | OutputChannel and send it to the logger as a parameter. 24 | 25 | ## [1.1.3](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@1.1.2...vscode-logging-extension-example@1.1.3) (2021-04-13) 26 | 27 | ### Bug Fixes 28 | 29 | - typo in the logging.level configuration description ([#181](https://github.com/SAP/vscode-logging/issues/181)) ([c05b17d](https://github.com/SAP/vscode-logging/commit/c05b17d4348e89f27a3cb86f2e20bc190cdf1afb)) 30 | 31 | ## [1.1.2](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@1.1.1...vscode-logging-extension-example@1.1.2) (2020-12-10) 32 | 33 | **Note:** Version bump only for package vscode-logging-extension-example 34 | 35 | ## [1.1.1](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@1.1.0...vscode-logging-extension-example@1.1.1) (2020-11-12) 36 | 37 | **Note:** Version bump only for package vscode-logging-extension-example 38 | 39 | # [1.1.0](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@1.0.1...vscode-logging-extension-example@1.1.0) (2020-07-29) 40 | 41 | ### Features 42 | 43 | - support console as log target ([#109](https://github.com/SAP/vscode-logging/issues/109)) ([ea16211](https://github.com/SAP/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) 44 | 45 | ## [1.0.1](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@1.0.0...vscode-logging-extension-example@1.0.1) (2020-05-21) 46 | 47 | **Note:** Version bump only for package vscode-logging-extension-example 48 | 49 | # [1.0.0](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@0.1.2...vscode-logging-extension-example@1.0.0) (2020-04-16) 50 | 51 | - Make OutputChannel optional. (#55) ([b7fa564](https://github.com/SAP/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/SAP/vscode-logging/issues/55) [#55](https://github.com/SAP/vscode-logging/issues/55) 52 | 53 | ### BREAKING CHANGES 54 | 55 | - OutputChannel will not be created inside the logger as today. Alternatively, it 56 | will be added as an optional parameter. The VS Code Extension will be able to create the 57 | OutputChannel and send it to the logger as a parameter. 58 | 59 | ## [0.1.2](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@0.1.1...vscode-logging-extension-example@0.1.2) (2020-02-19) 60 | 61 | **Note:** Version bump only for package vscode-logging-extension-example 62 | 63 | ## [0.1.1](https://github.com/SAP/vscode-logging/compare/vscode-logging-extension-example@0.1.0...vscode-logging-extension-example@0.1.1) (2020-01-20) 64 | 65 | **Note:** Version bump only for package vscode-logging-extension-example 66 | 67 | # 0.1.0 (2020-01-14) 68 | 69 | ### Features 70 | 71 | - initial Commit ([ee780af](https://github.com/SAP/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 72 | -------------------------------------------------------------------------------- /examples/extension/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging Extension Example 2 | 3 | This folder contains a sample VS code extension that demonstrates 4 | how to integrate @vscode-logging/logger in a VSCode extension. 5 | 6 | ## Running the Sample 7 | 8 | ### Initial setup (once) 9 | 10 | - `yarn` in this repository's **root**. 11 | 12 | ### Run/Debug the Example Extension in VSCode 13 | 14 | - Open this example folder in VSCode. 15 | - Press F5 in VSCode to start the example Extension. 16 | 17 | ### Basic logging flow 18 | 19 | - Activate the **Hello World Command**: `View Menu -> Command Palette -> Hello World` 20 | - Open the **Output Panel**: `View Menu -> Output` 21 | - Switch to the `vscode-logging-extension-example` output Channel using the dropdown. 22 | - Inspect the output channel log and note that the location of the log **Files** was written. 23 | - Inspect the contents of the log files using your favorite file system explorer. 24 | 25 | ### Changing logging Configuration. 26 | 27 | - Open the **Settings Page**: `File -> Preferences -> Settings` 28 | - Locate the **Example_Logging: Logging Level** configuration. 29 | - Play with these settings and inspect how the logging output changes. 30 | 31 | ## Points of Interest in this Example: 32 | 33 | - [extension.js](./lib/extension.js) - The main entry point and where the `activate()` method is implemented. 34 | - [logger-wrapper.js](./lib/logger-wrapper.js) - Access point to the Logger for our VSCode Extension Files/Modules. 35 | - [package.json -> configuration](./package.json) - Declaring end user configurable logging settings. 36 | - [settings.js](./lib/settings.js) - Logic to read the logging configuration from the VSCode settings. 37 | - [settings-changes-handler.js](./lib/settings-changes-handler.js) - Listening to VSCode settings changes and reacting by updating the Logger configuration. 38 | - [passing-logger-to-library.js](./lib/passing-logger-to-library.js) - A simple Dependency Injection of the Logger to a consumed (external) library. 39 | - [commands.js](./lib/commands.js) - The implementation of the `Hello World` Command. 40 | -------------------------------------------------------------------------------- /examples/extension/lib/commands.js: -------------------------------------------------------------------------------- 1 | const vscode = require("vscode"); 2 | 3 | // Our modules consume the logger via the logger-wrapper module 4 | const { getLogger } = require("./logger-wrapper"); 5 | 6 | /** 7 | * 8 | * @param {vscode.ExtensionContext} context 9 | */ 10 | function registerCommands(context) { 11 | let logMessagesCounter = 1; 12 | const commandSubscription = vscode.commands.registerCommand( 13 | "extension.helloWorld", 14 | // By providing a real function name rather then a `fat arrow` ( ()=>{} ) 15 | // The `sourceLocationTracking` can provide a meaningful output. 16 | function registerCallback() { 17 | vscode.window.showInformationMessage("Hello Cruel World!"); 18 | getLogger().error( 19 | `Hip Hip Hurray, the Command was executed! counter: <${logMessagesCounter++}>` 20 | ); 21 | } 22 | ); 23 | context.subscriptions.push(commandSubscription); 24 | } 25 | 26 | module.exports = { 27 | registerCommands: registerCommands 28 | }; 29 | -------------------------------------------------------------------------------- /examples/extension/lib/extension.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require("path"); 2 | const vscode = require("vscode"); 3 | const { getExtensionLogger } = require("@vscode-logging/logger"); 4 | const { 5 | getLoggingLevelSetting, 6 | getSourceLocationTrackingSetting 7 | } = require("./settings"); 8 | const { registerCommands } = require("./commands"); 9 | const { listenToLogSettingsChanges } = require("./settings-changes-handler"); 10 | const { callLibraryAndPassLogger } = require("./passing-logger-to-library"); 11 | const { getLogger, initLogger } = require("./logger-wrapper"); 12 | 13 | /** 14 | * Entry Point for a VSCode Extension. 15 | * 16 | * @param {vscode.ExtensionContext} context 17 | */ 18 | function activate(context) { 19 | // The `logPath` is only available from the `ExtensionContext` 20 | const logPath = context.logPath; 21 | const logLevelSetting = getLoggingLevelSetting(); 22 | const sourceLocationTrackingSettings = getSourceLocationTrackingSetting(); 23 | const meta = require(resolve(__dirname, "..", "package.json")); 24 | const logOutputChannel = vscode.window.createOutputChannel(meta.name); 25 | 26 | // The Logger must first be initialized before any logging commands may be invoked. 27 | const extLogger = getExtensionLogger({ 28 | extName: meta.name, 29 | level: logLevelSetting, 30 | logPath: logPath, 31 | logOutputChannel: logOutputChannel, 32 | sourceLocationTracking: sourceLocationTrackingSettings, 33 | logConsole: true 34 | }); 35 | 36 | // Update our logger-wrapper with a reference to the extLogger. 37 | initLogger(extLogger); 38 | 39 | // Lets log some useful entries... 40 | getLogger().info( 41 | `Extension: <${meta.name}> version: <${meta.version}> has been activated` 42 | ); 43 | getLogger().info(`Rotating Log Files will be written to: <${logPath}>`); 44 | 45 | // Simple Dependency Injection example of using the VSCode Logger in a "consumed" npm library/package. 46 | callLibraryAndPassLogger(); 47 | 48 | getLogger().info(`Testing logging info level`); 49 | getLogger().warn(`Testing logging warn level`); 50 | getLogger().error(`Testing logging error level`); 51 | 52 | registerCommands(context); 53 | 54 | // We must subscribe to settings changes, otherwise the Logger will always remain in its initial state. 55 | listenToLogSettingsChanges(context); 56 | } 57 | 58 | /** 59 | * this method is called when your extension is deactivated 60 | */ 61 | function deactivate() { 62 | getLogger().info( 63 | "Sad Sad Panda: extension was deactivated, why oh why?" 64 | ); 65 | } 66 | 67 | module.exports = { 68 | activate, 69 | deactivate 70 | }; 71 | -------------------------------------------------------------------------------- /examples/extension/lib/logger-wrapper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A Simple Wrapper to hold the state of our "singleton" (per extension) IVSCodeExtLogger 3 | * implementation. 4 | */ 5 | 6 | /** 7 | * @type {IVSCodeExtLogger} 8 | */ 9 | let _logger; 10 | let isInitialized = false; 11 | 12 | /** 13 | * Note the use of a getter function so the value would be lazy resolved on each use. 14 | * This enables concise and simple consumption of the Logger throughout our Extension. 15 | * 16 | * @returns { IVSCodeExtLogger } 17 | */ 18 | function getLogger() { 19 | if (!isInitialized) { 20 | throw Error("Logger has not yet been initialized!"); 21 | } 22 | return _logger; 23 | } 24 | 25 | /** 26 | * This function should be invoked after the Logger has been initialized in the Extension's `activate` function. 27 | * @param {IVSCodeExtLogger} newLogger 28 | */ 29 | function initLogger(newLogger) { 30 | isInitialized = true; 31 | _logger = newLogger; 32 | } 33 | 34 | module.exports = { 35 | getLogger, 36 | initLogger 37 | }; 38 | -------------------------------------------------------------------------------- /examples/extension/lib/passing-logger-to-library.js: -------------------------------------------------------------------------------- 1 | const { Calculator } = require("@vscode-logging/library-example"); 2 | 3 | // Our modules consume the logger via the logger-wrapper module 4 | const { getLogger } = require("./logger-wrapper"); 5 | 6 | function callLibraryAndPassLogger() { 7 | // Using a childLogger will add a suffix to the `label` information in each logEntry. 8 | const calculatorChildLogger = getLogger().getChildLogger({ 9 | label: "Calculator" 10 | }); 11 | 12 | const calc = new Calculator(calculatorChildLogger); 13 | 14 | // These methods will now log the same log targets as our VSCode Extension Logger (OutChannel / Rolling File) 15 | calc.add(1, 2); 16 | calc.subtract(5, 3); 17 | calc.multiply(2, 6); 18 | } 19 | 20 | module.exports = { 21 | callLibraryAndPassLogger 22 | }; 23 | -------------------------------------------------------------------------------- /examples/extension/lib/settings-changes-handler.js: -------------------------------------------------------------------------------- 1 | const vscode = require("vscode"); 2 | 3 | const { 4 | LOGGING_LEVEL_CONFIG_PROP, 5 | SOURCE_TRACKING_CONFIG_PROP 6 | } = require("./settings"); 7 | 8 | // Our modules consume the logger via the logger-wrapper module 9 | const { getLogger } = require("./logger-wrapper"); 10 | 11 | /** 12 | * @param {vscode.ExtensionContext} context 13 | */ 14 | function listenToLogSettingsChanges(context) { 15 | // To enable dynamic logging level we must listen to VSCode configuration changes 16 | // on our `loggingLevelConfigProp` configuration setting. 17 | context.subscriptions.push( 18 | vscode.workspace.onDidChangeConfiguration(e => { 19 | if (e.affectsConfiguration(LOGGING_LEVEL_CONFIG_PROP)) { 20 | const logLevel = vscode.workspace 21 | .getConfiguration() 22 | .get(LOGGING_LEVEL_CONFIG_PROP); 23 | 24 | getLogger().changeLevel(logLevel); 25 | } 26 | }) 27 | ); 28 | 29 | // Enable responding to changes in the sourceLocationTracking setting 30 | context.subscriptions.push( 31 | vscode.workspace.onDidChangeConfiguration(e => { 32 | if (e.affectsConfiguration(SOURCE_TRACKING_CONFIG_PROP)) { 33 | const newSourceLocationTracking = vscode.workspace 34 | .getConfiguration() 35 | .get(SOURCE_TRACKING_CONFIG_PROP); 36 | 37 | getLogger().changeSourceLocationTracking(newSourceLocationTracking); 38 | } 39 | }) 40 | ); 41 | } 42 | 43 | module.exports = { 44 | listenToLogSettingsChanges 45 | }; 46 | -------------------------------------------------------------------------------- /examples/extension/lib/settings.js: -------------------------------------------------------------------------------- 1 | const vscode = require("vscode"); 2 | 3 | /** 4 | * Note that the values of these configuration properties must match those defined in the package.json 5 | */ 6 | const LOGGING_LEVEL_CONFIG_PROP = "Example_Logging.loggingLevel"; 7 | const SOURCE_TRACKING_CONFIG_PROP = "Example_Logging.sourceLocationTracking"; 8 | 9 | /** 10 | * @returns {LogLevel} 11 | */ 12 | function getLoggingLevelSetting() { 13 | return vscode.workspace.getConfiguration().get(LOGGING_LEVEL_CONFIG_PROP); 14 | } 15 | 16 | /** 17 | * @returns {boolean} 18 | */ 19 | function getSourceLocationTrackingSetting() { 20 | return vscode.workspace.getConfiguration().get(SOURCE_TRACKING_CONFIG_PROP); 21 | } 22 | 23 | module.exports = { 24 | LOGGING_LEVEL_CONFIG_PROP, 25 | SOURCE_TRACKING_CONFIG_PROP, 26 | getLoggingLevelSetting, 27 | getSourceLocationTrackingSetting 28 | }; 29 | -------------------------------------------------------------------------------- /examples/extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-logging-extension-example", 3 | "private": true, 4 | "displayName": "logging-example", 5 | "description": "Example using @vscode-logging/logger in a VSCode Extension", 6 | "version": "2.0.0", 7 | "publisher": "SAP", 8 | "engines": { 9 | "vscode": "^1.32.0" 10 | }, 11 | "activationEvents": [ 12 | "onCommand:extension.helloWorld" 13 | ], 14 | "main": "./lib/extension.js", 15 | "contributes": { 16 | "commands": [ 17 | { 18 | "command": "extension.helloWorld", 19 | "title": "Hello World" 20 | } 21 | ], 22 | "configuration": { 23 | "type": "object", 24 | "title": "Example_Logging", 25 | "properties": { 26 | "Example_Logging.loggingLevel": { 27 | "type": "string", 28 | "enum": [ 29 | "off", 30 | "fatal", 31 | "error", 32 | "warn", 33 | "info", 34 | "debug", 35 | "trace" 36 | ], 37 | "default": "info", 38 | "description": "The verbosity of logging. The Order is off < fatal < error < warn < info < debug < trace.", 39 | "scope": "resource" 40 | }, 41 | "Example_Logging.sourceLocationTracking": { 42 | "type": "boolean", 43 | "default": false, 44 | "description": "Should Source Code Location Info be added to log entries, DANGER - May be very slow, only use in debugging scenarios", 45 | "scope": "resource" 46 | } 47 | } 48 | } 49 | }, 50 | "scripts": {}, 51 | "dependencies": { 52 | "@vscode-logging/library-example": "^2.0.0", 53 | "@vscode-logging/logger": "^2.0.0" 54 | }, 55 | "devDependencies": { 56 | "@types/vscode": "^1.32.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /examples/library/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Features 9 | 10 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 11 | 12 | ## [0.1.4](https://github.com/sap/vscode-logging/compare/@vscode-logging/library-example@0.1.3...@vscode-logging/library-example@0.1.4) (2021-04-13) 13 | 14 | **Note:** Version bump only for package @vscode-logging/library-example 15 | 16 | ## [0.1.3](https://github.com/sap/vscode-logging/compare/@vscode-logging/library-example@0.1.2...@vscode-logging/library-example@0.1.3) (2020-11-12) 17 | 18 | **Note:** Version bump only for package @vscode-logging/library-example 19 | 20 | ## [0.1.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/library-example@0.1.1...@vscode-logging/library-example@0.1.2) (2020-02-19) 21 | 22 | **Note:** Version bump only for package @vscode-logging/library-example 23 | 24 | ## [0.1.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/library-example@0.1.0...@vscode-logging/library-example@0.1.1) (2020-01-20) 25 | 26 | **Note:** Version bump only for package @vscode-logging/library-example 27 | 28 | # 0.1.0 (2020-01-14) 29 | 30 | ### Features 31 | 32 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 33 | -------------------------------------------------------------------------------- /examples/library/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging Library DI Example 2 | 3 | This folder contains an npm package example which uses simple constructor argument 4 | [Dependency Injection][di] in order to use the @vscode-logging/logger in an npm library called from a VSCode Extension. 5 | 6 | This "library" is consumed by the extension example's [passing-logger-to-library.js](../extension/lib/passing-logger-to-library.js), 7 | file. 8 | 9 | [di]: https://en.wikipedia.org/wiki/Dependency_injection 10 | -------------------------------------------------------------------------------- /examples/library/index.js: -------------------------------------------------------------------------------- 1 | const NO_OPERATION = () => {}; 2 | 3 | /** 4 | * Empty Implementation of the Logger in case none is provided via Dependency Injection. 5 | * An alternative implementation could log to the console or provide another (real) implementation. 6 | * 7 | * @type {IChildLogger} 8 | */ 9 | const noopLogger = { 10 | fatal: NO_OPERATION, 11 | error: NO_OPERATION, 12 | warn: NO_OPERATION, 13 | info: NO_OPERATION, 14 | debug: NO_OPERATION, 15 | trace: NO_OPERATION, 16 | getChildLogger: function(opts) { 17 | return noopLogger; 18 | } 19 | }; 20 | 21 | class Calculator { 22 | /** 23 | * @param {IChildLogger} [logger] 24 | */ 25 | constructor(logger = noopLogger) { 26 | this.logger = logger; 27 | } 28 | 29 | subtract(lhs, rhs) { 30 | // Adding Metadata Objects to the JSON structured log entry. 31 | this.logger.info("Subtract called with arguments:", { lhs: lhs, rhs: rhs }); 32 | return lhs - rhs; 33 | } 34 | 35 | add(lhs, rhs) { 36 | // Using ECMAScript Template Literals to construct the message (Recommended!) 37 | // - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals 38 | this.logger.warn(`add called with arguments: <${lhs}> + <${rhs}>`); 39 | return lhs + rhs; 40 | } 41 | 42 | multiply(lhs, rhs) { 43 | // using splat style to construct the message 44 | this.logger.error("multiple called with arguments: <%d> + <%d>", lhs, rhs); 45 | return lhs * rhs; 46 | } 47 | } 48 | 49 | module.exports = { 50 | Calculator: Calculator 51 | }; 52 | -------------------------------------------------------------------------------- /examples/library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vscode-logging/library-example", 3 | "version": "2.0.0", 4 | "private": true, 5 | "description": "Example node library using @vscode-logging/logger via Dependency Injection", 6 | "main": "index.js", 7 | "repository": "https://github.com/sap/vscode-logging/", 8 | "license": "Apache-2.0", 9 | "devDependencies": { 10 | "@vscode-logging/types": "^2.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": ["packages/*", "examples/*"], 3 | "useWorkspaces": true, 4 | "npmClient": "npm", 5 | "command": { 6 | "publish": { 7 | "conventionalCommits": true 8 | }, 9 | "version": { 10 | "message": "chore(release): release %s" 11 | } 12 | }, 13 | "version": "2.0.0" 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "workspaces": { 5 | "packages": [ 6 | "packages/*", 7 | "examples/*" 8 | ] 9 | }, 10 | "engines": { 11 | "node": ">=18", 12 | "pnpm": ">=8" 13 | }, 14 | "scripts": { 15 | "version": "pnpm install && git add pnpm-lock.yaml", 16 | "preinstall": "npx only-allow pnpm", 17 | "prepare": "husky install", 18 | "release:version": "lerna version", 19 | "release:publish": "lerna publish from-git --yes --no-verify-access", 20 | "ci": "npm-run-all format:validate ci:subpackages legal:* coverage:merge", 21 | "ci:subpackages": "lerna run ci", 22 | "format:fix": "prettier --write --ignore-path .gitignore \"**/*.@(ts|js|json|md)\"", 23 | "format:validate": "prettier --check --ignore-path .gitignore \"**/*.@(ts|js|json|md)\"", 24 | "coverage:merge": "node ./scripts/merge-coverage", 25 | "legal:copy-reuse": "pnpm -r exec -- shx cp -r ../../.reuse .reuse", 26 | "legal:copy-license": "pnpm -r exec -- shx cp -r ../../LICENSES LICENSES" 27 | }, 28 | "commitlint": { 29 | "extends": [ 30 | "@commitlint/config-conventional" 31 | ] 32 | }, 33 | "lint-staged": { 34 | "*.{js,ts,json,md}": [ 35 | "prettier --write" 36 | ], 37 | "*.{ts,js}": [ 38 | "eslint --fix --max-warnings=0 --ignore-pattern=!.*" 39 | ] 40 | }, 41 | "devDependencies": { 42 | "@commitlint/cli": "18.4.3", 43 | "@commitlint/config-conventional": "18.4.3", 44 | "@types/chai": "4.3.11", 45 | "@types/mocha": "10.0.6", 46 | "@types/node": "18.15.3", 47 | "chai": "4.3.10", 48 | "chai-exclude": "2.1.0", 49 | "cz-conventional-changelog": "3.3.0", 50 | "husky": "8.0.3", 51 | "lerna": "3.20.2", 52 | "lint-staged": "15.2.0", 53 | "mocha": "10.2.0", 54 | "npm-run-all": "4.1.5", 55 | "nyc": "15.1.0", 56 | "prettier": "1.19.1", 57 | "rimraf": "5.0.5", 58 | "shx": "0.3.4", 59 | "typescript": "5.3.3" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/logger/.mocharc.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const chaiExclude = require("chai-exclude"); 3 | 4 | chai.use(chaiExclude); 5 | 6 | module.exports = { 7 | // If coverage is enabled each test takes quite a while 8 | timeout: 5000 9 | }; 10 | -------------------------------------------------------------------------------- /packages/logger/.npmignore: -------------------------------------------------------------------------------- 1 | # Mocha 2 | .mocharc.js 3 | 4 | # TypeScript 5 | tsconfig.json 6 | 7 | # NYC 8 | .nyc_output 9 | nyc.config.js 10 | coverage 11 | 12 | # tests 13 | test 14 | 15 | # Dev Docs 16 | CONTRIBUTING.md -------------------------------------------------------------------------------- /packages/logger/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) 11 | - convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/sap/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) 12 | - **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/sap/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) 13 | - source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/sap/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/sap/vscode-logging/issues/15) 14 | 15 | ### Features 16 | 17 | - support console as log target ([#109](https://github.com/sap/vscode-logging/issues/109)) ([ea16211](https://github.com/sap/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) 18 | - **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/sap/vscode-logging/issues/76)) ([482c708](https://github.com/sap/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) 19 | - aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) 20 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 21 | 22 | * Make OutputChannel optional. (#55) ([b7fa564](https://github.com/sap/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/sap/vscode-logging/issues/55) [#55](https://github.com/sap/vscode-logging/issues/55) 23 | 24 | ### BREAKING CHANGES 25 | 26 | - OutputChannel will not be created inside the logger as today. Alternatively, it 27 | will be added as an optional parameter. The VS Code Extension will be able to create the 28 | OutputChannel and send it to the logger as a parameter. 29 | 30 | ## [1.2.3](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.2...@vscode-logging/logger@1.2.3) (2021-04-13) 31 | 32 | **Note:** Version bump only for package @vscode-logging/logger 33 | 34 | ## [1.2.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.1...@vscode-logging/logger@1.2.2) (2020-12-10) 35 | 36 | **Note:** Version bump only for package @vscode-logging/logger 37 | 38 | ## [1.2.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.2.0...@vscode-logging/logger@1.2.1) (2020-11-12) 39 | 40 | ### Bug Fixes 41 | 42 | - convert timestamp format according to ISO-8601 ([b4ab3e4](https://github.com/sap/vscode-logging/commit/b4ab3e48829df42bd73c67de3f068385aabd1259)) 43 | 44 | # [1.2.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.1.0...@vscode-logging/logger@1.2.0) (2020-07-29) 45 | 46 | ### Features 47 | 48 | - support console as log target ([#109](https://github.com/sap/vscode-logging/issues/109)) ([ea16211](https://github.com/sap/vscode-logging/commit/ea16211a5e2fbcdc86f4e96c8c60eaaf440d2431)) 49 | 50 | # [1.1.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@1.0.0...@vscode-logging/logger@1.1.0) (2020-05-21) 51 | 52 | ### Features 53 | 54 | - **logger:** a memory leak when creating 9 or more ChildLogger's ([#76](https://github.com/sap/vscode-logging/issues/76)) ([482c708](https://github.com/sap/vscode-logging/commit/482c708e9b8643849f6a14253c51650ffac70416)) 55 | 56 | # [1.0.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.2...@vscode-logging/logger@1.0.0) (2020-04-16) 57 | 58 | - Make OutputChannel optional. (#55) ([b7fa564](https://github.com/sap/vscode-logging/commit/b7fa56436693df9787f8ea720559beb3b0566612)), closes [#55](https://github.com/sap/vscode-logging/issues/55) [#55](https://github.com/sap/vscode-logging/issues/55) 59 | 60 | ### BREAKING CHANGES 61 | 62 | - OutputChannel will not be created inside the logger as today. Alternatively, it 63 | will be added as an optional parameter. The VS Code Extension will be able to create the 64 | OutputChannel and send it to the logger as a parameter. 65 | 66 | ## [0.1.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.1...@vscode-logging/logger@0.1.2) (2020-02-19) 67 | 68 | ### Bug Fixes 69 | 70 | - add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) 71 | - **logger:** vscode-logging/types package should be a dependency ([e2ea6c7](https://github.com/sap/vscode-logging/commit/e2ea6c7d26efed219f2b983ad7e601eeb9f4704f)) 72 | 73 | ## [0.1.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/logger@0.1.0...@vscode-logging/logger@0.1.1) (2020-01-20) 74 | 75 | ### Bug Fixes 76 | 77 | - source Location Tracking not affecting ChildLoggers ([72b151a](https://github.com/sap/vscode-logging/commit/72b151a773ba2707cb131d59799389a7cfe93c85)), closes [#15](https://github.com/sap/vscode-logging/issues/15) 78 | 79 | # 0.1.0 (2020-01-14) 80 | 81 | ### Features 82 | 83 | - aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) 84 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 85 | -------------------------------------------------------------------------------- /packages/logger/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | Please see the top level [Contribution Guide](../../CONTRIBUTING.md). 4 | -------------------------------------------------------------------------------- /packages/logger/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging/logger 2 | 3 | A Logging Library for VSCode Extension which supports the following features: 4 | 5 | - JSON structure log entries output. 6 | - Logging to a VSCode outputChannel. 7 | - Logging to rolling file logs. 8 | - [Source Location Tracking](#on-sourcelocationtracking). 9 | 10 | ## Installation 11 | 12 | With npm: 13 | 14 | - `npm install @vscode-logging/logger --save` 15 | 16 | With Yarn: 17 | 18 | - `yarn add @vscode-logging/logger` 19 | 20 | ## Usage 21 | 22 | Please see the [TypeScript Definitions](./api.d.ts) for full API details. 23 | 24 | Runnable and documented usage examples can be found in the [examples](../../examples) folder. 25 | It is recommended to review these examples as integrating @vscode-logging/logger into a 26 | VSCode Extension necessitates **multiple changes**, mainly around managing the configuration options 27 | as user exposed settings. 28 | 29 | ### Basic Usage 30 | 31 | The only function exposed (directly) by @vscode-logging/logger is `getExtensionLogger` which 32 | should be invoked by a VSCode extension's `activate()` function. 33 | 34 | ```javascript 35 | const { getExtensionLogger } = require("@vscode-logging/logger"); 36 | 37 | function activate(context) { 38 | const extLogger = getExtensionLogger({ 39 | extName: "MyExtName", 40 | level: "info", // See LogLevel type in @vscode-logging/types for possible logLevels 41 | logPath: context.logPath, // The logPath is only available from the `vscode.ExtensionContext` 42 | logOutputChannel: logOutputChannel, // OutputChannel for the logger 43 | sourceLocationTracking: false, 44 | logConsol: false // define if messages should be logged to the consol 45 | }); 46 | 47 | extLogger.warn("Hello World"); 48 | // Will Log The following entry to **both** 49 | // - To outputChannel `logOutputChannel` 50 | // - To log files in `logPath` 51 | // { 52 | // "label": "MyExtName", 53 | // "level": "warn", 54 | // "message": "Hello World", 55 | // "time": "2020-01-10 15:29:52.038Z" 56 | // } 57 | } 58 | ``` 59 | 60 | ### On ChildLoggers 61 | 62 | The `getChildLogger` API is available on the interface returned by `getExtensionLogger`. 63 | This can be used to obtain sub logger which log to the same targets (outChannel/files) as 64 | the root logger, but with a more specific label. 65 | 66 | ```javascript 67 | const { getExtensionLogger } = require("@vscode-logging/logger"); 68 | 69 | const extLogger = getExtensionLogger({ extName: "MyExtName" /* ... */ }); 70 | 71 | const childLogger = extLogger.getChildLogger({ label: "MyClass" }); 72 | childLogger.warn("Hello World"); 73 | // Will Log to the same targets as extLogger but with a suffix added to the `label` 74 | // { 75 | // "label": "MyExtName.MyClass", // Note the `.MyClass` suffix in the label 76 | // "level": "warn", 77 | // "message": "Hello World", 78 | // "time": "2020-01-10 15:29:52.038Z" 79 | // } 80 | 81 | const grandChildLogger = childLogger.getChildLogger({ label: "MyMethod" }); 82 | grandChildLogger.warn("Hip Hip Hurray"); 83 | // { 84 | // "label": "MyExtName.MyClass.MyMethod", // Note the `.MyMethod` suffix in the label 85 | // "level": "warn", 86 | // "message": "Hello World", 87 | // "time": "2020-01-10 15:30:52.038Z" 88 | // } 89 | ``` 90 | 91 | Important notes on child loggers: 92 | 93 | - They share the same configuration (e.g logging level) as the root extension logger. 94 | - Child Loggers cannot change their configuration, all configuration mutation must be done 95 | via the root extension logger. 96 | - Child Loggers can be created from other child loggers, the `label` property would simply 97 | expend with the additional suffixes. 98 | - Child Loggers are **cached** using their label as the key, so repeatedly calling `getChildLogger` 99 | using the same `label` on the same Logger object would return the **same** childLogger object. 100 | 101 | ### On `sourceLocationTracking` 102 | 103 | When enabled the `sourceLocationTracking` will augment the log entries with the function 104 | name and location (file/line/column) where the log method was invoked, e.g: 105 | 106 | ```json 107 | { 108 | "label": "osem", 109 | "level": "error", 110 | "message": "Hip Hip Hurray, the Command was executed! counter: <1>", 111 | "source": { 112 | "function": "registerCalback", 113 | "location": "c:\\workspace\\vscode-logging\\examples\\extension\\lib\\commands.js:21:19" 114 | }, 115 | "time": "2020-01-11 11:51:48.659Z" 116 | } 117 | ``` 118 | 119 | Important things to note on `sourceLocationTracking` 120 | 121 | - This functionality is not guaranteed to always work. 122 | - e.g anonymous functions have no name... 123 | 124 | - Obtaining the source location information is **very slow**. 125 | 126 | - This means that this feature should **not be used** in productive flows. 127 | - Therefore A "Fatal" log entry will be logged each time this option is enabled. 128 | 129 | - Processes which manipulate the source code such as [bundling][vscode-bundling] or compilation (TypeScript/Babel) 130 | may make the information produced less relevant due to the lack of sourceMaps support. 131 | Therefore this feature may be more useful during development flows rather then productive flows. 132 | 133 | [vscode-bundling]: https://code.visualstudio.com/api/working-with-extensions/bundling-extension 134 | 135 | ## Support 136 | 137 | Please open [issues](https://github.com/SAP/vscode-logging/issues) on github. 138 | 139 | ## Contributing 140 | 141 | See [CONTRIBUTING.md](./CONTRIBUTING.md). 142 | 143 | ## License 144 | 145 | Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. 146 | This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the [LICENSE file](../../LICENSE). 147 | -------------------------------------------------------------------------------- /packages/logger/api.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get A Root VSCode Extension Logger capable of Logging to: 3 | * - Rolling Log File (optional) 4 | * - VSCode OutputChannel 5 | */ 6 | import { IVSCodeExtLogger, LogLevel } from "@vscode-logging/types"; 7 | export { 8 | IVSCodeExtLogger, 9 | IChildLogger, 10 | LogLevel 11 | } from "@vscode-logging/types"; 12 | 13 | /** 14 | * This is a sub-type of VSCode.OutputChannel. 15 | * We define it to avoid direct dependency to @vscode/types. 16 | * In some edge cases having multiple versions of @vscode/types can cause compilation errors. 17 | */ 18 | export type BasicOutputChannel = { 19 | appendLine(value: string): void; 20 | show(): void; 21 | dispose(): void; 22 | }; 23 | 24 | export function getExtensionLogger( 25 | opts: getExtensionLoggerOpts 26 | ): IVSCodeExtLogger; 27 | 28 | export type getExtensionLoggerOpts = { 29 | /** 30 | * This parameter will be used for two things: 31 | * 32 | * 33 | * - Root Label used when creating log entries. 34 | * This will also be used as the prefix label for any childLogger e.g: 35 | * - "Root.child" 36 | * - "Root.child.grandChild" 37 | */ 38 | extName: string; 39 | /** 40 | * The Initial Log Level to use. 41 | * This should normally provided by a configuration setting exposed to the Extension's end-users. 42 | * - See: https://github.com/SAP/vscode-logging/tree/master/examples/extension for a runnable example 43 | * exposing logging configuration to the end user. 44 | */ 45 | level: LogLevel; 46 | /** 47 | * Optional directory where the rolling log files should be kept. 48 | * If this is not passed no rolling File Logs will be used. 49 | * 50 | * If the directory does not exist it would be created. 51 | * 52 | * It is normally expected to send the `ExtensionContext.logPath` in the parameter, 53 | * - https://code.visualstudio.com/api/references/vscode-api#ExtensionContext.logPath 54 | * which means `getExtensionLogger` should be called from the Extension's main `activate` function. 55 | * - https://code.visualstudio.com/api/references/activation-events#Start-up 56 | */ 57 | logPath?: string; 58 | /** 59 | * Flag for adding sourceLocation information to each logEntry. 60 | * This is disabled by default and should only be enabled during debugging flows as it 61 | * Will likely cause significant performance regressions in productive flows. 62 | */ 63 | sourceLocationTracking?: boolean; 64 | /** 65 | * Optional Output channel where the logs should be shown. 66 | * If this is not passed no Output channel will be used. 67 | */ 68 | logOutputChannel?: BasicOutputChannel; 69 | /** 70 | * Optional Console output channel, if set to true the log message will be printed to the Console output stream (stdout/stderr in Linux) 71 | * Error and Fatal messages are printed to the standard error console 72 | * Warn, Info, Debug and Trace are printed to the standard output console 73 | * Default = false 74 | */ 75 | logConsole?: boolean; 76 | }; 77 | -------------------------------------------------------------------------------- /packages/logger/lib/api.js: -------------------------------------------------------------------------------- 1 | const { has } = require("lodash"); 2 | const { createLogger } = require("winston"); 3 | /** 4 | * Normally we would prefer to depend upon `vscode` and `streamroller` inside their respective transports. 5 | * However, by depending on those here in `api.js` we are able to more easily mock/stub these 6 | * using proxyQuire. 7 | * 8 | * This is the lesser evil as the other options of: 9 | * - Using `@global` with proxyQuire. 10 | * - Running fuller (much more copmlex) integration tests with a real VSCode and real files. 11 | * 12 | * Seem much worse. 13 | */ 14 | const { RollingFileStream } = require("streamroller"); 15 | 16 | const { buildLoggerFormat } = require("./format"); 17 | const { VSCodeExtLogger } = require("./logger"); 18 | const { 19 | VscodeOutChannelTransport 20 | } = require("./transports/vscode-out-channel"); 21 | const { RollingFileTransport } = require("./transports/rolling-file"); 22 | const { levelsConfig, isValidLogLevel, levels } = require("./levels"); 23 | const { ConsoleTransport } = require("./transports/console-output"); 24 | 25 | /** 26 | * @param {import("../api").getExtensionLoggerOpts} opts 27 | * @return {import("@vscode-logging/types").IVSCodeExtLogger} 28 | */ 29 | function getExtensionLogger(opts) { 30 | if (isValidLogLevel(opts.level) === false) { 31 | throw Error(`Attempt to use unknown logging level: <${opts.level}>!`); 32 | } 33 | 34 | if ( 35 | !has(opts, "logOutputChannel") && 36 | !has(opts, "logPath") && 37 | !has(opts, "logConsole") 38 | ) { 39 | throw Error( 40 | "Logger must have at least one logging target defined, it should includes one (or more) of these options: logOutputChannel, logPath and logConsole" 41 | ); 42 | } 43 | 44 | /** 45 | * @type {any[]} 46 | */ 47 | const transports = []; 48 | 49 | if (opts.logConsole) { 50 | transports.push(new ConsoleTransport()); 51 | } 52 | 53 | if (opts.logOutputChannel) { 54 | transports.push( 55 | new VscodeOutChannelTransport({ 56 | outChannel: opts.logOutputChannel 57 | }) 58 | ); 59 | } 60 | 61 | if (opts.logPath) { 62 | transports.push( 63 | new RollingFileTransport({ 64 | logPath: opts.logPath, 65 | extName: opts.extName, 66 | RollingFileStream: RollingFileStream 67 | }) 68 | ); 69 | } 70 | 71 | const format = buildLoggerFormat(opts.extName); 72 | const rootWinstonLogger = createLogger({ 73 | levels: levelsConfig, 74 | // We are handling the log levels ourselves, to avoid calling winston's re-configure method 75 | // as it would require us to re-create the winston transports and may create race conditions 76 | // - Winston does not have a minimal API to change **only** the level... 77 | level: levels.trace, 78 | format: format, 79 | transports: transports 80 | }); 81 | 82 | return new VSCodeExtLogger({ 83 | label: opts.extName, 84 | level: opts.level, 85 | loggerImpel: rootWinstonLogger, 86 | outChannel: opts.logOutputChannel, 87 | sourceLocationTracking: opts.sourceLocationTracking, 88 | consoleOutput: opts.logConsole 89 | }); 90 | } 91 | 92 | /** 93 | * Ensure actual runtime API matches the declared public API 94 | * @type {typeof import("../api")} 95 | */ 96 | const publicApi = { 97 | getExtensionLogger: getExtensionLogger 98 | }; 99 | 100 | module.exports = publicApi; 101 | -------------------------------------------------------------------------------- /packages/logger/lib/format.js: -------------------------------------------------------------------------------- 1 | const { format } = require("winston"); 2 | const { MESSAGE } = require("triple-beam"); 3 | // @ts-ignore 4 | const { stableStringify } = require("fast-safe-stringify"); 5 | 6 | const utcTimestampProp = format(info => { 7 | // The timestamp format is according to ISO-8601 and the format is: YYYY-MM-DDTHH:mm:ss.sssZ 8 | info.time = new Date().toISOString(); 9 | 10 | return info; 11 | }); 12 | 13 | // TODO: stable json should be provided directly by winston / logForm (future versions). 14 | const JSONStable = format(info => { 15 | info[MESSAGE] = stableStringify(info, null, 2); 16 | return info; 17 | }); 18 | 19 | function buildLoggerFormat(label) { 20 | const resultFormat = format.combine( 21 | format.splat(), 22 | utcTimestampProp(), 23 | JSONStable() 24 | ); 25 | return resultFormat; 26 | } 27 | 28 | module.exports = { 29 | buildLoggerFormat: buildLoggerFormat 30 | }; 31 | -------------------------------------------------------------------------------- /packages/logger/lib/levels.js: -------------------------------------------------------------------------------- 1 | const { partial, mapValues, includes, keys } = require("lodash"); 2 | 3 | const levelsConfig = { 4 | off: -1, 5 | fatal: 0, 6 | error: 1, 7 | warn: 2, 8 | info: 3, 9 | debug: 4, 10 | trace: 5 11 | }; 12 | 13 | const logLevelsKeys = mapValues(levelsConfig, (val, key) => key); 14 | 15 | const isValidLogLevel = partial(includes, keys(levelsConfig)); 16 | 17 | module.exports = { 18 | levelsConfig: levelsConfig, 19 | levels: logLevelsKeys, 20 | isValidLogLevel: isValidLogLevel 21 | }; 22 | -------------------------------------------------------------------------------- /packages/logger/lib/logger.js: -------------------------------------------------------------------------------- 1 | const { forEach, findKey } = require("lodash"); 2 | const stacktrace = require("stacktrace-js"); 3 | const { levelsConfig, isValidLogLevel } = require("./levels"); 4 | const ADD_SOURCE_LOCATION_INFO = Symbol("addSourceLocationInfo"); 5 | const CHANGE_LEVEL = Symbol("changeLevel"); 6 | const CHANGE_SOURCE_LOCATION_TRACKING = Symbol("changeSourceLocationTracking"); 7 | 8 | const LABEL = Symbol("label"); 9 | const LOGGER_IMPEL = Symbol("loggerImpel"); 10 | const LEVEL_INT = Symbol("levelInt"); 11 | const SOURCE_LOCATION_TRACKING = Symbol("sourceLocationTracking"); 12 | const CONSOLE_OUTPUT = Symbol("consoleOutput"); 13 | const CHILD_LOGGERS = Symbol("childLoggers"); 14 | const OUT_CHANNEL = Symbol("outChannel"); 15 | const WARN_IF_LOCATION_TRACKING_IS_ENABLED = Symbol( 16 | "warnIfLocationTrackingIsEnabled" 17 | ); 18 | 19 | class BaseLogger { 20 | /** 21 | * @param {object} opts 22 | * @param {string} opts.label 23 | * @param {string} opts.level 24 | * @param {boolean} [opts.sourceLocationTracking] 25 | * @param {boolean} [opts.consoleOutput] 26 | * @param {import("winston").Logger} opts.loggerImpel 27 | */ 28 | constructor(opts) { 29 | this[LABEL] = opts.label; 30 | this[LOGGER_IMPEL] = opts.loggerImpel; 31 | this[LEVEL_INT] = levelsConfig[opts.level]; 32 | // disabled by default as this may cause performance regressions 33 | this[SOURCE_LOCATION_TRACKING] = opts.sourceLocationTracking || false; 34 | this[CONSOLE_OUTPUT] = opts.consoleOutput || false; 35 | // Possible memory leak if users forget to release their childLogger Resources 36 | // Could have been resolved using WeakMap, however Weak Collections in JavaScript are not iterable (yet) 37 | // So they are insufficient for our needs (see `getChildLogger` method). 38 | // - https://github.com/tc39/proposal-weakrefs#iterable-weakmaps 39 | this[CHILD_LOGGERS] = new Map([]); 40 | } 41 | 42 | getChildLogger(opts) { 43 | const newLabel = this[LABEL] + "." + opts.label; 44 | 45 | if (this[CHILD_LOGGERS].has(newLabel)) { 46 | return this[CHILD_LOGGERS].get(newLabel); 47 | } 48 | 49 | const newChildLoggerImpel = new BaseLogger({ 50 | label: newLabel, 51 | sourceLocationTracking: this[SOURCE_LOCATION_TRACKING], 52 | consoleOutput: this[CONSOLE_OUTPUT], 53 | level: findKey(levelsConfig, val => val === this[LEVEL_INT]), 54 | loggerImpel: this[LOGGER_IMPEL] 55 | }); 56 | 57 | this[CHILD_LOGGERS].set(newLabel, newChildLoggerImpel); 58 | 59 | return newChildLoggerImpel; 60 | } 61 | 62 | fatal(msg, ...args) { 63 | if (this[LEVEL_INT] >= levelsConfig.fatal) { 64 | this[ADD_SOURCE_LOCATION_INFO](args); 65 | args.push({ label: this[LABEL] }); 66 | // @ts-ignore 67 | this[LOGGER_IMPEL].fatal(msg, ...args); 68 | } 69 | } 70 | 71 | error(msg, ...args) { 72 | if (this[LEVEL_INT] >= levelsConfig.error) { 73 | this[ADD_SOURCE_LOCATION_INFO](args); 74 | args.push({ label: this[LABEL] }); 75 | this[LOGGER_IMPEL].error(msg, ...args); 76 | } 77 | } 78 | 79 | warn(msg, ...args) { 80 | if (this[LEVEL_INT] >= levelsConfig.warn) { 81 | this[ADD_SOURCE_LOCATION_INFO](args); 82 | args.push({ label: this[LABEL] }); 83 | this[LOGGER_IMPEL].warn(msg, ...args); 84 | } 85 | } 86 | 87 | info(msg, ...args) { 88 | if (this[LEVEL_INT] >= levelsConfig.info) { 89 | this[ADD_SOURCE_LOCATION_INFO](args); 90 | args.push({ label: this[LABEL] }); 91 | this[LOGGER_IMPEL].info(msg, ...args); 92 | } 93 | } 94 | 95 | debug(msg, ...args) { 96 | if (this[LEVEL_INT] >= levelsConfig.debug) { 97 | this[ADD_SOURCE_LOCATION_INFO](args); 98 | args.push({ label: this[LABEL] }); 99 | this[LOGGER_IMPEL].debug(msg, ...args); 100 | } 101 | } 102 | 103 | trace(msg, ...args) { 104 | if (this[LEVEL_INT] >= levelsConfig.trace) { 105 | this[ADD_SOURCE_LOCATION_INFO](args); 106 | args.push({ label: this[LABEL] }); 107 | // @ts-ignore 108 | this[LOGGER_IMPEL].trace(msg, ...args); 109 | } 110 | } 111 | } 112 | 113 | // private methods using Symbols to hide it, need to be added directly on the prototype 114 | BaseLogger.prototype[CHANGE_LEVEL] = function(newLevel) { 115 | this[LEVEL_INT] = levelsConfig[newLevel]; 116 | // @ts-ignore 117 | forEach([...this[CHILD_LOGGERS].values()], childLogger => { 118 | // Recursive Call 119 | childLogger[CHANGE_LEVEL](newLevel); 120 | }); 121 | }; 122 | 123 | // private methods using Symbols to hide it, need to be added directly on the prototype 124 | BaseLogger.prototype[CHANGE_SOURCE_LOCATION_TRACKING] = function( 125 | isSourceLocTrack 126 | ) { 127 | this[SOURCE_LOCATION_TRACKING] = isSourceLocTrack; 128 | // @ts-ignore 129 | forEach([...this[CHILD_LOGGERS].values()], childLogger => { 130 | // Recursive Call 131 | childLogger[CHANGE_SOURCE_LOCATION_TRACKING](isSourceLocTrack); 132 | }); 133 | }; 134 | 135 | BaseLogger.prototype[ADD_SOURCE_LOCATION_INFO] = function(args) { 136 | if (this[SOURCE_LOCATION_TRACKING] === true) { 137 | const stack = stacktrace.getSync(); 138 | // we need to go 2 levels up the stack to get to the end user's code 139 | const userFrame = stack[2]; 140 | const sourceLocMsg = `${userFrame.fileName}:${userFrame.lineNumber}:${userFrame.columnNumber}`; 141 | // Balance between reducing number of properties and readability 142 | args.push({ 143 | source: { 144 | function: userFrame.functionName, 145 | location: sourceLocMsg 146 | } 147 | }); 148 | } 149 | }; 150 | 151 | class VSCodeExtLogger extends BaseLogger { 152 | /** 153 | * @param {object} opts 154 | * @param {string} opts.label 155 | * @param {string} opts.level 156 | * @param {import("winston").Logger} opts.loggerImpel 157 | * @param {import("../api").BasicOutputChannel} [opts.outChannel] 158 | * @param {boolean} [opts.sourceLocationTracking] 159 | * @param {boolean} [opts.consoleOutput] 160 | */ 161 | constructor(opts) { 162 | super(opts); 163 | 164 | this[OUT_CHANNEL] = opts.outChannel || undefined; 165 | this.changeLevel(opts.level); 166 | this[WARN_IF_LOCATION_TRACKING_IS_ENABLED](); 167 | } 168 | 169 | changeLevel(newLevel) { 170 | if (isValidLogLevel(newLevel) === false) { 171 | this.fatal( 172 | `Attempt to use unknown logging level: <${newLevel}> has been ignored.` 173 | ); 174 | } else { 175 | // A strange kind of `super.changeLevel` call 176 | this[CHANGE_LEVEL](newLevel); 177 | } 178 | } 179 | 180 | changeSourceLocationTracking(newSourceLocation) { 181 | this[CHANGE_SOURCE_LOCATION_TRACKING](newSourceLocation); 182 | this[WARN_IF_LOCATION_TRACKING_IS_ENABLED](); 183 | } 184 | } 185 | 186 | // private method using Symbols to hide it, need to be added directly on the prototype 187 | VSCodeExtLogger.prototype[WARN_IF_LOCATION_TRACKING_IS_ENABLED] = function() { 188 | if (this[SOURCE_LOCATION_TRACKING] === true) { 189 | this.fatal( 190 | "SourceLocationTracking is Enabled, This must only be used during debugging flows as it causes performance regressions" 191 | ); 192 | 193 | if (this[OUT_CHANNEL]) { 194 | this[OUT_CHANNEL].show(); 195 | } 196 | } 197 | }; 198 | 199 | module.exports = { 200 | VSCodeExtLogger: VSCodeExtLogger 201 | }; 202 | -------------------------------------------------------------------------------- /packages/logger/lib/transports/console-output.js: -------------------------------------------------------------------------------- 1 | const Transport = require("winston-transport"); 2 | const { LEVEL, MESSAGE } = require("triple-beam"); 3 | 4 | class ConsoleTransport extends Transport { 5 | constructor() { 6 | super(); 7 | } 8 | 9 | /** 10 | * Core logging method exposed to Winston. 11 | * @param {Object} info 12 | * @param {Function} callback 13 | */ 14 | log(info, callback) { 15 | setImmediate(() => this.emit("logged", info)); 16 | 17 | switch (info[LEVEL]) { 18 | case "error": 19 | case "fatal": 20 | console.error(info[MESSAGE]); 21 | break; 22 | case "warn": 23 | console.warn(info[MESSAGE]); 24 | break; 25 | default: 26 | console.log(info[MESSAGE]); 27 | } 28 | /* istanbul ignore else - winston transports use guard conditions around the callbacks 29 | * however I have no idea how to simulate the callback not existing... (winston internals...) 30 | **/ 31 | if (callback) { 32 | callback(); // eslint-disable-line callback-return 33 | } 34 | } 35 | } 36 | 37 | module.exports = { 38 | ConsoleTransport: ConsoleTransport 39 | }; 40 | -------------------------------------------------------------------------------- /packages/logger/lib/transports/rolling-file.js: -------------------------------------------------------------------------------- 1 | const { EOL } = require("os"); 2 | const Transport = require("winston-transport"); 3 | const { MESSAGE } = require("triple-beam"); 4 | const { ensureDirSync } = require("fs-extra"); 5 | const { resolve } = require("path"); 6 | const { defaults } = require("lodash"); 7 | 8 | class RollingFileTransport extends Transport { 9 | /** 10 | * @param {object} opts 11 | * @param {*} opts.RollingFileStream 12 | * @param {string} opts.logPath 13 | * @param {string} opts.extName 14 | * @param {string} [opts.maxFiles] - Maximum number of files to roll before overwriting previous logs. 15 | * @param {string} [opts.maxFileSize] - In MegaByte, total max log size would be in Bytes. 16 | */ 17 | constructor(opts) { 18 | super(); 19 | 20 | const actualOpts = defaults(opts, { 21 | maxFiles: 10, 22 | maxFileSize: 1024 * 1024 23 | }); 24 | 25 | // better safe then sorry 26 | ensureDirSync(actualOpts.logPath); 27 | 28 | const logFilePath = resolve( 29 | actualOpts.logPath, 30 | `${actualOpts.extName}.log` 31 | ); 32 | 33 | this.rollingFileStream = new opts.RollingFileStream( 34 | logFilePath, 35 | actualOpts.maxFileSize, 36 | actualOpts.maxFiles 37 | ); 38 | } 39 | 40 | log(info, cb) { 41 | setImmediate(() => { 42 | this.emit("logged", info); 43 | }); 44 | 45 | this.rollingFileStream.write(info[MESSAGE] + EOL); 46 | 47 | /* istanbul ignore else - winston transports use guard conditions around the callbacks 48 | * however I have no idea how to simulate the callback not existing... (winston internals...) 49 | **/ 50 | if (cb) { 51 | cb(); 52 | } 53 | } 54 | 55 | /* istanbul ignore next - Our flows never close a transport, this only happens on exist of VSCode 56 | * Probably not worth the effort of mocking this or creating a proper integration test. 57 | **/ 58 | close(cb) { 59 | if (this.rollingFileStream === undefined) { 60 | return; 61 | } 62 | 63 | this.rollingFileStream.end(() => { 64 | if (cb) { 65 | cb(); 66 | } 67 | this.emit("flush"); 68 | this.emit("closed"); 69 | }); 70 | } 71 | } 72 | 73 | module.exports = { 74 | RollingFileTransport: RollingFileTransport 75 | }; 76 | -------------------------------------------------------------------------------- /packages/logger/lib/transports/vscode-out-channel.js: -------------------------------------------------------------------------------- 1 | const Transport = require("winston-transport"); 2 | const { MESSAGE } = require("triple-beam"); 3 | 4 | class VscodeOutChannelTransport extends Transport { 5 | /** 6 | * @param {object} opts 7 | * @param {import("../../api").BasicOutputChannel} opts.outChannel 8 | */ 9 | constructor(opts) { 10 | super(); 11 | 12 | this.outChannel = opts.outChannel; 13 | } 14 | 15 | log(info, cb) { 16 | setImmediate(() => { 17 | this.emit("logged", info); 18 | }); 19 | 20 | this.outChannel.appendLine(info[MESSAGE]); 21 | 22 | /* istanbul ignore else - winston transports use guard conditions around the callbacks 23 | * however I have no idea how to simulate the callback not existing... (winston internals...) 24 | **/ 25 | if (cb) { 26 | cb(); 27 | } 28 | } 29 | 30 | /* istanbul ignore next - Our flows never close a transport, this only happens on exist of VSCode 31 | * Probably not worth the effort of mocking this or creating a proper integration test. 32 | **/ 33 | close(cb) { 34 | if (this.outChannel === undefined) { 35 | return; 36 | } 37 | 38 | this.outChannel.dispose(); 39 | 40 | if (cb) { 41 | cb(); 42 | } 43 | } 44 | } 45 | 46 | module.exports = { 47 | VscodeOutChannelTransport: VscodeOutChannelTransport 48 | }; 49 | -------------------------------------------------------------------------------- /packages/logger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vscode-logging/logger", 3 | "version": "2.0.0", 4 | "description": "Logger Library for VSCode Extensions", 5 | "keywords": [ 6 | "vscode", 7 | "logger" 8 | ], 9 | "files": [ 10 | ".reuse", 11 | "LICENSES", 12 | "lib", 13 | "api.d.ts" 14 | ], 15 | "main": "lib/api.js", 16 | "repository": "https://github.com/sap/vscode-logging/", 17 | "license": "Apache-2.0", 18 | "typings": "./api.d.ts", 19 | "dependencies": { 20 | "@vscode-logging/types": "^2.0.0", 21 | "fast-safe-stringify": "2.1.1", 22 | "fs-extra": "11.2.0", 23 | "lodash": "4.17.21", 24 | "stacktrace-js": "2.0.2", 25 | "streamroller": "3.1.5", 26 | "triple-beam": "1.4.1", 27 | "winston": "3.11.0", 28 | "winston-transport": "4.6.0" 29 | }, 30 | "devDependencies": { 31 | "@types/fs-extra": "11.0.4", 32 | "@types/vscode": "1.51.0", 33 | "proxyquire": "2.1.3", 34 | "sinon": "17.0.1", 35 | "sinon-chai": "3.7.0" 36 | }, 37 | "scripts": { 38 | "ci": "npm-run-all type-check coverage:*", 39 | "test": "mocha \"./test/**/*spec.js\"", 40 | "coverage:run": "nyc mocha \"./test/**/*spec.js\"", 41 | "coverage:check": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100", 42 | "type-check": "tsc" 43 | }, 44 | "publishConfig": { 45 | "access": "public" 46 | }, 47 | "nyc": { 48 | "include": [ 49 | "lib/**/*.js" 50 | ], 51 | "reporter": [ 52 | "text", 53 | "lcov" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/logger/test/api-spec.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { getExtensionLogger } = require("../lib/api.js"); 3 | 4 | describe("VSCode Extension Logger", () => { 5 | /** 6 | * @type {typeof import("../api").getExtensionLogger} 7 | */ 8 | 9 | context("when receiving no OutputChannel and no logPath", () => { 10 | it("will throw on invalid/missing parameters", () => { 11 | expect(() => { 12 | getExtensionLogger({ 13 | extName: "MyExtName", 14 | level: "error" 15 | }); 16 | }).to.throw("logOutputChannel, logPath and logConsole"); 17 | }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /packages/logger/test/child-logger-spec.js: -------------------------------------------------------------------------------- 1 | const { map } = require("lodash"); 2 | const { expect } = require("chai"); 3 | const proxyquire = require("proxyquire").noCallThru(); 4 | 5 | const { VSCodeStub } = require("./stubs/vscode-stub"); 6 | const { resolve } = require("path"); 7 | const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); 8 | 9 | describe("VSCode Extension Logger", () => { 10 | context("childLogger capabilities", () => { 11 | /** 12 | * @type {typeof import("../api").getExtensionLogger} 13 | */ 14 | let getExtensionLogger; 15 | let vsCodeStub; 16 | beforeEach(() => { 17 | // VSCode outChannel is optional but we still need a stub for it 18 | // in order to test its functionality 19 | vsCodeStub = new VSCodeStub(); 20 | const mainModuleStubbed = proxyquire("../lib/api.js", { 21 | vscode: vsCodeStub 22 | }); 23 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 24 | }); 25 | 26 | it("will log to childLogger", () => { 27 | const extLogger = getExtensionLogger({ 28 | extName: "MyExtName", 29 | logOutputChannel: vsCodeStub.OutputChannel, 30 | level: "error" 31 | }); 32 | 33 | const childLogger = extLogger.getChildLogger({ label: "myLibName" }); 34 | childLogger.fatal("Oops I did it again!"); 35 | 36 | const logEntries = map(vsCodeStub.lines, JSON.parse); 37 | expect(logEntries) 38 | .excluding("time") 39 | .to.deep.eql([ 40 | { 41 | label: "MyExtName.myLibName", 42 | level: "fatal", 43 | message: "Oops I did it again!" 44 | } 45 | ]); 46 | }); 47 | 48 | it("will handle logging level at the root Logger of all childLoggers", () => { 49 | const extLogger = getExtensionLogger({ 50 | extName: "MyExtName", 51 | logOutputChannel: vsCodeStub.OutputChannel, 52 | level: "error" 53 | }); 54 | 55 | const childLogger = extLogger.getChildLogger({ label: "myLibName" }); 56 | childLogger.warn("Oops I did it again!"); 57 | // nothing logged, warn < 'error' 58 | expect(vsCodeStub.lines).to.be.empty; 59 | 60 | extLogger.changeLevel("info"); 61 | 62 | // Changes on the root affected the level of the child... 63 | childLogger.warn("Oops I did it again!"); 64 | const logEntries = map(vsCodeStub.lines, JSON.parse); 65 | expect(logEntries) 66 | .excluding("time") 67 | .to.deep.eql([ 68 | { 69 | label: "MyExtName.myLibName", 70 | level: "warn", 71 | message: "Oops I did it again!" 72 | } 73 | ]); 74 | }); 75 | 76 | it("will handle sourceLocationTracking option at the root Logger of all childLoggers", () => { 77 | const extLogger = getExtensionLogger({ 78 | extName: "MyExtName", 79 | logOutputChannel: vsCodeStub.OutputChannel, 80 | level: "info" 81 | }); 82 | 83 | const childLogger = extLogger.getChildLogger({ label: "myLibName" }); 84 | const grandChildLogger = childLogger.getChildLogger({ 85 | label: "myClassName" 86 | }); 87 | 88 | childLogger.warn("Oops I did it again!"); 89 | grandChildLogger.warn("Oops I did it again!"); 90 | const logEntriesBefore = map(vsCodeStub.lines, JSON.parse); 91 | expect(logEntriesBefore[0]).to.not.have.property("source"); 92 | expect(logEntriesBefore[1]).to.not.have.property("source"); 93 | 94 | // Change at the root Logger Source Location 95 | extLogger.changeSourceLocationTracking(true); 96 | vsCodeStub.lines = []; 97 | childLogger.warn("Oops I did it again!"); 98 | grandChildLogger.warn("Oops I did it again!"); 99 | 100 | const logEntriesAfter = map(vsCodeStub.lines, JSON.parse); 101 | expect(logEntriesAfter[0]).to.have.property("source"); 102 | expect(logEntriesAfter[1]).to.have.property("source"); 103 | }); 104 | 105 | it("will cache and re-use the same childLogger for the same label", () => { 106 | const extLogger = getExtensionLogger({ 107 | extName: "MyExtName", 108 | logOutputChannel: vsCodeStub.OutputChannel, 109 | level: "error" 110 | }); 111 | 112 | const childLogger = extLogger.getChildLogger({ label: "myLibName" }); 113 | const childLoggerSameLabel = extLogger.getChildLogger({ 114 | label: "myLibName" 115 | }); 116 | expect(childLogger).to.equal(childLoggerSameLabel); 117 | }); 118 | }); 119 | 120 | context("childLogger load", () => { 121 | /** 122 | * @type {typeof import("../api").getExtensionLogger} 123 | */ 124 | let getExtensionLogger; 125 | let vsCodeStub; 126 | beforeEach(() => { 127 | const mainModuleStubbed = proxyquire("../lib/api.js", {}); 128 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 129 | }); 130 | 131 | function createExtLoggerWithNumberOfChildLoggers(childLoggersNumbers) { 132 | const extLogger = getExtensionLogger({ 133 | extName: "MainLoggerExtension", 134 | logPath: TESTS_LOG_PATH, 135 | level: "error" 136 | }); 137 | 138 | for ( 139 | let childLoggerNumber = 1; 140 | childLoggerNumber <= childLoggersNumbers; 141 | childLoggerNumber++ 142 | ) { 143 | extLogger.getChildLogger({ 144 | label: "ChildLoggerClass" + childLoggerNumber 145 | }); 146 | } 147 | } 148 | 149 | /** 150 | * Issue #76 reproduction. 151 | * 152 | * The issue is reproducible 153 | * when running "yarn ci" in the home directory vscode-logging 154 | * with only on this it, context or describe; 155 | * or when running "yarn test" in vscode-logging/packages/logger folder. 156 | * 157 | * The following errors appeared before fixing Issue #76 after the successful test result: 158 | childLogger load 159 | √ will create 9 childLoggers and write error message to stderr 160 | (node:2280) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 unpipe listeners added to [RollingFileTransport]. Use emitter.setMaxListeners() to increase limit 161 | (node:2280) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 error listeners added to [RollingFileTransport]. Use emitter.setMaxListeners() to increase limit 162 | */ 163 | it("will create 9 childLoggers and write error message to stderr (before fixing Issue #76)", () => { 164 | return createExtLoggerWithNumberOfChildLoggers(9); 165 | }); 166 | 167 | /** 168 | * Issue #76 doesn't happen when the number of ChildLogger's is less than 9. 169 | * 170 | * No errors appear after the successful test result: 171 | childLogger load 172 | √ will create 8 childLoggers and no error message to stderr 173 | */ 174 | it("will create 8 childLoggers and no error message to stderr (before fixing Issue #76)", () => { 175 | return createExtLoggerWithNumberOfChildLoggers(8); 176 | }); 177 | }); 178 | context( 179 | "childLogger will log to childLogger log entry with correct label", 180 | () => { 181 | /** 182 | * @type {typeof import("../api").getExtensionLogger} 183 | */ 184 | let getExtensionLogger; 185 | let vsCodeStub; 186 | beforeEach(() => { 187 | // VSCode outChannel is optional but we still need a stub for it 188 | // in order to test its functionality 189 | vsCodeStub = new VSCodeStub(); 190 | const mainModuleStubbed = proxyquire("../lib/api.js", { 191 | vscode: vsCodeStub 192 | }); 193 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 194 | }); 195 | 196 | it("in fatal log message for extension class", () => { 197 | const extLogger = getExtensionLogger({ 198 | extName: "MyExtName", 199 | logOutputChannel: vsCodeStub.OutputChannel, 200 | level: "trace" 201 | }); 202 | 203 | const classLogger = extLogger.getChildLogger({ label: "myClassName" }); 204 | classLogger.fatal("Oops I did it again!"); 205 | 206 | const logEntries = map(vsCodeStub.lines, JSON.parse); 207 | expect(logEntries) 208 | .excluding("time") 209 | .to.deep.eql([ 210 | { 211 | label: "MyExtName.myClassName", 212 | level: "fatal", 213 | message: "Oops I did it again!" 214 | } 215 | ]); 216 | }); 217 | 218 | it("in error log message for extension library class", () => { 219 | const extLogger = getExtensionLogger({ 220 | extName: "MyExtName", 221 | logOutputChannel: vsCodeStub.OutputChannel, 222 | level: "trace" 223 | }); 224 | 225 | const libLogger = extLogger.getChildLogger({ label: "myLibName" }); 226 | const classLogger = libLogger.getChildLogger({ label: "myClassName" }); 227 | 228 | classLogger.error("Oops I did it again!"); 229 | 230 | const logEntries = map(vsCodeStub.lines, JSON.parse); 231 | expect(logEntries) 232 | .excluding("time") 233 | .to.deep.eql([ 234 | { 235 | label: "MyExtName.myLibName.myClassName", 236 | level: "error", 237 | message: "Oops I did it again!" 238 | } 239 | ]); 240 | }); 241 | 242 | it("in warn log message for extension internal library class", () => { 243 | const extLogger = getExtensionLogger({ 244 | extName: "MyExtName", 245 | logOutputChannel: vsCodeStub.OutputChannel, 246 | level: "trace" 247 | }); 248 | 249 | const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); 250 | const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); 251 | const classLogger = lib2Logger.getChildLogger({ label: "myClassName" }); 252 | classLogger.warn("Oops I did it again!"); 253 | 254 | const logEntries = map(vsCodeStub.lines, JSON.parse); 255 | expect(logEntries) 256 | .excluding("time") 257 | .to.deep.eql([ 258 | { 259 | label: "MyExtName.myLib1Name.myLib2Name.myClassName", 260 | level: "warn", 261 | message: "Oops I did it again!" 262 | } 263 | ]); 264 | }); 265 | 266 | it("in info log message for extension internal library class", () => { 267 | const extLogger = getExtensionLogger({ 268 | extName: "MyExtName", 269 | logOutputChannel: vsCodeStub.OutputChannel, 270 | level: "trace" 271 | }); 272 | 273 | const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); 274 | const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); 275 | const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); 276 | const classLogger = lib3Logger.getChildLogger({ label: "myClassName" }); 277 | classLogger.info("Oops I did it again!"); 278 | 279 | const logEntries = map(vsCodeStub.lines, JSON.parse); 280 | expect(logEntries) 281 | .excluding("time") 282 | .to.deep.eql([ 283 | { 284 | label: "MyExtName.myLib1Name.myLib2Name.myLib3Name.myClassName", 285 | level: "info", 286 | message: "Oops I did it again!" 287 | } 288 | ]); 289 | }); 290 | it("in debug log message for extension internal library class", () => { 291 | const extLogger = getExtensionLogger({ 292 | extName: "MyExtName", 293 | logOutputChannel: vsCodeStub.OutputChannel, 294 | level: "trace" 295 | }); 296 | 297 | const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); 298 | const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); 299 | const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); 300 | const lib4Logger = lib3Logger.getChildLogger({ label: "myLib4Name" }); 301 | const classLogger = lib4Logger.getChildLogger({ label: "myClassName" }); 302 | classLogger.debug("Oops I did it again!"); 303 | 304 | const logEntries = map(vsCodeStub.lines, JSON.parse); 305 | expect(logEntries) 306 | .excluding("time") 307 | .to.deep.eql([ 308 | { 309 | label: 310 | "MyExtName.myLib1Name.myLib2Name.myLib3Name.myLib4Name.myClassName", 311 | level: "debug", 312 | message: "Oops I did it again!" 313 | } 314 | ]); 315 | }); 316 | it("in trace log message for extension internal library class", () => { 317 | const extLogger = getExtensionLogger({ 318 | extName: "MyExtName", 319 | logOutputChannel: vsCodeStub.OutputChannel, 320 | level: "trace" 321 | }); 322 | 323 | const lib1Logger = extLogger.getChildLogger({ label: "myLib1Name" }); 324 | const lib2Logger = lib1Logger.getChildLogger({ label: "myLib2Name" }); 325 | const lib3Logger = lib2Logger.getChildLogger({ label: "myLib3Name" }); 326 | const lib4Logger = lib3Logger.getChildLogger({ label: "myLib4Name" }); 327 | const lib5Logger = lib4Logger.getChildLogger({ label: "myLib5Name" }); 328 | const classLogger = lib5Logger.getChildLogger({ label: "myClassName" }); 329 | classLogger.trace("Oops I did it again!"); 330 | 331 | const logEntries = map(vsCodeStub.lines, JSON.parse); 332 | expect(logEntries) 333 | .excluding("time") 334 | .to.deep.eql([ 335 | { 336 | label: 337 | "MyExtName.myLib1Name.myLib2Name.myLib3Name.myLib4Name.myLib5Name.myClassName", 338 | level: "trace", 339 | message: "Oops I did it again!" 340 | } 341 | ]); 342 | }); 343 | } 344 | ); 345 | }); 346 | -------------------------------------------------------------------------------- /packages/logger/test/console-output-spec.js: -------------------------------------------------------------------------------- 1 | const proxyquire = require("proxyquire").noCallThru(); 2 | const chai = require("chai"); 3 | const sinonChai = require("sinon-chai"); 4 | const sinon = require("sinon"); 5 | const { StreamRollerStub } = require("./stubs/stream-roller-stub"); 6 | const { resolve } = require("path"); 7 | 8 | chai.use(sinonChai); 9 | 10 | const expect = chai.expect; 11 | 12 | const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); 13 | 14 | describe("VSCode Extension Logger", function() { 15 | let vsCodeStub; 16 | /** 17 | * @type {typeof import("../api").getExtensionLogger} 18 | */ 19 | let getExtensionLogger; 20 | let errSpy, warnSpy, logSpy; 21 | let streamRollerStub; 22 | beforeEach(() => { 23 | streamRollerStub = new StreamRollerStub(); 24 | const mainModuleStubbed = proxyquire("../lib/api.js", { 25 | streamroller: streamRollerStub 26 | }); 27 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 28 | errSpy = sinon.spy(console, "error"); 29 | warnSpy = sinon.spy(console, "warn"); 30 | logSpy = sinon.spy(console, "log"); 31 | }); 32 | 33 | afterEach(function() { 34 | console.error.restore(); 35 | console.warn.restore(); 36 | console.log.restore(); 37 | }); 38 | 39 | describe("Console Logging - check that the output stream match the message type and log level - error/warn/info and stdout/stderr", function() { 40 | it("should log to console in info level", function() { 41 | const extLogger = getExtensionLogger({ 42 | extName: "MyExtName", 43 | level: "info", 44 | logConsole: true 45 | }); 46 | extLogger.fatal("Oy Vey!"); 47 | extLogger.error("Oh Dear..."); 48 | extLogger.info("Oh Wow..."); 49 | 50 | sinon.assert.calledTwice(console.error); 51 | sinon.assert.called(console.log); 52 | sinon.assert.notCalled(console.warn); 53 | expect(errSpy.args[0][0]).to.include("Oy Vey!"); 54 | expect(errSpy.args[1][0]).to.include("Oh Dear..."); 55 | expect(logSpy.args[0][0]).to.include("Oh Wow..."); 56 | }); 57 | 58 | it("should log to console in error level", function() { 59 | const extLogger = getExtensionLogger({ 60 | extName: "MyExtName", 61 | level: "error", 62 | logConsole: true 63 | }); 64 | extLogger.fatal("Oy Vey!"); 65 | extLogger.error("Oh Dear..."); 66 | extLogger.info("Oh Wow..."); 67 | 68 | sinon.assert.calledTwice(console.error); 69 | sinon.assert.notCalled(console.log); 70 | sinon.assert.notCalled(console.warn); 71 | expect(errSpy.args[0][0]).to.include("Oy Vey!"); 72 | expect(errSpy.args[1][0]).to.include("Oh Dear..."); 73 | }); 74 | 75 | it("should log to console in warn level", function() { 76 | const extLogger = getExtensionLogger({ 77 | extName: "MyExtName", 78 | level: "warn", 79 | logConsole: true 80 | }); 81 | extLogger.warn("Oy Might..."); 82 | extLogger.error("Oh Dear..."); 83 | extLogger.info("Oh Wow..."); 84 | 85 | sinon.assert.called(console.error); 86 | sinon.assert.called(console.warn); 87 | sinon.assert.notCalled(console.log); 88 | expect(errSpy.args[0][0]).to.include("Oh Dear..."); 89 | expect(warnSpy.args[0][0]).to.include("Oy Might..."); 90 | }); 91 | 92 | it("should not log to console when consoleOutput is false", function() { 93 | const extLogger = getExtensionLogger({ 94 | extName: "MyExtName", 95 | logPath: TESTS_LOG_PATH, 96 | level: "info" 97 | }); 98 | extLogger.fatal("Oy Vey!"); 99 | extLogger.info("Oy Wow!"); 100 | sinon.assert.notCalled(console.error); 101 | sinon.assert.notCalled(console.warn); 102 | sinon.assert.notCalled(console.log); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /packages/logger/test/levels-spec.js: -------------------------------------------------------------------------------- 1 | const { map, forEach, pickBy, take } = require("lodash"); 2 | const { expect } = require("chai"); 3 | const proxyquire = require("proxyquire").noCallThru(); 4 | 5 | const { levels, levelsConfig } = require("../lib/levels"); 6 | const { VSCodeStub } = require("./stubs/vscode-stub"); 7 | 8 | describe("VSCode Extension Logger", () => { 9 | /** 10 | * @type {typeof import("../api").getExtensionLogger} 11 | */ 12 | let getExtensionLogger; 13 | let vsCodeStub; 14 | beforeEach(() => { 15 | // VSCode outChannel is optional but we still need a stub for it 16 | // in order to test its functionality 17 | vsCodeStub = new VSCodeStub(); 18 | const mainModuleStubbed = proxyquire("../lib/api.js", { 19 | vscode: vsCodeStub 20 | }); 21 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 22 | }); 23 | 24 | context("when using an invalid level", () => { 25 | it(`will throw on initial invalid level`, () => { 26 | expect(() => { 27 | getExtensionLogger({ 28 | extName: "MyExtName", 29 | logOutputChannel: vsCodeStub.OutputChannel, 30 | level: "Emergency" // This is a sysLog severity 31 | }); 32 | }).to.throw("Attempt to use unknown logging level: !"); 33 | }); 34 | 35 | it(`will warn and ignore on subsequent invalid level`, () => { 36 | const extLogger = getExtensionLogger({ 37 | extName: "MyExtName", 38 | logOutputChannel: vsCodeStub.OutputChannel, 39 | level: "fatal" 40 | }); 41 | 42 | extLogger.changeLevel("Emergency"); 43 | const logEntries = map(vsCodeStub.lines, JSON.parse); 44 | expect(logEntries[0].message).to.eql( 45 | "Attempt to use unknown logging level: has been ignored." 46 | ); 47 | 48 | expect(vsCodeStub.lines).to.have.lengthOf(1); 49 | extLogger.error( 50 | "should not be logger as the logger should still be on fatal mode" 51 | ); 52 | expect(vsCodeStub.lines).to.have.lengthOf(1); 53 | extLogger.fatal("should be logged"); 54 | expect(vsCodeStub.lines).to.have.lengthOf(2); 55 | }); 56 | }); 57 | 58 | context("Specific logging Levels Support", () => { 59 | const allLevelsExceptOff = pickBy(levels, levelKey => levelKey !== "off"); 60 | 61 | const fullExpectedLogEntries = [ 62 | { 63 | level: "fatal", 64 | message: "fatal" 65 | }, 66 | { 67 | level: "error", 68 | message: "error" 69 | }, 70 | { 71 | level: "warn", 72 | message: "warn" 73 | }, 74 | { 75 | level: "info", 76 | message: "info" 77 | }, 78 | { 79 | level: "debug", 80 | message: "debug" 81 | }, 82 | { 83 | level: "trace", 84 | message: "trace" 85 | } 86 | ]; 87 | 88 | forEach(levels, currLevel => { 89 | it(`will only log correct levels in '${currLevel}'`, () => { 90 | const extLogger = getExtensionLogger({ 91 | extName: "MyExtName", 92 | logOutputChannel: vsCodeStub.OutputChannel, 93 | level: currLevel 94 | }); 95 | 96 | // try logging using all the possible levels 97 | forEach(allLevelsExceptOff, level => { 98 | extLogger[level](level); 99 | }); 100 | 101 | const logEntries = map(vsCodeStub.lines, JSON.parse); 102 | 103 | // The higher the log level the more entries are expected to be logged. 104 | const expectedLogEntries = take( 105 | fullExpectedLogEntries, 106 | // 0 based index necessitates '+1" 107 | levelsConfig[currLevel] + 1 108 | ); 109 | 110 | expect(logEntries) 111 | .excluding(["time", "label"]) 112 | .to.deep.eql(expectedLogEntries); 113 | }); 114 | }); 115 | }); 116 | }); 117 | -------------------------------------------------------------------------------- /packages/logger/test/log-methods-apis-spec.js: -------------------------------------------------------------------------------- 1 | const { map } = require("lodash"); 2 | const { expect } = require("chai"); 3 | const proxyquire = require("proxyquire").noCallThru(); 4 | 5 | const { VSCodeStub } = require("./stubs/vscode-stub"); 6 | 7 | describe("VSCode Extension extLogger", () => { 8 | context("Log Methods APIs and Argument Types", () => { 9 | /** 10 | * @type {typeof import("../api").getExtensionLogger}output 11 | */ 12 | let getExtensionLogger; 13 | let vsCodeStub; 14 | beforeEach(() => { 15 | // VSCode outChannel is optional but we still need a stub for it 16 | // in order to test its functionality 17 | vsCodeStub = new VSCodeStub(); 18 | const mainModuleStubbed = proxyquire("../lib/api.js", { 19 | vscode: vsCodeStub 20 | }); 21 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 22 | }); 23 | 24 | it("supports splat arguments", () => { 25 | const extLogger = getExtensionLogger({ 26 | extName: "MyExtName", 27 | logOutputChannel: vsCodeStub.OutputChannel, 28 | level: "error" 29 | }); 30 | 31 | extLogger.fatal("hello %s and 1 + %i equals 2", "world", 1); 32 | const logEntries = map(vsCodeStub.lines, JSON.parse); 33 | expect(logEntries[0].message).to.equal("hello world and 1 + 1 equals 2"); 34 | }); 35 | 36 | it("supports metadata object arguments", () => { 37 | const extLogger = getExtensionLogger({ 38 | extName: "MyExtName", 39 | logOutputChannel: vsCodeStub.OutputChannel, 40 | level: "error" 41 | }); 42 | 43 | extLogger.fatal("hello world", { a: 666, b: "oops" }, { c: 333 }); 44 | const logEntries = map(vsCodeStub.lines, JSON.parse); 45 | expect(logEntries[0].a).to.equal(666); 46 | expect(logEntries[0].b).to.equal("oops"); 47 | expect(logEntries[0].c).to.equal(333); 48 | }); 49 | 50 | it("supports combining splat and object arguments", () => { 51 | const extLogger = getExtensionLogger({ 52 | logOutputChannel: vsCodeStub.OutputChannel, 53 | level: "error" 54 | }); 55 | 56 | extLogger.fatal("hello %s", "world", { a: 666, b: "oops" }, { c: 333 }); 57 | const logEntries = map(vsCodeStub.lines, JSON.parse); 58 | expect(logEntries[0].message).to.equal("hello world"); 59 | expect(logEntries[0].a).to.equal(666); 60 | expect(logEntries[0].b).to.equal("oops"); 61 | expect(logEntries[0].c).to.equal(333); 62 | }); 63 | 64 | it("overwrites label field", () => { 65 | const extLogger = getExtensionLogger({ 66 | extName: "MyExtName", 67 | logOutputChannel: vsCodeStub.OutputChannel, 68 | level: "error" 69 | }); 70 | 71 | extLogger.fatal("hello world", { label: "kuku" }); 72 | const logEntries = map(vsCodeStub.lines, JSON.parse); 73 | expect(logEntries[0].label).to.equal("MyExtName"); 74 | }); 75 | 76 | it("overwrites label field for ChildLogger", () => { 77 | const extLogger = getExtensionLogger({ 78 | extName: "MyExtName", 79 | logOutputChannel: vsCodeStub.OutputChannel, 80 | level: "error" 81 | }); 82 | const libLogger = extLogger.getChildLogger({ label: "MyLibName" }); 83 | const classLogger = libLogger.getChildLogger({ label: "MyClassName" }); 84 | 85 | classLogger.fatal("hello world", { label: "kuku" }); 86 | const logEntries = map(vsCodeStub.lines, JSON.parse); 87 | expect(logEntries[0].label).to.equal("MyExtName.MyLibName.MyClassName"); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /packages/logger/test/out-channel-spec.js: -------------------------------------------------------------------------------- 1 | const { VSCodeStub } = require("./stubs/vscode-stub"); 2 | const { map } = require("lodash"); 3 | const { basename } = require("path"); 4 | 5 | const { expect } = require("chai"); 6 | const proxyquire = require("proxyquire").noCallThru(); 7 | 8 | describe("VSCode Extension Logger", () => { 9 | context("OutChannel Logging", () => { 10 | let vsCodeStub; 11 | /** 12 | * @type {typeof import("../api").getExtensionLogger} 13 | */ 14 | let getExtensionLogger; 15 | 16 | beforeEach(() => { 17 | vsCodeStub = new VSCodeStub(); 18 | const mainModuleStubbed = proxyquire("../lib/api.js", { 19 | vscode: vsCodeStub 20 | }); 21 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 22 | }); 23 | 24 | it("will Log in JSON Format", () => { 25 | const extLogger = getExtensionLogger({ 26 | extName: "MyExtName", 27 | logOutputChannel: vsCodeStub.OutputChannel, 28 | level: "error" 29 | }); 30 | extLogger.fatal("Oy Vey!"); 31 | extLogger.error("Oh Dear..."); 32 | 33 | const logEntries = map(vsCodeStub.lines, JSON.parse); 34 | expect(logEntries) 35 | .excluding("time") 36 | .to.deep.eql([ 37 | { 38 | label: "MyExtName", 39 | level: "fatal", 40 | message: "Oy Vey!" 41 | }, 42 | { 43 | label: "MyExtName", 44 | level: "error", 45 | message: "Oh Dear..." 46 | } 47 | ]); 48 | }); 49 | 50 | it("can add sourceLocation information", function myFuncName() { 51 | const extLogger = getExtensionLogger({ 52 | extName: "MyExtName", 53 | sourceLocationTracking: true, 54 | logOutputChannel: vsCodeStub.OutputChannel, 55 | level: "error" 56 | }); 57 | extLogger.fatal("Oh dear"); 58 | const logEntries = map(vsCodeStub.lines, JSON.parse); 59 | // [0] contains the warning about using `sourceLocationTracking` 60 | const logJsonEntry = logEntries[1]; 61 | expect(logJsonEntry.label).to.eql("MyExtName"); 62 | expect(logJsonEntry.level).to.eql("fatal"); 63 | expect(logJsonEntry.message).to.eql("Oh dear"); 64 | expect(logJsonEntry.source.function).to.eql("Context.myFuncName"); 65 | const baseFileName = basename(__filename); 66 | const fileNameAndLocRegExp = new RegExp(`.+${baseFileName}:\\d+:\\d+`); 67 | // The location includes our own source file name! :) 68 | expect(logJsonEntry.source.location).to.match(fileNameAndLocRegExp); 69 | }); 70 | 71 | context( 72 | "will **show** on the outChannel and log a warning when the sourceLocationTracking is enabled", 73 | () => { 74 | it("on initialization", () => { 75 | getExtensionLogger({ 76 | extName: "MyExtName", 77 | sourceLocationTracking: true, 78 | logOutputChannel: vsCodeStub.OutputChannel, 79 | level: "warn" 80 | }); 81 | expect(vsCodeStub.shown).to.be.true; 82 | }); 83 | 84 | it("on sourceLocationChange change", () => { 85 | const extLogger = getExtensionLogger({ 86 | extName: "MyExtName", 87 | logOutputChannel: vsCodeStub.OutputChannel, 88 | level: "error" 89 | }); 90 | expect(vsCodeStub.shown).to.be.false; 91 | extLogger.changeSourceLocationTracking(true); 92 | expect(vsCodeStub.shown).to.be.true; 93 | }); 94 | } 95 | ); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /packages/logger/test/output-channel-spec.ts: -------------------------------------------------------------------------------- 1 | import { window, OutputChannel } from "vscode"; 2 | import { BasicOutputChannel } from "../api"; 3 | 4 | // We check that vscodeOutputChannel is assignable to BasicOutputChannel. 5 | // Checked in `type-check` npm script 6 | const vscodeChannel: OutputChannel = window.createOutputChannel("test"); 7 | const basicChannel: BasicOutputChannel = vscodeChannel; 8 | -------------------------------------------------------------------------------- /packages/logger/test/rolling-file-spec.js: -------------------------------------------------------------------------------- 1 | const { map } = require("lodash"); 2 | const { expect } = require("chai"); 3 | const proxyquire = require("proxyquire").noCallThru(); 4 | const { basename, resolve } = require("path"); 5 | 6 | const { StreamRollerStub } = require("./stubs/stream-roller-stub"); 7 | 8 | const TESTS_LOG_PATH = resolve(__dirname, ".log-out"); 9 | 10 | describe("VSCode Extension Logger", () => { 11 | /** 12 | * We are not testing the behavior of the StreamRoller, that is not our responsibility... 13 | * Rather we are only inspecting what gets written to the stream. 14 | */ 15 | context("Rolling File Logging", () => { 16 | /** 17 | * @type {typeof import("../api").getExtensionLogger} 18 | */ 19 | let getExtensionLogger; 20 | let streamRollerStub; 21 | beforeEach(() => { 22 | streamRollerStub = new StreamRollerStub(); 23 | const mainModuleStubbed = proxyquire("../lib/api.js", { 24 | streamroller: streamRollerStub 25 | }); 26 | getExtensionLogger = mainModuleStubbed.getExtensionLogger; 27 | }); 28 | 29 | it("will Log in JSON Format", () => { 30 | const extLogger = getExtensionLogger({ 31 | extName: "MyExtName", 32 | logPath: TESTS_LOG_PATH, 33 | level: "error" 34 | }); 35 | extLogger.fatal("Oy Vey!"); 36 | extLogger.error("Oh Dear..."); 37 | 38 | const logEntries = map(streamRollerStub.lines, JSON.parse); 39 | expect(logEntries) 40 | .excluding("time") 41 | .to.deep.eql([ 42 | { 43 | label: "MyExtName", 44 | level: "fatal", 45 | message: "Oy Vey!" 46 | }, 47 | { 48 | label: "MyExtName", 49 | level: "error", 50 | message: "Oh Dear..." 51 | } 52 | ]); 53 | }); 54 | 55 | it("can add sourceLocation information", function myFuncName() { 56 | const extLogger = getExtensionLogger({ 57 | extName: "MyExtName", 58 | sourceLocationTracking: true, 59 | logPath: TESTS_LOG_PATH, 60 | level: "error" 61 | }); 62 | extLogger.fatal("Oy Vey!"); 63 | const logEntries = map(streamRollerStub.lines, JSON.parse); 64 | // [0] contains the warning about using `sourceLocationTracking` 65 | const logJsonEntry = logEntries[1]; 66 | expect(logJsonEntry.label).to.eql("MyExtName"); 67 | expect(logJsonEntry.level).to.eql("fatal"); 68 | expect(logJsonEntry.message).to.eql("Oy Vey!"); 69 | expect(logJsonEntry.source.function).to.eql("Context.myFuncName"); 70 | const baseFileName = basename(__filename); 71 | const fileNameAndLocRegExp = new RegExp(`.+${baseFileName}:\\d+:\\d+`); 72 | // The location includes our own source file name! :) 73 | expect(logJsonEntry.source.location).to.match(fileNameAndLocRegExp); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /packages/logger/test/stubs/stream-roller-stub.js: -------------------------------------------------------------------------------- 1 | class StreamRollerStub { 2 | constructor() { 3 | this.lines = []; 4 | const that = this; 5 | 6 | this.RollingFileStream = class RollingFileStreamStub { 7 | constructor() { 8 | that.lines = []; 9 | } 10 | 11 | write(txt) { 12 | that.lines.push(txt); 13 | } 14 | 15 | end() { 16 | // NOOP 17 | } 18 | }; 19 | } 20 | } 21 | 22 | module.exports = { 23 | StreamRollerStub: StreamRollerStub 24 | }; 25 | -------------------------------------------------------------------------------- /packages/logger/test/stubs/vscode-stub.js: -------------------------------------------------------------------------------- 1 | class VSCodeStub { 2 | constructor() { 3 | this.lines = []; 4 | this.shown = false; 5 | 6 | const that = this; 7 | this.OutputChannel = { 8 | dispose: () => {}, 9 | show: () => { 10 | that.shown = true; 11 | }, 12 | appendLine: txt => { 13 | that.lines.push(txt); 14 | } 15 | }; 16 | } 17 | } 18 | 19 | module.exports = { 20 | VSCodeStub: VSCodeStub 21 | }; 22 | -------------------------------------------------------------------------------- /packages/logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noEmit": true, 5 | "checkJs": true, 6 | "allowJs": true 7 | }, 8 | "include": ["./lib/**/*.js", "./api.d.ts", "./test/output-channel-spec.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/.npmignore: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | tsconfig.json 3 | 4 | # Dev Docs 5 | CONTRIBUTING.md -------------------------------------------------------------------------------- /packages/types/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) 11 | 12 | ### Features 13 | 14 | - aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) 15 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 16 | 17 | ## [0.1.4](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.3...@vscode-logging/types@0.1.4) (2021-04-13) 18 | 19 | **Note:** Version bump only for package @vscode-logging/types 20 | 21 | ## [0.1.3](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.2...@vscode-logging/types@0.1.3) (2020-11-12) 22 | 23 | **Note:** Version bump only for package @vscode-logging/types 24 | 25 | ## [0.1.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.1...@vscode-logging/types@0.1.2) (2020-02-19) 26 | 27 | ### Bug Fixes 28 | 29 | - add .npmignore files to remove unneeded contents in published pkgs ([998b1f8](https://github.com/sap/vscode-logging/commit/998b1f8341352af2bba9a640f425c66c2d3a8a74)) 30 | 31 | ## [0.1.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/types@0.1.0...@vscode-logging/types@0.1.1) (2020-01-20) 32 | 33 | **Note:** Version bump only for package @vscode-logging/types 34 | 35 | # 0.1.0 (2020-01-14) 36 | 37 | ### Features 38 | 39 | - aPI Fixes (dummy change to force minor version) ([0fb0fb6](https://github.com/sap/vscode-logging/commit/0fb0fb624def760bb1a1cf4a7b46b18133d85cf0)) 40 | - initial Commit ([ee780af](https://github.com/sap/vscode-logging/commit/ee780afa90dc17cfac91a28cb2921728c1cc4489)) 41 | -------------------------------------------------------------------------------- /packages/types/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | Please see the top level [Contribution Guide](../../CONTRIBUTING.md). 4 | 5 | Note that this package only contains type signatures, therefore many of the development flows 6 | are irrelevant (e.g: testing/coverage). 7 | -------------------------------------------------------------------------------- /packages/types/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging/types 2 | 3 | Types used by @vscode-logging/logger extracted to a separate package to enable 4 | in-direct dependents to type check against the correct interfaces. 5 | 6 | For example imagine a VSCode extension called `Foo` which uses an npm package called `Bar` 7 | as a dependency and `Bar` APIs can accept an optional logger implementation: 8 | 9 | ```typescript 10 | // Code in `Bar` npm package 11 | // ------------------------------------------------------- 12 | 13 | // By defining the `IChildLogger` interface` in `@vscode-logging/types` 14 | // `Bar` only depends (dev dependency) on the interface **not** the implementation. 15 | import { IChildLogger } from "@vscode-logging/types"; 16 | 17 | export function add(lhs: number, rhs: number, logger: IChildLogger): number { 18 | logger.info("Entering function with params:", { lhs: lhs, rhs: rhs }); 19 | return lhs + rhs; 20 | } 21 | ``` 22 | 23 | ## Installation 24 | 25 | With npm: 26 | 27 | - `npm install @vscode-logging/types --save-dev` 28 | 29 | With Yarn: 30 | 31 | - `yarn add @vscode-logging/types --dev` 32 | 33 | ## Usage 34 | 35 | As shown above, simply import the `IChildLogger` interface and use it to define 36 | the type of your injected logger implementation. 37 | 38 | ## Support 39 | 40 | Please open [issues](https://github.com/SAP/vscode-logging/issues) on github. 41 | 42 | ## Contributing 43 | 44 | See [CONTRIBUTING.md](./CONTRIBUTING.md). 45 | 46 | ## License 47 | 48 | Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. 49 | This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the [LICENSE file](../../LICENSE). 50 | -------------------------------------------------------------------------------- /packages/types/api.d.ts: -------------------------------------------------------------------------------- 1 | type LogLevel = "off" | "fatal" | "error" | "warn" | "info" | "debug" | "trace"; 2 | 3 | export interface IVSCodeExtLogger extends IChildLogger { 4 | /** 5 | * Modify the Logging Level. 6 | * This will affect all child loggers as well. 7 | * Note that the level "off" is used to 'disable' the Logger. 8 | */ 9 | changeLevel(newLevel: LogLevel): void; 10 | 11 | /** 12 | * Modify the Log's sourceLocation tracking behavior. 13 | */ 14 | changeSourceLocationTracking(newSourceLocation: boolean): void; 15 | } 16 | 17 | export interface IChildLogger { 18 | /** 19 | * @param msg - The Message to log, It may include format specifiers as defined in 20 | * - https://nodejs.org/api/util.html#util_util_format_format_args 21 | * @param [args] - Args can be either: 22 | * - Optional Variable Argument List of values to "replace" the format specifiers: 23 | * myLogger.fatal("Oy %s %d", "Vey", 123) // --> { message: "Oy Vey 123"} 24 | * - Metadata Objects: 25 | * myLogger.fatal("Exec Command", { p1: 666, p2: "foo" }) // --> { message: "Exec Command", p1: 666, p2: "foo"} 26 | * Or a combination of both, however note that format specifier values should appear before 27 | * any metadata objects. 28 | * 29 | */ 30 | fatal(msg: string, ...args: any[]): void; 31 | /** 32 | * @see {IChildLogger.fatal} 33 | */ 34 | error(msg: string, ...args: any[]): void; 35 | /** 36 | * @see {IChildLogger.fatal} 37 | */ 38 | warn(msg: string, ...args: any[]): void; 39 | /** 40 | * @see {IChildLogger.fatal} 41 | */ 42 | info(msg: string, ...args: any[]): void; 43 | /** 44 | * @see {IChildLogger.fatal} 45 | */ 46 | debug(msg: string, ...args: any[]): void; 47 | /** 48 | * @see {IChildLogger.fatal} 49 | */ 50 | trace(msg: string, ...args: any[]): void; 51 | 52 | /** 53 | * Will create an logger which uses the **same** log Targets (VSCode outputChannel / Rolling File(?)). 54 | * With one difference: This "child-logger" will prefix its name to the log entries label property. 55 | * This enables distinguishing between logging messages arriving from different sources, e.g: 56 | * 57 | * - Per Class Logger 58 | * - Per Module Logger 59 | * - Per Library Dependency Logger (via dependency injection) 60 | * - ... 61 | * 62 | * Note that the childLoggers are cached using the label as the key, this means that 63 | * One can safely request a (same label) childLogger **multiple times** and not worry about 64 | * memory leaks due to the creation of many objects. 65 | */ 66 | getChildLogger(opts: { label: string }): IChildLogger; 67 | } 68 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vscode-logging/types", 3 | "version": "2.0.0", 4 | "description": "Common Type Signatures for @vscode-logging/logger", 5 | "keywords": [ 6 | "vscode", 7 | "logger" 8 | ], 9 | "files": [ 10 | ".reuse", 11 | "LICENSES", 12 | "api.d.ts" 13 | ], 14 | "repository": "https://github.com/sap/vscode-logging/", 15 | "license": "Apache-2.0", 16 | "typings": "./api.d.ts", 17 | "scripts": { 18 | "ci": "npm-run-all type-check", 19 | "type-check": "tsc" 20 | }, 21 | "publishConfig": { 22 | "access": "public" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "noEmit": true, 4 | "target": "ES2015" 5 | }, 6 | "include": ["./"] 7 | } 8 | -------------------------------------------------------------------------------- /packages/wrapper/.npmignore: -------------------------------------------------------------------------------- 1 | # TypeScript 2 | tsconfig.json 3 | 4 | # NYC 5 | .nyc_output 6 | nyc.config.js 7 | coverage 8 | 9 | # tests 10 | test 11 | 12 | # Dev Docs 13 | CONTRIBUTING.md 14 | -------------------------------------------------------------------------------- /packages/wrapper/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | # 2.0.0 (2024-01-05) 7 | 8 | ### Bug Fixes 9 | 10 | - upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/sap/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) 11 | 12 | ### Features 13 | 14 | - **wrapper:** remove dep to @types/vscode ([#167](https://github.com/sap/vscode-logging/issues/167)) ([b362e5c](https://github.com/sap/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) 15 | - logger wrapper package ([#163](https://github.com/sap/vscode-logging/issues/163)) ([fc6abc5](https://github.com/sap/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) 16 | 17 | ### BREAKING CHANGES 18 | 19 | - **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties 20 | were removed from the `configureLogger` public API 21 | 22 | ## [1.0.2](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@1.0.1...@vscode-logging/wrapper@1.0.2) (2021-09-12) 23 | 24 | ### Bug Fixes 25 | 26 | - upgrade lodash version due to CVE-2021-23337 ([c514c16](https://github.com/sap/vscode-logging/commit/c514c169ae5941cea9ebd5ae0dcb4eef8fc431d8)) 27 | 28 | ## [1.0.1](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@1.0.0...@vscode-logging/wrapper@1.0.1) (2021-04-13) 29 | 30 | **Note:** Version bump only for package @vscode-logging/wrapper 31 | 32 | # [1.0.0](https://github.com/sap/vscode-logging/compare/@vscode-logging/wrapper@0.2.0...@vscode-logging/wrapper@1.0.0) (2021-02-10) 33 | 34 | ### Features 35 | 36 | - **wrapper:** remove dep to @types/vscode ([#167](https://github.com/sap/vscode-logging/issues/167)) ([b362e5c](https://github.com/sap/vscode-logging/commit/b362e5c3b11020ab09a5e705d7834fa53e8bd48e)) 37 | 38 | ### BREAKING CHANGES 39 | 40 | - **wrapper:** `getConfigurations` and `onDidChangeConfiguration` properties 41 | were removed from the `configureLogger` public API 42 | 43 | # 0.2.0 (2021-02-07) 44 | 45 | ### Features 46 | 47 | - logger wrapper package ([#163](https://github.com/sap/vscode-logging/issues/163)) ([fc6abc5](https://github.com/sap/vscode-logging/commit/fc6abc5ea43403c3039edb8589c68a0a339e5ebc)) 48 | -------------------------------------------------------------------------------- /packages/wrapper/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | Please see the top level [Contribution Guide](../../CONTRIBUTING.md). 4 | -------------------------------------------------------------------------------- /packages/wrapper/README.md: -------------------------------------------------------------------------------- 1 | # @vscode-logging/wrapper 2 | 3 | A **optional** and **opinionated** wrapper utilities for @vscode-logging/logger. 4 | 5 | ## Installation 6 | 7 | With npm: 8 | 9 | - `npm install @vscode-logging/wrapper --save` 10 | 11 | With Yarn: 12 | 13 | - `yarn add @vscode-logging/wrapper` 14 | 15 | ## Usage 16 | 17 | Please see the [TypeScript Definitions](./api.d.ts) for full API details. 18 | 19 | ## Example 20 | 21 | An example VSCode extension is available in the [examples' folder](https://github.com/SAP/vscode-logging/tree/master/examples/extension-wrapper). 22 | 23 | ## Support 24 | 25 | Please open [issues](https://github.com/SAP/vscode-logging/issues) on github. 26 | 27 | ## Contributing 28 | 29 | See [CONTRIBUTING.md](./CONTRIBUTING.md). 30 | 31 | ## License 32 | 33 | Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. 34 | This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the [LICENSE file](../../LICENSE). 35 | -------------------------------------------------------------------------------- /packages/wrapper/api.d.ts: -------------------------------------------------------------------------------- 1 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 2 | import { BasicOutputChannel } from "@vscode-logging/logger"; 3 | 4 | /** 5 | * An "empty" Logger implementation. 6 | * This value should be used to initialize an extension's logger 7 | * before the `activate` function has been called. 8 | */ 9 | export declare const NOOP_LOGGER: IVSCodeExtLogger; 10 | 11 | export interface ConfigureLoggerOpts { 12 | /** 13 | * @see {GetExtensionLoggerOpts.extName} in "@vscode-logging/logger" 14 | */ 15 | extName: string; 16 | /** 17 | * @see {GetExtensionLoggerOpts.logPath} in "@vscode-logging/logger" 18 | */ 19 | logPath: string; 20 | loggingLevelProp: string; 21 | sourceLocationProp: string; 22 | /** 23 | * @see {GetExtensionLoggerOpts.logOutputChannel} in "@vscode-logging/logger" 24 | */ 25 | logOutputChannel?: BasicOutputChannel; 26 | /** 27 | * @see {GetExtensionLoggerOpts.logConsole} in "@vscode-logging/logger" 28 | */ 29 | logConsole?: boolean; 30 | /** 31 | * The vscode extension's subscriptions 32 | * This is normally available via the `activate` function's `context` 33 | * parameter. 34 | */ 35 | subscriptions: { dispose(): any }[]; 36 | } 37 | 38 | /** 39 | * The main opinionated wrapper utility. 40 | * This will do the following: 41 | * 42 | * 1. Initialize an @vscode-logging/logger instance. 43 | * 2. Log the initial file `logPath` and `logLevel`. 44 | * 3. Register a listener for VSCode workspace configuration changes 45 | * for the `logLevel` and `sourceLocationTracking` 46 | * 47 | * - Note that this utility is slightly opinionated and has more more restrictive 48 | * than the @vscode-logging/logger APIs, e.g: 49 | * 1. `logPath` param is mandatory. 50 | * 2. default logLevel (if none is found in the workspace configuration) is `error`. 51 | * 3. ... 52 | */ 53 | export function configureLogger(opts: ConfigureLoggerOpts): IVSCodeExtLogger; 54 | -------------------------------------------------------------------------------- /packages/wrapper/nyc.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | reporter: ["text", "lcov"], 3 | "check-coverage": false, 4 | all: true, 5 | include: ["dist/src/**/*.js"], 6 | exclude: [ 7 | // The `api.ts` is the only file which imports("vscode") 8 | // We could still test it (e.g: with `proxyquire`) 9 | // However the added value of such a test is smaller than 10 | // the overhead cost of implementing such a test: 11 | // - The api.ts only 12 | // 1. Import needed params from `vscode` package. 13 | // 2. merges user arguments with `vscode` params and calls an internal API. 14 | // 15 | // - These merges are partially "tested" at design time by TSC. 16 | // - Implementing a test with proxyquire would not test real VSCode scenarios 17 | // - The existing `configureLogger` tests are fairly complex and we are better off avoiding duplicating them. 18 | // - See: `configure-logger-spec.ts`. 19 | "dist/src/api.js" 20 | ], 21 | excludeAfterRemap: false 22 | }; 23 | -------------------------------------------------------------------------------- /packages/wrapper/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@vscode-logging/wrapper", 3 | "version": "2.0.0", 4 | "description": "opinionated DI based wrapper for @vscode-logging/logger", 5 | "keywords": [ 6 | "vscode", 7 | "logger" 8 | ], 9 | "files": [ 10 | ".reuse", 11 | "LICENSES", 12 | "dist/src", 13 | "api.d.ts" 14 | ], 15 | "main": "dist/src/api.js", 16 | "repository": "https://github.com/sap/vscode-logging/", 17 | "license": "Apache-2.0", 18 | "typings": "./api.d.ts", 19 | "dependencies": { 20 | "@vscode-logging/logger": "^2.0.0", 21 | "@vscode-logging/types": "^2.0.0" 22 | }, 23 | "devDependencies": { 24 | "@types/lodash": "4.14.202", 25 | "@types/vscode": "1.52.0", 26 | "lodash": "4.17.21" 27 | }, 28 | "scripts": { 29 | "ci": "npm-run-all clean compile coverage:*", 30 | "clean": "rimraf ./dist ./coverage", 31 | "compile": "tsc", 32 | "test": "mocha \"./dist/test/**/*spec.js\"", 33 | "coverage:run": "nyc mocha \"./dist/test/**/*spec.js\"", 34 | "coverage:check": "nyc check-coverage --lines 100 --branches 100 --statements 100 --functions 100" 35 | }, 36 | "publishConfig": { 37 | "access": "public" 38 | }, 39 | "nyc": { 40 | "include": [ 41 | "dist/src/**/*.js" 42 | ], 43 | "reporter": [ 44 | "text", 45 | "lcov" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/wrapper/src/api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is the only file in the `src` folder we import("vscode") 3 | * The rest of the code is implemented with Dependency Injection to 4 | * the relevant VSCode APIs to enable easier testing. 5 | */ 6 | import { workspace } from "vscode"; 7 | import { configureLoggerInternal } from "./configure-logger"; 8 | import { BasicOutputChannel } from "@vscode-logging/logger"; 9 | export { NOOP_LOGGER } from "./noop-logger"; 10 | 11 | export function configureLogger(opts: { 12 | extName: string; 13 | logPath: string; 14 | loggingLevelProp: string; 15 | sourceLocationProp: string; 16 | logOutputChannel?: BasicOutputChannel; 17 | logConsole?: boolean; 18 | subscriptions: { dispose(): any }[]; 19 | }) { 20 | return configureLoggerInternal({ 21 | ...opts, 22 | getConfiguration: workspace.getConfiguration, 23 | onDidChangeConfiguration: workspace.onDidChangeConfiguration 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /packages/wrapper/src/configure-logger.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from "vscode"; 2 | import { getExtensionLogger } from "@vscode-logging/logger"; 3 | import { IVSCodeExtLogger } from "@vscode-logging/types"; 4 | import { ConfigureLoggerOpts } from "../api"; 5 | import { 6 | getLoggingLevelSetting, 7 | getSourceLocationTrackingSetting 8 | } from "./settings"; 9 | import { 10 | listenToLogSettingsChanges, 11 | logLoggerDetails 12 | } from "./settings-changes-handler"; 13 | 14 | export type ConfigureLoggerDIOpts = { 15 | /** 16 | * The `vscode.workspace.getConfiguration` method. 17 | * Note this is the method itself, not the returned value from executing it. 18 | */ 19 | getConfiguration: typeof workspace.getConfiguration; 20 | /** 21 | * The `vscode.workspace.onDidChangeConfiguration` method. 22 | * Note this is the method itself, not the returned value from executing it. 23 | */ 24 | onDidChangeConfiguration: typeof workspace.onDidChangeConfiguration; 25 | }; 26 | 27 | export type configureLoggerInternalOpts = ConfigureLoggerOpts & 28 | ConfigureLoggerDIOpts; 29 | 30 | export function configureLoggerInternal( 31 | opts: configureLoggerInternalOpts 32 | ): IVSCodeExtLogger { 33 | const logLevelSetting = getLoggingLevelSetting({ 34 | getConfiguration: opts.getConfiguration, 35 | loggingLevelProp: opts.loggingLevelProp 36 | }); 37 | 38 | const sourceLocationTrackingSettings = getSourceLocationTrackingSetting({ 39 | getConfiguration: opts.getConfiguration, 40 | sourceLocationProp: opts.sourceLocationProp 41 | }); 42 | 43 | const extensionLogger = getExtensionLogger({ 44 | extName: opts.extName, 45 | level: logLevelSetting, 46 | logPath: opts.logPath, 47 | sourceLocationTracking: sourceLocationTrackingSettings, 48 | logConsole: opts.logConsole, 49 | logOutputChannel: opts.logOutputChannel 50 | }); 51 | 52 | logLoggerDetails({ 53 | logger: extensionLogger, 54 | logPath: opts.logPath, 55 | logLevel: logLevelSetting 56 | }); 57 | 58 | listenToLogSettingsChanges({ 59 | subscriptions: opts.subscriptions, 60 | onDidChangeConfiguration: opts.onDidChangeConfiguration, 61 | getConfiguration: opts.getConfiguration, 62 | loggingLevelProp: opts.loggingLevelProp, 63 | sourceLocationProp: opts.sourceLocationProp, 64 | logger: extensionLogger, 65 | logPath: opts.logPath 66 | }); 67 | 68 | return extensionLogger; 69 | } 70 | -------------------------------------------------------------------------------- /packages/wrapper/src/helper-types.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IChildLogger, 3 | IVSCodeExtLogger, 4 | LogLevel 5 | } from "@vscode-logging/types"; 6 | // importing "vscode" in a **d.ts** files avoids a actual runtime dependency to vscode. 7 | import { ExtensionContext, workspace } from "vscode"; 8 | 9 | export interface GetLoggingLevelOpts { 10 | getConfiguration: typeof workspace.getConfiguration; 11 | loggingLevelProp: string; 12 | } 13 | 14 | export interface GetSourceLocationOpts { 15 | getConfiguration: typeof workspace.getConfiguration; 16 | sourceLocationProp: string; 17 | } 18 | 19 | export interface ListenToLogSettingsOpts { 20 | subscriptions: ExtensionContext["subscriptions"]; 21 | onDidChangeConfiguration: typeof workspace.onDidChangeConfiguration; 22 | getConfiguration: typeof workspace.getConfiguration; 23 | loggingLevelProp: string; 24 | sourceLocationProp: string; 25 | logger: IVSCodeExtLogger; 26 | logPath: string; 27 | } 28 | 29 | export interface LogLoggerDetailsOpts { 30 | logger: IChildLogger; 31 | logPath: string; 32 | logLevel: LogLevel; 33 | } 34 | -------------------------------------------------------------------------------- /packages/wrapper/src/noop-logger.ts: -------------------------------------------------------------------------------- 1 | import { IVSCodeExtLogger, IChildLogger } from "@vscode-logging/types"; 2 | 3 | export function noop() {} 4 | 5 | export const NOOP_LOGGER: IVSCodeExtLogger = { 6 | changeLevel: noop, 7 | changeSourceLocationTracking: noop, 8 | debug: noop, 9 | error: noop, 10 | fatal: noop, 11 | getChildLogger(opts: { label: string }): IChildLogger { 12 | return this; 13 | }, 14 | info: noop, 15 | trace: noop, 16 | warn: noop 17 | }; 18 | 19 | Object.freeze(NOOP_LOGGER); 20 | -------------------------------------------------------------------------------- /packages/wrapper/src/settings-changes-handler.ts: -------------------------------------------------------------------------------- 1 | import { LogLevel } from "@vscode-logging/types"; 2 | import { ListenToLogSettingsOpts, LogLoggerDetailsOpts } from "./helper-types"; 3 | import { 4 | getLoggingLevelSetting, 5 | getSourceLocationTrackingSetting 6 | } from "./settings"; 7 | 8 | export function logLoggerDetails(opts: LogLoggerDetailsOpts): void { 9 | opts.logger.info(`Start Logging in Log Level: <${opts.logLevel}>`); 10 | opts.logger.info(`Full Logs can be found in the <${opts.logPath}> folder.`); 11 | } 12 | 13 | export function listenToLogSettingsChanges( 14 | opts: ListenToLogSettingsOpts 15 | ): void { 16 | opts.subscriptions.push( 17 | opts.onDidChangeConfiguration(e => { 18 | if (e.affectsConfiguration(opts.loggingLevelProp)) { 19 | const logLevel: LogLevel = getLoggingLevelSetting({ 20 | loggingLevelProp: opts.loggingLevelProp, 21 | getConfiguration: opts.getConfiguration 22 | }); 23 | opts.logger.changeLevel(logLevel); 24 | logLoggerDetails({ 25 | logger: opts.logger, 26 | logPath: opts.logPath, 27 | logLevel 28 | }); 29 | } 30 | }) 31 | ); 32 | 33 | opts.subscriptions.push( 34 | opts.onDidChangeConfiguration(e => { 35 | if (e.affectsConfiguration(opts.sourceLocationProp)) { 36 | const newSourceLocationTracking: boolean = getSourceLocationTrackingSetting( 37 | { 38 | sourceLocationProp: opts.sourceLocationProp, 39 | getConfiguration: opts.getConfiguration 40 | } 41 | ); 42 | opts.logger.changeSourceLocationTracking(newSourceLocationTracking); 43 | } 44 | }) 45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /packages/wrapper/src/settings.ts: -------------------------------------------------------------------------------- 1 | import { LogLevel } from "@vscode-logging/logger"; 2 | import { GetLoggingLevelOpts, GetSourceLocationOpts } from "./helper-types"; 3 | 4 | export function getLoggingLevelSetting(opts: GetLoggingLevelOpts): LogLevel { 5 | return opts.getConfiguration().get(opts.loggingLevelProp, "error"); 6 | } 7 | 8 | export function getSourceLocationTrackingSetting( 9 | opts: GetSourceLocationOpts 10 | ): boolean { 11 | return opts.getConfiguration().get(opts.sourceLocationProp, false); 12 | } 13 | -------------------------------------------------------------------------------- /packages/wrapper/test/configure-logger-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { ExtensionContext, WorkspaceConfiguration } from "vscode"; 3 | import { IChildLogger, LogLevel } from "@vscode-logging/types"; 4 | import { BasicOutputChannel } from "@vscode-logging/logger"; 5 | import { configureLoggerInternal } from "../src/configure-logger"; 6 | import { configureLoggerInternalOpts } from "../src/configure-logger"; 7 | 8 | describe("The `configureLogger` main wrapper utility function", () => { 9 | let outputChannelMock: BasicOutputChannel; 10 | let subscriptions: ExtensionContext["subscriptions"]; 11 | let onDidChangeConfiguration: configureLoggerInternalOpts["onDidChangeConfiguration"]; 12 | let loggingLevelProp: string; 13 | let sourceLocationProp: string; 14 | let configLogLevel: LogLevel; 15 | let getConfiguration: configureLoggerInternalOpts["getConfiguration"]; 16 | let loggedLines: Record[]; 17 | 18 | beforeEach(() => { 19 | loggedLines = []; 20 | subscriptions = []; 21 | }); 22 | 23 | before(() => { 24 | outputChannelMock = { 25 | appendLine(value: string): void { 26 | loggedLines.push(JSON.parse(value)); 27 | }, 28 | dispose(): void {}, 29 | show(): void {} 30 | }; 31 | 32 | // we are not testing configuration changes in this spec, these will be tested 33 | // in `settings-changes-handler-spec` 34 | onDidChangeConfiguration = () => { 35 | return { dispose() {} }; 36 | }; 37 | 38 | loggingLevelProp = "my_vscode_ext.loggingLevel"; 39 | sourceLocationProp = "my_vscode_ext.sourceLocationTracking"; 40 | configLogLevel = "debug"; 41 | const settingsMap = new Map([ 42 | [loggingLevelProp, configLogLevel], 43 | [sourceLocationProp, false] 44 | ]); 45 | // don't worry be happy (or unknown)... 46 | getConfiguration = _ => (settingsMap as unknown) as WorkspaceConfiguration; 47 | }); 48 | 49 | function configureLoggerHelper(): IChildLogger { 50 | return configureLoggerInternal({ 51 | extName: "my_vscode_ext", 52 | // only logging "in memory" during our test. 53 | logPath: (undefined as unknown) as string, 54 | loggingLevelProp: "my_vscode_ext.loggingLevel", 55 | sourceLocationProp: "my_vscode_ext.sourceLocationTracking", 56 | logOutputChannel: outputChannelMock, 57 | subscriptions, 58 | getConfiguration, 59 | onDidChangeConfiguration 60 | }); 61 | } 62 | 63 | it("will log initial details on configure", () => { 64 | expect(loggedLines).to.be.empty; 65 | configureLoggerHelper(); 66 | expect(loggedLines).to.have.lengthOf(2); 67 | const loggerDetailsEntry = loggedLines[0]; 68 | expect(loggerDetailsEntry.message).to.include(configLogLevel); 69 | }); 70 | 71 | it("will return a VSCodeExtLogger that can be used for 'regular logging' e.g log/info/debug", () => { 72 | expect(loggedLines).to.be.empty; 73 | const testLogger = configureLoggerHelper(); 74 | loggedLines = []; 75 | testLogger.debug("hello world", { param1: 666 }); 76 | expect(loggedLines).to.have.lengthOf(1); 77 | const loggerEntry = loggedLines[0]; 78 | expect(loggerEntry.message).to.equal("hello world"); 79 | expect(loggerEntry.param1).to.equal(666); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /packages/wrapper/test/noop-logger-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { keys, difference, forEach } from "lodash"; 3 | import { noop, NOOP_LOGGER } from "../src/noop-logger"; 4 | 5 | describe("The no-operation logger", () => { 6 | const noopFuncProps = difference(keys(NOOP_LOGGER), ["getChildLogger"]); 7 | 8 | forEach(noopFuncProps, funcPropName => { 9 | it(`exposes a no operation method for <${funcPropName}>`, () => { 10 | // @ts-expect-error -- runtime reflection 11 | expect(NOOP_LOGGER[funcPropName]).to.eql(noop); 12 | }); 13 | }); 14 | 15 | it("uses an **empty** method for the noop implementation", () => { 16 | const noopSource = noop.toString(); 17 | // 18 | // The `cov_...` section in optional capturing group handles the case of execution under nyc wrapper 19 | // - instrumented code, in which case `toString` would return something like: 20 | // ``` 21 | // function noop(){cov_1w547p8w8s().f[0]++;}' 22 | // ``` 23 | expect(noopSource).to.match( 24 | /function\s*\w+\s*\(\s*\)\s*{\s*(cov_\w+\(\)\.f\[\d]\+\+;)?}/ 25 | ); 26 | }); 27 | 28 | it("will not throw when executing the NOOP function", () => { 29 | expect(noop()).to.not.throw; 30 | }); 31 | 32 | it("implements by returning 'itself'", () => { 33 | expect(NOOP_LOGGER.getChildLogger({ label: "foo" })).to.equal(NOOP_LOGGER); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /packages/wrapper/test/settings-changes-handler-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { 3 | ConfigurationChangeEvent, 4 | Disposable, 5 | ExtensionContext, 6 | WorkspaceConfiguration 7 | } from "vscode"; 8 | import { LogLevel } from "@vscode-logging/logger"; 9 | import { IChildLogger, IVSCodeExtLogger } from "@vscode-logging/types"; 10 | import { configureLoggerInternalOpts } from "../src/configure-logger"; 11 | import { listenToLogSettingsChanges } from "../src/settings-changes-handler"; 12 | 13 | describe("The `listenToLogSettingsChanges` utility function", () => { 14 | let logger: IVSCodeExtLogger; 15 | let loggingLevelProp: string; 16 | let sourceLocationProp: string; 17 | let logPath: string; 18 | let getConfiguration: configureLoggerInternalOpts["getConfiguration"]; 19 | let onDidChangeConfiguration: configureLoggerInternalOpts["onDidChangeConfiguration"]; 20 | let subscriptions: ExtensionContext["subscriptions"]; 21 | let currentLogLevel: LogLevel; 22 | let currentSourceLocationTracking: boolean; 23 | 24 | beforeEach(() => { 25 | currentLogLevel = "error"; 26 | currentSourceLocationTracking = false; 27 | }); 28 | 29 | before(() => { 30 | logPath = "c:\\logs"; 31 | 32 | logger = { 33 | changeLevel(newLevel: LogLevel) { 34 | currentLogLevel = newLevel; 35 | }, 36 | changeSourceLocationTracking(newSourceLocation: boolean) { 37 | currentSourceLocationTracking = newSourceLocation; 38 | }, 39 | getChildLogger(): IChildLogger { 40 | return this; 41 | }, 42 | fatal(): void {}, 43 | error(): void {}, 44 | warn(): void {}, 45 | info(): void {}, 46 | debug(): void {}, 47 | trace(): void {} 48 | }; 49 | 50 | loggingLevelProp = "my_vscode_ext.loggingLevel"; 51 | sourceLocationProp = "my_vscode_ext.sourceLocationTracking"; 52 | subscriptions = []; 53 | }); 54 | 55 | context("affects Configuration", () => { 56 | before(() => { 57 | const settingsMap = new Map([ 58 | [loggingLevelProp, "info"], 59 | [sourceLocationProp, true] 60 | ]); 61 | // don't worry be happy (or unknown)... 62 | getConfiguration = _ => 63 | (settingsMap as unknown) as WorkspaceConfiguration; 64 | 65 | onDidChangeConfiguration = function( 66 | cb: (e: ConfigurationChangeEvent) => void 67 | ): Disposable { 68 | const e: ConfigurationChangeEvent = { 69 | affectsConfiguration(): boolean { 70 | return true; 71 | } 72 | }; 73 | cb(e); 74 | return { dispose(): any {} }; 75 | }; 76 | }); 77 | 78 | it("will listen to `logLevel` changes", () => { 79 | expect(currentLogLevel).to.equal("error"); 80 | listenToLogSettingsChanges({ 81 | sourceLocationProp, 82 | loggingLevelProp, 83 | getConfiguration, 84 | logger, 85 | logPath, 86 | onDidChangeConfiguration, 87 | subscriptions 88 | }); 89 | expect(currentLogLevel).to.equal("info"); 90 | }); 91 | 92 | it("will listen to `sourceLocationTracking` changes", () => { 93 | expect(currentSourceLocationTracking).to.be.false; 94 | listenToLogSettingsChanges({ 95 | sourceLocationProp, 96 | loggingLevelProp, 97 | getConfiguration, 98 | logger, 99 | logPath, 100 | onDidChangeConfiguration, 101 | subscriptions 102 | }); 103 | expect(currentSourceLocationTracking).to.be.true; 104 | }); 105 | }); 106 | 107 | context("does **not** affects Configuration", () => { 108 | before(() => { 109 | const settingsMap = new Map([ 110 | ["someOtherProp", "blue"], 111 | ["someOtherProp2", 666] 112 | ]); 113 | // don't worry be happy (or unknown)... 114 | getConfiguration = _ => 115 | (settingsMap as unknown) as WorkspaceConfiguration; 116 | 117 | onDidChangeConfiguration = function( 118 | cb: (e: ConfigurationChangeEvent) => void 119 | ): Disposable { 120 | const e: ConfigurationChangeEvent = { 121 | affectsConfiguration(): boolean { 122 | return false; 123 | } 124 | }; 125 | cb(e); 126 | return { dispose(): any {} }; 127 | }; 128 | }); 129 | 130 | it("will **not** listen to unrelated configuration changes - logLevel", () => { 131 | expect(currentLogLevel).to.equal("error"); 132 | listenToLogSettingsChanges({ 133 | sourceLocationProp, 134 | loggingLevelProp, 135 | getConfiguration, 136 | logger, 137 | logPath, 138 | onDidChangeConfiguration, 139 | subscriptions 140 | }); 141 | expect(currentLogLevel).to.equal("error"); 142 | }); 143 | 144 | it("will **not** listen to unrelated configuration changes - sourceLocationTracking", () => { 145 | expect(currentSourceLocationTracking).to.be.false; 146 | listenToLogSettingsChanges({ 147 | sourceLocationProp, 148 | loggingLevelProp, 149 | getConfiguration, 150 | logger, 151 | logPath, 152 | onDidChangeConfiguration, 153 | subscriptions 154 | }); 155 | expect(currentSourceLocationTracking).to.be.false; 156 | }); 157 | }); 158 | }); 159 | -------------------------------------------------------------------------------- /packages/wrapper/test/settings-spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { 3 | getLoggingLevelSetting, 4 | getSourceLocationTrackingSetting 5 | } from "../src/settings"; 6 | import { GetLoggingLevelOpts } from "../src/helper-types"; 7 | import { WorkspaceConfiguration } from "vscode"; 8 | 9 | describe("The settings related utilities", () => { 10 | describe("The function", () => { 11 | let getConfiguration: GetLoggingLevelOpts["getConfiguration"]; 12 | 13 | before(() => { 14 | const settingsMap = new Map([ 15 | ["ext1.loggingLevel", "debug"], 16 | ["otherExt.loggingLevel", "info"] 17 | ]); 18 | getConfiguration = _ => 19 | (settingsMap as unknown) as WorkspaceConfiguration; 20 | }); 21 | 22 | it("Can retrieve the logging level value from a configuration", () => { 23 | const logLevel = getLoggingLevelSetting({ 24 | getConfiguration, 25 | loggingLevelProp: "ext1.loggingLevel" 26 | }); 27 | expect(logLevel).to.equal("debug"); 28 | }); 29 | }); 30 | 31 | describe("The function", () => { 32 | let getConfiguration: GetLoggingLevelOpts["getConfiguration"]; 33 | 34 | before(() => { 35 | const settingsMap = new Map([ 36 | ["ext1.sourceLocationTracking", true], 37 | ["otherExt.sourceLocationTracking", false] 38 | ]); 39 | getConfiguration = _ => 40 | (settingsMap as unknown) as WorkspaceConfiguration; 41 | }); 42 | 43 | it("Can retrieve the source location tracking value from a configuration", () => { 44 | const sourceLocationEnabled = getSourceLocationTrackingSetting({ 45 | getConfiguration, 46 | sourceLocationProp: "otherExt.sourceLocationTracking" 47 | }); 48 | expect(sourceLocationEnabled).to.be.false; 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/wrapper/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "baseUrl": ".", 7 | "module": "commonjs", 8 | "lib": ["es7"], 9 | "target": "es2017", 10 | "declaration": true, 11 | "sourceMap": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "strict": true 14 | }, 15 | "include": ["./src/**/*.ts", "./test/**/*.ts", "./api.d.ts"] 16 | } 17 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'examples/*' -------------------------------------------------------------------------------- /scripts/merge-coverage.js: -------------------------------------------------------------------------------- 1 | /** 2 | * based on https://github.com/istanbuljs/istanbuljs/blob/1fe490e51909607137ded25b1688581c9fd926cd/monorepo-merge-reports.js 3 | */ 4 | const { dirname, basename, join, resolve } = require("path"); 5 | const { spawnSync } = require("child_process"); 6 | 7 | const rimraf = require("rimraf"); 8 | const makeDir = require("make-dir"); 9 | const glob = require("glob"); 10 | 11 | process.chdir(resolve(__dirname, "..")); 12 | rimraf.sync(".nyc_output"); 13 | makeDir.sync(".nyc_output"); 14 | 15 | // Merge coverage data from each package so we can generate a complete reports 16 | glob.sync("packages/*/.nyc_output").forEach(nycOutput => { 17 | const cwd = dirname(nycOutput); 18 | const { status, stderr } = spawnSync( 19 | resolve("node_modules", ".bin", "nyc"), 20 | [ 21 | "merge", 22 | ".nyc_output", 23 | join(__dirname, "..", ".nyc_output", basename(cwd) + ".json") 24 | ], 25 | { 26 | encoding: "utf8", 27 | shell: true, 28 | cwd 29 | } 30 | ); 31 | 32 | if (status !== 0) { 33 | console.error(stderr); 34 | process.exit(status); 35 | } 36 | }); 37 | 38 | const { status, stderr } = spawnSync( 39 | resolve("node_modules", ".bin", "nyc"), 40 | ["report", "--reporter=lcov"], 41 | { 42 | encoding: "utf8", 43 | shell: true, 44 | cwd: resolve(__dirname, "..") 45 | } 46 | ); 47 | 48 | if (status !== 0) { 49 | console.error(stderr); 50 | process.exit(status); 51 | } 52 | --------------------------------------------------------------------------------