2222using System . Globalization ;
2323using System . IO ;
2424using System . Text ;
25+ using System . Threading . Tasks ;
2526
2627namespace OpenQA . Selenium . Firefox ;
2728
@@ -31,6 +32,11 @@ namespace OpenQA.Selenium.Firefox;
3132public sealed class FirefoxDriverService : DriverService
3233{
3334 private const string DefaultFirefoxDriverServiceFileName = "geckodriver" ;
35+
36+ /// <summary>
37+ /// Process management fields for the log writer.
38+ /// </summary>
39+ private StreamWriter ? logWriter ;
3440
3541 /// <summary>
3642 /// Initializes a new instance of the <see cref="FirefoxDriverService"/> class.
@@ -87,6 +93,16 @@ protected override DriverOptions GetDefaultDriverOptions()
8793 /// </summary>
8894 public bool OpenBrowserToolbox { get ; set ; }
8995
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+
90106 /// <summary>
91107 /// Gets or sets the level at which log output is displayed.
92108 /// </summary>
@@ -176,6 +192,74 @@ protected override string CommandLineArguments
176192 return argsBuilder . ToString ( ) . Trim ( ) ;
177193 }
178194 }
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+ }
260+
261+ base . Dispose ( disposing ) ;
262+ }
179263
180264 /// <summary>
181265 /// Creates a default instance of the FirefoxDriverService.
@@ -258,4 +342,16 @@ private static string FirefoxDriverServiceFileName()
258342
259343 return fileName ;
260344 }
345+
346+ private async Task ReadStreamAsync ( StreamReader reader )
347+ {
348+ string ? line ;
349+ while ( ( line = await reader . ReadLineAsync ( ) ) != null )
350+ {
351+ if ( logWriter != null )
352+ {
353+ logWriter . WriteLine ( $ "{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} { line } ") ;
354+ }
355+ }
356+ }
261357}
0 commit comments