1
1
// Copyright (c) Microsoft Corporation.
2
2
// Licensed under the MIT License.
3
3
4
- import utils = require( "./utils" ) ;
5
4
import os = require( "os" ) ;
6
- import vscode = require( "vscode" ) ;
7
-
8
- // NOTE: This is not a string enum because the order is used for comparison.
9
- export enum LogLevel {
10
- Diagnostic ,
11
- Verbose ,
12
- Normal ,
13
- Warning ,
14
- Error ,
15
- None ,
16
- }
5
+ import { sleep , checkIfFileExists } from "./utils" ;
6
+ import { LogOutputChannel , Uri , Disposable , LogLevel , window , env , commands , workspace } from "vscode" ;
17
7
18
8
/** Interface for logging operations. New features should use this interface for the "type" of logger.
19
9
* This will allow for easy mocking of the logger during unit tests.
20
10
*/
21
11
export interface ILogger {
22
- logDirectoryPath : vscode . Uri ;
23
- updateLogLevel ( logLevelName : string ) : void ;
12
+ logDirectoryPath : Uri ;
24
13
write ( message : string , ...additionalMessages : string [ ] ) : void ;
25
14
writeAndShowInformation ( message : string , ...additionalMessages : string [ ] ) : Promise < void > ;
26
15
writeDiagnostic ( message : string , ...additionalMessages : string [ ] ) : void ;
@@ -35,37 +24,36 @@ export interface ILogger {
35
24
}
36
25
37
26
export class Logger implements ILogger {
38
- public logDirectoryPath : vscode . Uri ; // The folder for all the logs
39
- private logLevel : LogLevel ;
40
- private commands : vscode . Disposable [ ] ;
41
- private logChannel : vscode . OutputChannel ;
42
- private logFilePath : vscode . Uri ; // The client's logs
27
+ public logDirectoryPath : Uri ; // The folder for all the logs
28
+ private commands : Disposable [ ] ;
29
+ // Log output channel handles all the verbosity management so we don't have to.
30
+ private logChannel : LogOutputChannel ;
31
+ private logFilePath : Uri ; // The client's logs
43
32
private logDirectoryCreated = false ;
44
33
private writingLog = false ;
34
+ public get logLevel ( ) : LogLevel { return this . logChannel . logLevel ; }
45
35
46
- constructor ( logLevelName : string , globalStorageUri : vscode . Uri ) {
47
- this . logLevel = Logger . logLevelNameToValue ( logLevelName ) ;
48
- this . logChannel = vscode . window . createOutputChannel ( "PowerShell Extension Logs" ) ;
36
+ constructor ( globalStorageUri : Uri , logChannel ?: LogOutputChannel ) {
37
+ this . logChannel = logChannel ?? window . createOutputChannel ( "PowerShell" , { log : true } ) ;
49
38
// We have to override the scheme because it defaults to
50
39
// 'vscode-userdata' which breaks UNC paths.
51
- this . logDirectoryPath = vscode . Uri . joinPath (
40
+ this . logDirectoryPath = Uri . joinPath (
52
41
globalStorageUri . with ( { scheme : "file" } ) ,
53
42
"logs" ,
54
- `${ Math . floor ( Date . now ( ) / 1000 ) } -${ vscode . env . sessionId } ` ) ;
55
- this . logFilePath = vscode . Uri . joinPath ( this . logDirectoryPath , "vscode-powershell.log" ) ;
43
+ `${ Math . floor ( Date . now ( ) / 1000 ) } -${ env . sessionId } ` ) ;
44
+ this . logFilePath = Uri . joinPath ( this . logDirectoryPath , "vscode-powershell.log" ) ;
56
45
57
46
// Early logging of the log paths for debugging.
58
- if ( LogLevel . Diagnostic >= this . logLevel ) {
59
- const uriMessage = Logger . timestampMessage ( `Log file path: '${ this . logFilePath } '` , LogLevel . Verbose ) ;
60
- this . logChannel . appendLine ( uriMessage ) ;
47
+ if ( this . logLevel >= LogLevel . Trace ) {
48
+ this . logChannel . trace ( `Log directory: ${ this . logDirectoryPath . fsPath } ` ) ;
61
49
}
62
50
63
51
this . commands = [
64
- vscode . commands . registerCommand (
52
+ commands . registerCommand (
65
53
"PowerShell.ShowLogs" ,
66
54
( ) => { this . showLogPanel ( ) ; } ) ,
67
55
68
- vscode . commands . registerCommand (
56
+ commands . registerCommand (
69
57
"PowerShell.OpenLogFolder" ,
70
58
async ( ) => { await this . openLogFolder ( ) ; } ) ,
71
59
] ;
@@ -89,24 +77,24 @@ export class Logger implements ILogger {
89
77
}
90
78
91
79
public write ( message : string , ...additionalMessages : string [ ] ) : void {
92
- this . writeAtLevel ( LogLevel . Normal , message , ...additionalMessages ) ;
80
+ this . writeAtLevel ( LogLevel . Info , message , ...additionalMessages ) ;
93
81
}
94
82
95
83
public async writeAndShowInformation ( message : string , ...additionalMessages : string [ ] ) : Promise < void > {
96
84
this . write ( message , ...additionalMessages ) ;
97
85
98
- const selection = await vscode . window . showInformationMessage ( message , "Show Logs" , "Okay" ) ;
86
+ const selection = await window . showInformationMessage ( message , "Show Logs" , "Okay" ) ;
99
87
if ( selection === "Show Logs" ) {
100
88
this . showLogPanel ( ) ;
101
89
}
102
90
}
103
91
104
92
public writeDiagnostic ( message : string , ...additionalMessages : string [ ] ) : void {
105
- this . writeAtLevel ( LogLevel . Diagnostic , message , ...additionalMessages ) ;
93
+ this . writeAtLevel ( LogLevel . Trace , message , ...additionalMessages ) ;
106
94
}
107
95
108
96
public writeVerbose ( message : string , ...additionalMessages : string [ ] ) : void {
109
- this . writeAtLevel ( LogLevel . Verbose , message , ...additionalMessages ) ;
97
+ this . writeAtLevel ( LogLevel . Debug , message , ...additionalMessages ) ;
110
98
}
111
99
112
100
public writeWarning ( message : string , ...additionalMessages : string [ ] ) : void {
@@ -116,7 +104,7 @@ export class Logger implements ILogger {
116
104
public async writeAndShowWarning ( message : string , ...additionalMessages : string [ ] ) : Promise < void > {
117
105
this . writeWarning ( message , ...additionalMessages ) ;
118
106
119
- const selection = await vscode . window . showWarningMessage ( message , "Show Logs" ) ;
107
+ const selection = await window . showWarningMessage ( message , "Show Logs" ) ;
120
108
if ( selection !== undefined ) {
121
109
this . showLogPanel ( ) ;
122
110
}
@@ -129,7 +117,7 @@ export class Logger implements ILogger {
129
117
public async writeAndShowError ( message : string , ...additionalMessages : string [ ] ) : Promise < void > {
130
118
this . writeError ( message , ...additionalMessages ) ;
131
119
132
- const choice = await vscode . window . showErrorMessage ( message , "Show Logs" ) ;
120
+ const choice = await window . showErrorMessage ( message , "Show Logs" ) ;
133
121
if ( choice !== undefined ) {
134
122
this . showLogPanel ( ) ;
135
123
}
@@ -147,7 +135,7 @@ export class Logger implements ILogger {
147
135
148
136
const actionKeys : string [ ] = fullActions . map ( ( action ) => action . prompt ) ;
149
137
150
- const choice = await vscode . window . showErrorMessage ( message , ...actionKeys ) ;
138
+ const choice = await window . showErrorMessage ( message , ...actionKeys ) ;
151
139
if ( choice ) {
152
140
for ( const action of fullActions ) {
153
141
if ( choice === action . prompt && action . action !== undefined ) {
@@ -158,23 +146,6 @@ export class Logger implements ILogger {
158
146
}
159
147
}
160
148
161
- // TODO: Make the enum smarter about strings so this goes away.
162
- private static logLevelNameToValue ( logLevelName : string ) : LogLevel {
163
- switch ( logLevelName . trim ( ) . toLowerCase ( ) ) {
164
- case "diagnostic" : return LogLevel . Diagnostic ;
165
- case "verbose" : return LogLevel . Verbose ;
166
- case "normal" : return LogLevel . Normal ;
167
- case "warning" : return LogLevel . Warning ;
168
- case "error" : return LogLevel . Error ;
169
- case "none" : return LogLevel . None ;
170
- default : return LogLevel . Normal ;
171
- }
172
- }
173
-
174
- public updateLogLevel ( logLevelName : string ) : void {
175
- this . logLevel = Logger . logLevelNameToValue ( logLevelName ) ;
176
- }
177
-
178
149
private showLogPanel ( ) : void {
179
150
this . logChannel . show ( ) ;
180
151
}
@@ -183,7 +154,7 @@ export class Logger implements ILogger {
183
154
if ( this . logDirectoryCreated ) {
184
155
// Open the folder in VS Code since there isn't an easy way to
185
156
// open the folder in the platform's file browser
186
- await vscode . commands . executeCommand ( "vscode. openFolder" , this . logDirectoryPath , true ) ;
157
+ await commands . executeCommand ( "openFolder" , this . logDirectoryPath , true ) ;
187
158
} else {
188
159
void this . writeAndShowError ( "Cannot open PowerShell log directory as it does not exist!" ) ;
189
160
}
@@ -195,26 +166,35 @@ export class Logger implements ILogger {
195
166
}
196
167
197
168
// TODO: Should we await this function above?
198
- private async writeLine ( message : string , level : LogLevel = LogLevel . Normal ) : Promise < void > {
199
- const timestampedMessage = Logger . timestampMessage ( message , level ) ;
200
- this . logChannel . appendLine ( timestampedMessage ) ;
201
- if ( this . logLevel !== LogLevel . None ) {
169
+ private async writeLine ( message : string , level : LogLevel = LogLevel . Info ) : Promise < void > {
170
+ switch ( level ) {
171
+ case LogLevel . Trace : this . logChannel . trace ( message ) ; break ;
172
+ case LogLevel . Debug : this . logChannel . debug ( message ) ; break ;
173
+ case LogLevel . Info : this . logChannel . info ( message ) ; break ;
174
+ case LogLevel . Warning : this . logChannel . warn ( message ) ; break ;
175
+ case LogLevel . Error : this . logChannel . error ( message ) ; break ;
176
+ default : this . logChannel . appendLine ( message ) ; break ;
177
+ }
178
+
179
+ if ( this . logLevel !== LogLevel . Off ) {
202
180
// A simple lock because this function isn't re-entrant.
203
181
while ( this . writingLog ) {
204
- await utils . sleep ( 300 ) ;
182
+ await sleep ( 300 ) ;
205
183
}
206
184
try {
207
185
this . writingLog = true ;
208
186
if ( ! this . logDirectoryCreated ) {
209
187
this . writeVerbose ( `Creating log directory at: '${ this . logDirectoryPath } '` ) ;
210
- await vscode . workspace . fs . createDirectory ( this . logDirectoryPath ) ;
188
+ await workspace . fs . createDirectory ( this . logDirectoryPath ) ;
211
189
this . logDirectoryCreated = true ;
212
190
}
213
191
let log = new Uint8Array ( ) ;
214
- if ( await utils . checkIfFileExists ( this . logFilePath ) ) {
215
- log = await vscode . workspace . fs . readFile ( this . logFilePath ) ;
192
+ if ( await checkIfFileExists ( this . logFilePath ) ) {
193
+ log = await workspace . fs . readFile ( this . logFilePath ) ;
216
194
}
217
- await vscode . workspace . fs . writeFile (
195
+
196
+ const timestampedMessage = Logger . timestampMessage ( message , level ) ;
197
+ await workspace . fs . writeFile (
218
198
this . logFilePath ,
219
199
Buffer . concat ( [ log , Buffer . from ( timestampedMessage ) ] ) ) ;
220
200
} catch ( err ) {
0 commit comments