Skip to content

Commit 68f406b

Browse files
authoredJan 10, 2023
Merge pull request #669 from bash-lsp/logging
Logger setup
2 parents bf2b984 + deccd7f commit 68f406b

21 files changed

+393
-150
lines changed
 

‎.eslintrc.js

+1-6
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,7 @@ module.exports = {
2727
ignoreRestSiblings: true,
2828
},
2929
],
30-
'no-console': [
31-
'error',
32-
{
33-
allow: ['warn', 'error'],
34-
},
35-
],
30+
'no-console': ['error'],
3631
'prefer-destructuring': [
3732
'error',
3833
{

‎README.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Bash language server that brings an IDE-like experience for bash scripts to most
44

55
We strongly recommend that you install [shellcheck][shellcheck] to enable linting: https://github.com/koalaman/shellcheck#installing
66

7-
Documentation around configuration can be found in the [config.ts](https://github.com/bash-lsp/bash-language-server/blob/main/server/src/config.ts) file.
7+
Documentation around configuration variables can be found in the [config.ts](https://github.com/bash-lsp/bash-language-server/blob/main/server/src/config.ts) file.
88

99
## Features
1010

@@ -24,6 +24,10 @@ To be implemented:
2424

2525
## Installation
2626

27+
Usually you want to install a client for your editor (see the section below).
28+
29+
But if you want to install the server binary:
30+
2731
```bash
2832
npm i -g bash-language-server
2933
```
@@ -34,6 +38,12 @@ On Fedora based distros:
3438
dnf install -y nodejs-bash-language-server
3539
```
3640

41+
To verify that everything is working:
42+
43+
```bash
44+
bash-language-server --help
45+
```
46+
3747
If you encounter installation errors, ensure you have node version 14 or newer (`node --version`).
3848

3949
### Clients
@@ -68,7 +78,7 @@ endif
6878
For Vim 8 or Neovim using [YouCompleteMe](https://github.com/ycm-core/YouCompleteMe), add the following to `.vimrc`:
6979

7080
```vim
71-
let g:ycm_language_server =
81+
let g:ycm_language_server =
7282
\ [
7383
\ {
7484
\ 'name': 'bash',
@@ -148,6 +158,11 @@ Add the configuration to your `.emacs.d/init.el`
148158
(sh-mode . lsp))
149159
```
150160

161+
## Logging
162+
163+
The minimum logging level for the server can be adjusted using the `BASH_IDE_LOG_LEVEL` environment variable
164+
and through the general [workspace configuration](https://github.com/bash-lsp/bash-language-server/blob/main/server/src/config.ts).
165+
151166
## Development Guide
152167

153168
Please see [docs/development-guide][dev-guide] for more information.

‎server/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Bash Language Server
22

3+
## 4.3.0
4+
5+
- Add centralized and configurable logger that can be controlled using the `BASH_IDE_LOG_LEVEL` environment variable and workspace configuration. https://github.com/bash-lsp/bash-language-server/pull/669
6+
37
## 4.2.5
48

59
- Fix a critical bug causing memory leaks and high CPU usage for workspaces with many files https://github.com/bash-lsp/bash-language-server/pull/661

‎server/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"description": "A language server for Bash",
44
"author": "Mads Hartmann",
55
"license": "MIT",
6-
"version": "4.2.5",
6+
"version": "4.3.0",
77
"main": "./out/server.js",
88
"typings": "./out/server.d.ts",
99
"bin": {

‎server/src/__tests__/analyzer.test.ts

+11-33
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,30 @@ import {
66
FIXTURE_URI,
77
REPO_ROOT_FOLDER,
88
} from '../../../testing/fixtures'
9-
import { getMockConnection } from '../../../testing/mocks'
109
import Analyzer from '../analyser'
1110
import { getDefaultConfiguration } from '../config'
1211
import { initializeParser } from '../parser'
1312
import * as fsUtil from '../util/fs'
13+
import { Logger } from '../util/logger'
1414

1515
let analyzer: Analyzer
1616

1717
const CURRENT_URI = 'dummy-uri.sh'
18-
const mockConsole = getMockConnection().console
1918

2019
// if you add a .sh file to testing/fixtures, update this value
2120
const FIXTURE_FILES_MATCHING_GLOB = 15
2221

2322
const defaultConfig = getDefaultConfiguration()
2423

24+
jest.spyOn(Logger.prototype, 'log').mockImplementation(() => {
25+
// noop
26+
})
27+
const loggerInfo = jest.spyOn(Logger.prototype, 'info')
28+
const loggerWarn = jest.spyOn(Logger.prototype, 'warn')
29+
2530
beforeAll(async () => {
2631
const parser = await initializeParser()
2732
analyzer = new Analyzer({
28-
console: mockConsole,
2933
parser,
3034
workspaceFolder: FIXTURE_FOLDER,
3135
})
@@ -93,9 +97,7 @@ describe('findDeclarationLocations', () => {
9397
const { uri } = document
9498

9599
const parser = await initializeParser()
96-
const connection = getMockConnection()
97100
const newAnalyzer = new Analyzer({
98-
console: connection.console,
99101
parser,
100102
workspaceFolder: REPO_ROOT_FOLDER,
101103
})
@@ -242,10 +244,8 @@ describe('getDeclarationsForUri', () => {
242244
describe('findAllSourcedUris', () => {
243245
it('returns references to sourced files', async () => {
244246
const parser = await initializeParser()
245-
const connection = getMockConnection()
246247

247248
const newAnalyzer = new Analyzer({
248-
console: connection.console,
249249
parser,
250250
workspaceFolder: pathToFileURL(REPO_ROOT_FOLDER).href,
251251
})
@@ -266,10 +266,8 @@ describe('findAllSourcedUris', () => {
266266

267267
it('returns references to sourced files without file extension', async () => {
268268
const parser = await initializeParser()
269-
const connection = getMockConnection()
270269

271270
const newAnalyzer = new Analyzer({
272-
console: connection.console,
273271
parser,
274272
workspaceFolder: FIXTURE_FOLDER,
275273
})
@@ -344,10 +342,8 @@ describe('commandNameAtPoint', () => {
344342
describe('findDeclarationsMatchingWord', () => {
345343
it('returns a list of symbols across the workspace when includeAllWorkspaceSymbols is true', async () => {
346344
const parser = await initializeParser()
347-
const connection = getMockConnection()
348345

349346
const analyzer = new Analyzer({
350-
console: connection.console,
351347
parser,
352348
includeAllWorkspaceSymbols: true,
353349
workspaceFolder: FIXTURE_FOLDER,
@@ -456,10 +452,8 @@ describe('findDeclarationsMatchingWord', () => {
456452

457453
it('returns a list of symbols accessible to the uri when includeAllWorkspaceSymbols is false', async () => {
458454
const parser = await initializeParser()
459-
const connection = getMockConnection()
460455

461456
const analyzer = new Analyzer({
462-
console: connection.console,
463457
parser,
464458
includeAllWorkspaceSymbols: false,
465459
workspaceFolder: FIXTURE_FOLDER,
@@ -510,10 +504,8 @@ describe('findDeclarationsMatchingWord', () => {
510504

511505
it('returns symbols depending on the scope', async () => {
512506
const parser = await initializeParser()
513-
const connection = getMockConnection()
514507

515508
const analyzer = new Analyzer({
516-
console: connection.console,
517509
parser,
518510
includeAllWorkspaceSymbols: false,
519511
workspaceFolder: FIXTURE_FOLDER,
@@ -721,10 +713,7 @@ describe('initiateBackgroundAnalysis', () => {
721713

722714
jest.spyOn(Date, 'now').mockImplementation(() => 0)
723715

724-
const connection = getMockConnection()
725-
726716
const newAnalyzer = new Analyzer({
727-
console: connection.console,
728717
parser,
729718
workspaceFolder: FIXTURE_FOLDER,
730719
})
@@ -733,13 +722,12 @@ describe('initiateBackgroundAnalysis', () => {
733722
globPattern: defaultConfig.globPattern,
734723
})
735724

736-
expect(connection.window.showWarningMessage).not.toHaveBeenCalled()
737-
expect(connection.console.warn).not.toHaveBeenCalled()
725+
expect(loggerWarn).not.toHaveBeenCalled()
738726

739727
// Intro, stats on glob, one file skipped due to shebang, and outro
740728
expect(filesParsed).toEqual(FIXTURE_FILES_MATCHING_GLOB)
741729

742-
expect(connection.console.log).toHaveBeenNthCalledWith(
730+
expect(loggerInfo).toHaveBeenNthCalledWith(
743731
1,
744732
expect.stringContaining('BackgroundAnalysis: resolving glob'),
745733
)
@@ -752,10 +740,7 @@ describe('initiateBackgroundAnalysis', () => {
752740

753741
const parser = await initializeParser()
754742

755-
const connection = getMockConnection()
756-
757743
const newAnalyzer = new Analyzer({
758-
console: connection.console,
759744
parser,
760745
workspaceFolder: FIXTURE_FOLDER,
761746
})
@@ -764,8 +749,7 @@ describe('initiateBackgroundAnalysis', () => {
764749
globPattern: defaultConfig.globPattern,
765750
})
766751

767-
expect(connection.window.showWarningMessage).not.toHaveBeenCalled()
768-
expect(connection.console.warn).toHaveBeenCalledWith(expect.stringContaining('BOOM'))
752+
expect(loggerWarn).toHaveBeenCalledWith(expect.stringContaining('BOOM'))
769753
expect(filesParsed).toEqual(0)
770754
})
771755

@@ -774,10 +758,7 @@ describe('initiateBackgroundAnalysis', () => {
774758

775759
jest.spyOn(Date, 'now').mockImplementation(() => 0)
776760

777-
const connection = getMockConnection()
778-
779761
const newAnalyzer = new Analyzer({
780-
console: connection.console,
781762
parser,
782763
workspaceFolder: FIXTURE_FOLDER,
783764
})
@@ -786,8 +767,7 @@ describe('initiateBackgroundAnalysis', () => {
786767
globPattern: defaultConfig.globPattern,
787768
})
788769

789-
expect(connection.window.showWarningMessage).not.toHaveBeenCalled()
790-
expect(connection.console.warn).not.toHaveBeenCalled()
770+
expect(loggerWarn).not.toHaveBeenCalled()
791771

792772
expect(filesParsed).toEqual(0)
793773
})
@@ -799,10 +779,8 @@ describe('getAllVariables', () => {
799779
const { uri } = document
800780

801781
const parser = await initializeParser()
802-
const connection = getMockConnection()
803782

804783
const newAnalyzer = new Analyzer({
805-
console: connection.console,
806784
parser,
807785
workspaceFolder: REPO_ROOT_FOLDER,
808786
})

‎server/src/__tests__/config.test.ts

+7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ConfigSchema, getConfigFromEnvironmentVariables } from '../config'
2+
import { LOG_LEVEL_ENV_VAR } from '../util/logger'
23

34
describe('ConfigSchema', () => {
45
it('returns a default', () => {
@@ -9,6 +10,7 @@ describe('ConfigSchema', () => {
910
"globPattern": "**/*@(.sh|.inc|.bash|.command)",
1011
"highlightParsingErrors": false,
1112
"includeAllWorkspaceSymbols": false,
13+
"logLevel": "info",
1214
"shellcheckArguments": Array [],
1315
"shellcheckPath": "shellcheck",
1416
}
@@ -32,6 +34,7 @@ describe('ConfigSchema', () => {
3234
"globPattern": "**/*@(.sh)",
3335
"highlightParsingErrors": true,
3436
"includeAllWorkspaceSymbols": true,
37+
"logLevel": "info",
3538
"shellcheckArguments": Array [
3639
"-e",
3740
"SC2001",
@@ -62,6 +65,7 @@ describe('getConfigFromEnvironmentVariables', () => {
6265
"globPattern": "**/*@(.sh|.inc|.bash|.command)",
6366
"highlightParsingErrors": false,
6467
"includeAllWorkspaceSymbols": false,
68+
"logLevel": "info",
6569
"shellcheckArguments": Array [],
6670
"shellcheckPath": "shellcheck",
6771
}
@@ -80,6 +84,7 @@ describe('getConfigFromEnvironmentVariables', () => {
8084
"globPattern": "**/*@(.sh|.inc|.bash|.command)",
8185
"highlightParsingErrors": false,
8286
"includeAllWorkspaceSymbols": false,
87+
"logLevel": "info",
8388
"shellcheckArguments": Array [],
8489
"shellcheckPath": "",
8590
}
@@ -93,6 +98,7 @@ describe('getConfigFromEnvironmentVariables', () => {
9398
EXPLAINSHELL_ENDPOINT: 'localhost:8080',
9499
GLOB_PATTERN: '*.*',
95100
BACKGROUND_ANALYSIS_MAX_FILES: '1',
101+
[LOG_LEVEL_ENV_VAR]: 'error',
96102
}
97103
const { config } = getConfigFromEnvironmentVariables()
98104
expect(config).toMatchInlineSnapshot(`
@@ -102,6 +108,7 @@ describe('getConfigFromEnvironmentVariables', () => {
102108
"globPattern": "*.*",
103109
"highlightParsingErrors": false,
104110
"includeAllWorkspaceSymbols": false,
111+
"logLevel": "error",
105112
"shellcheckArguments": Array [
106113
"-e",
107114
"SC2001",

‎server/src/__tests__/server.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ import {
1414
import { getMockConnection } from '../../../testing/mocks'
1515
import LspServer from '../server'
1616
import { CompletionItemDataType } from '../types'
17+
import { Logger } from '../util/logger'
1718

1819
// Skip ShellCheck throttle delay in test cases
1920
jest.spyOn(global, 'setTimeout').mockImplementation((fn) => {
2021
fn()
2122
return 0 as any
2223
})
2324

25+
jest.spyOn(Logger.prototype, 'log').mockImplementation(() => {
26+
// noop
27+
})
28+
2429
async function initializeServer(
2530
{ rootPath }: { rootPath?: string } = { rootPath: pathToFileURL(FIXTURE_FOLDER).href },
2631
) {
@@ -480,6 +485,7 @@ describe('server', () => {
480485
)
481486

482487
if (getOptionsResult.status !== 0) {
488+
// eslint-disable-next-line no-console
483489
console.warn('Skipping onCompletion test as get-options.sh failed')
484490
return
485491
}

0 commit comments

Comments
 (0)