1
+ //
2
+ // Copyright (c) Microsoft. All rights reserved.
3
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+ //
5
+
6
+ using System ;
7
+ using System . IO ;
8
+ using System . Runtime . CompilerServices ;
9
+ using System . Text ;
10
+ using System . Threading ;
11
+
12
+ namespace Microsoft . PowerShell . EditorServices . Utility
13
+ {
14
+ /// <summary>
15
+ /// Provides an implementation of ILogger for writing messages to
16
+ /// a log file on disk.
17
+ /// </summary>
18
+ public class FileLogger : ILogger , IDisposable
19
+ {
20
+ private TextWriter textWriter ;
21
+ private LogLevel minimumLogLevel = LogLevel . Verbose ;
22
+
23
+ /// <summary>
24
+ /// Creates an ILogger implementation that writes to the specified file.
25
+ /// </summary>
26
+ /// <param name="logFilePath">
27
+ /// Specifies the path at which log messages will be written.
28
+ /// </param>
29
+ /// <param name="minimumLogLevel">
30
+ /// Specifies the minimum log message level to write to the log file.
31
+ /// </param>
32
+ public FileLogger ( string logFilePath , LogLevel minimumLogLevel )
33
+ {
34
+ this . minimumLogLevel = minimumLogLevel ;
35
+
36
+ // Ensure that we have a usable log file path
37
+ if ( ! Path . IsPathRooted ( logFilePath ) )
38
+ {
39
+ logFilePath =
40
+ Path . Combine (
41
+ #if CoreCLR
42
+ AppContext . BaseDirectory ,
43
+ #else
44
+ AppDomain . CurrentDomain . BaseDirectory ,
45
+ #endif
46
+ logFilePath) ;
47
+ }
48
+
49
+ if ( ! this . TryOpenLogFile ( logFilePath ) )
50
+ {
51
+ // If the log file couldn't be opened at this location,
52
+ // try opening it in a more reliable path
53
+ this . TryOpenLogFile (
54
+ Path . Combine (
55
+ #if CoreCLR
56
+ Environment . GetEnvironmentVariable ( "TEMP" ) ,
57
+ #else
58
+ Environment . GetFolderPath ( Environment . SpecialFolder . LocalApplicationData ) ,
59
+ #endif
60
+ Path. GetFileName ( logFilePath ) ) ) ;
61
+ }
62
+ }
63
+
64
+ /// <summary>
65
+ /// Writes a message to the log file.
66
+ /// </summary>
67
+ /// <param name="logLevel">The level at which the message will be written.</param>
68
+ /// <param name="logMessage">The message text to be written.</param>
69
+ /// <param name="callerName">The name of the calling method.</param>
70
+ /// <param name="callerSourceFile">The source file path where the calling method exists.</param>
71
+ /// <param name="callerLineNumber">The line number of the calling method.</param>
72
+ public void Write (
73
+ LogLevel logLevel ,
74
+ string logMessage ,
75
+ string callerName = null ,
76
+ string callerSourceFile = null ,
77
+ int callerLineNumber = 0 )
78
+ {
79
+ if ( this . textWriter != null &&
80
+ logLevel >= this . minimumLogLevel )
81
+ {
82
+ // Print the timestamp and log level
83
+ this . textWriter . WriteLine (
84
+ "{0} [{1}] - Method \" {2}\" at line {3} of {4}\r \n " ,
85
+ DateTime . Now ,
86
+ logLevel . ToString ( ) . ToUpper ( ) ,
87
+ callerName ,
88
+ callerLineNumber ,
89
+ callerSourceFile ) ;
90
+
91
+ // Print out indented message lines
92
+ foreach ( var messageLine in logMessage . Split ( '\n ' ) )
93
+ {
94
+ this . textWriter . WriteLine ( " " + messageLine . TrimEnd ( ) ) ;
95
+ }
96
+
97
+ // Finish with a newline and flush the writer
98
+ this . textWriter . WriteLine ( ) ;
99
+ this . textWriter . Flush ( ) ;
100
+ }
101
+ }
102
+
103
+ /// <summary>
104
+ /// Writes an error message and exception to the log file.
105
+ /// </summary>
106
+ /// <param name="errorMessage">The error message text to be written.</param>
107
+ /// <param name="errorException">The exception to be written..</param>
108
+ /// <param name="callerName">The name of the calling method.</param>
109
+ /// <param name="callerSourceFile">The source file path where the calling method exists.</param>
110
+ /// <param name="callerLineNumber">The line number of the calling method.</param>
111
+ public void WriteException (
112
+ string errorMessage ,
113
+ Exception errorException ,
114
+ [ CallerMemberName ] string callerName = null ,
115
+ [ CallerFilePath ] string callerSourceFile = null ,
116
+ [ CallerLineNumber ] int callerLineNumber = 0 )
117
+ {
118
+ this . Write (
119
+ LogLevel . Error ,
120
+ $ "{ errorMessage } \r \n \r \n { errorException . ToString ( ) } ",
121
+ callerName ,
122
+ callerSourceFile ,
123
+ callerLineNumber ) ;
124
+ }
125
+
126
+ /// <summary>
127
+ /// Flushes any remaining log write and closes the log file.
128
+ /// </summary>
129
+ public void Dispose ( )
130
+ {
131
+ if ( this . textWriter != null )
132
+ {
133
+ this . textWriter . Flush ( ) ;
134
+ this . textWriter . Dispose ( ) ;
135
+ this . textWriter = null ;
136
+ }
137
+ }
138
+
139
+ private bool TryOpenLogFile ( string logFilePath )
140
+ {
141
+ try
142
+ {
143
+ // Make sure the log directory exists
144
+ Directory . CreateDirectory (
145
+ Path . GetDirectoryName (
146
+ logFilePath ) ) ;
147
+
148
+ // Open the log file for writing with UTF8 encoding
149
+ this . textWriter =
150
+ new StreamWriter (
151
+ new FileStream (
152
+ logFilePath ,
153
+ FileMode . Create ) ,
154
+ Encoding . UTF8 ) ;
155
+
156
+ return true ;
157
+ }
158
+ catch ( Exception e )
159
+ {
160
+ if ( e is UnauthorizedAccessException ||
161
+ e is IOException )
162
+ {
163
+ // This exception is thrown when we can't open the file
164
+ // at the path in logFilePath. Return false to indicate
165
+ // that the log file couldn't be created.
166
+ return false ;
167
+ }
168
+
169
+ // Unexpected exception, rethrow it
170
+ throw ;
171
+ }
172
+ }
173
+ }
174
+ }
0 commit comments