22
22
using System . Globalization ;
23
23
using System . IO ;
24
24
using System . Text ;
25
+ using System . Threading . Tasks ;
25
26
26
27
namespace OpenQA . Selenium . Firefox ;
27
28
@@ -32,6 +33,11 @@ public sealed class FirefoxDriverService : DriverService
32
33
{
33
34
private const string DefaultFirefoxDriverServiceFileName = "geckodriver" ;
34
35
36
+ /// <summary>
37
+ /// Process management fields for the log writer.
38
+ /// </summary>
39
+ private StreamWriter ? logWriter ;
40
+
35
41
/// <summary>
36
42
/// Initializes a new instance of the <see cref="FirefoxDriverService"/> class.
37
43
/// </summary>
@@ -87,6 +93,16 @@ protected override DriverOptions GetDefaultDriverOptions()
87
93
/// </summary>
88
94
public bool OpenBrowserToolbox { get ; set ; }
89
95
96
+ /// <summary>
97
+ /// Gets or sets the file path where log output should be written.
98
+ /// </summary>
99
+ /// <remarks>
100
+ /// A <see langword="null"/> or <see cref="string.Empty"/> value indicates no log file to specify.
101
+ /// This approach takes the process output and redirects it to a file because GeckoDriver does not
102
+ /// offer a way to specify a log file path directly.
103
+ /// </remarks>
104
+ public string ? LogPath { get ; set ; }
105
+
90
106
/// <summary>
91
107
/// Gets or sets the level at which log output is displayed.
92
108
/// </summary>
@@ -177,6 +193,75 @@ protected override string CommandLineArguments
177
193
}
178
194
}
179
195
196
+ /// <summary>
197
+ /// Handles the event when the driver service process is starting.
198
+ /// </summary>
199
+ /// <param name="eventArgs">The event arguments containing information about the driver service process.</param>
200
+ /// <remarks>
201
+ /// This method initializes a log writer if a log path is specified and redirects output streams to capture logs.
202
+ /// </remarks>
203
+ protected override void OnDriverProcessStarting ( DriverProcessStartingEventArgs eventArgs )
204
+ {
205
+ if ( ! string . IsNullOrEmpty ( this . LogPath ) )
206
+ {
207
+ string ? directory = Path . GetDirectoryName ( this . LogPath ) ;
208
+ if ( ! string . IsNullOrEmpty ( directory ) && ! Directory . Exists ( directory ) )
209
+ {
210
+ Directory . CreateDirectory ( directory ) ;
211
+ }
212
+
213
+ // Initialize the log writer
214
+ logWriter = new StreamWriter ( this . LogPath , append : true ) { AutoFlush = true } ;
215
+
216
+ // Configure process to redirect output
217
+ eventArgs . DriverServiceProcessStartInfo . RedirectStandardOutput = true ;
218
+ eventArgs . DriverServiceProcessStartInfo . RedirectStandardError = true ;
219
+ }
220
+
221
+ base . OnDriverProcessStarting ( eventArgs ) ;
222
+ }
223
+
224
+ /// <summary>
225
+ /// Handles the event when the driver process has started.
226
+ /// </summary>
227
+ /// <param name="eventArgs">The event arguments containing information about the started driver process.</param>
228
+ /// <remarks>
229
+ /// This method reads the output and error streams asynchronously and writes them to the log file if available.
230
+ /// </remarks>
231
+ protected override void OnDriverProcessStarted ( DriverProcessStartedEventArgs eventArgs )
232
+ {
233
+ if ( logWriter == null ) return ;
234
+ if ( eventArgs . StandardOutputStreamReader != null )
235
+ {
236
+ _ = Task . Run ( ( ) => ReadStreamAsync ( eventArgs . StandardOutputStreamReader ) ) ;
237
+ }
238
+
239
+ if ( eventArgs . StandardErrorStreamReader != null )
240
+ {
241
+ _ = Task . Run ( ( ) => ReadStreamAsync ( eventArgs . StandardErrorStreamReader ) ) ;
242
+ }
243
+
244
+ base . OnDriverProcessStarted ( eventArgs ) ;
245
+ }
246
+
247
+ /// <summary>
248
+ /// Disposes of the resources used by the <see cref="FirefoxDriverService"/> instance.
249
+ /// </summary>
250
+ /// <param name="disposing">A value indicating whether the method is being called from Dispose.</param>
251
+ /// <remarks>
252
+ /// If disposing is true, it disposes of the log writer if it exists.
253
+ /// </remarks>
254
+ protected override void Dispose ( bool disposing )
255
+ {
256
+ if ( logWriter != null && disposing )
257
+ {
258
+ logWriter . Dispose ( ) ;
259
+ logWriter = null ;
260
+ }
261
+
262
+ base . Dispose ( disposing ) ;
263
+ }
264
+
180
265
/// <summary>
181
266
/// Creates a default instance of the FirefoxDriverService.
182
267
/// </summary>
@@ -258,4 +343,24 @@ private static string FirefoxDriverServiceFileName()
258
343
259
344
return fileName ;
260
345
}
346
+
347
+ private async Task ReadStreamAsync ( StreamReader reader )
348
+ {
349
+ try
350
+ {
351
+ string ? line ;
352
+ while ( ( line = await reader . ReadLineAsync ( ) ) != null )
353
+ {
354
+ if ( logWriter != null )
355
+ {
356
+ logWriter . WriteLine ( $ "{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} { line } ") ;
357
+ }
358
+ }
359
+ }
360
+ catch ( Exception ex )
361
+ {
362
+ // Log or handle the exception appropriately
363
+ System . Diagnostics . Debug . WriteLine ( $ "Error reading stream: { ex . Message } ") ;
364
+ }
365
+ }
261
366
}
0 commit comments