Skip to content

Commit 3c1a376

Browse files
diemolnvborisenko
andauthored
[dotnet] Enabling drivers to set log to console. (#16097)
* [dotnet] Enabling drivers to set log to console. This will be false by default, following what the .NET bindings have usually done. Helps with #12273 * [dotnet] Avoid changing state of variable. LogPath takes precedence anyway. * Using existing logger functionality. Messages will be shown to the console using the Logger functionality. Adding a test that listens to the logger and checks that logs are being sent to console. Fixing an issue when sending the log to a file, as the OnDriverProcessStarted method in the base class was not being invoked. * Running format script. * WriteDriverLogToConsole Internal variable to avoid using the stream when logs are sent to a file. Not exposed to the user. * Format script. * Update dotnet/src/webdriver/DriverService.cs Co-authored-by: Nikolay Borisenko <[email protected]> * Switching from async tasks to sync threads. * Format script. * Reading output through async events. * Addressing PR comments --------- Co-authored-by: Nikolay Borisenko <[email protected]>
1 parent cc8315a commit 3c1a376

File tree

4 files changed

+114
-68
lines changed

4 files changed

+114
-68
lines changed

dotnet/src/webdriver/DriverProcessStartedEventArgs.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
using System;
2121
using System.Diagnostics;
22-
using System.IO;
2322

2423
namespace OpenQA.Selenium;
2524

@@ -41,31 +40,10 @@ public DriverProcessStartedEventArgs(Process driverProcess)
4140
}
4241

4342
this.ProcessId = driverProcess.Id;
44-
if (driverProcess.StartInfo.RedirectStandardOutput && !driverProcess.StartInfo.UseShellExecute)
45-
{
46-
this.StandardOutputStreamReader = driverProcess.StandardOutput;
47-
}
48-
49-
if (driverProcess.StartInfo.RedirectStandardError && !driverProcess.StartInfo.UseShellExecute)
50-
{
51-
this.StandardErrorStreamReader = driverProcess.StandardError;
52-
}
5343
}
5444

5545
/// <summary>
5646
/// Gets the unique ID of the driver executable process.
5747
/// </summary>
5848
public int ProcessId { get; }
59-
60-
/// <summary>
61-
/// Gets a <see cref="StreamReader"/> object that can be used to read the contents
62-
/// printed to <c>stdout</c> by a driver service process.
63-
/// </summary>
64-
public StreamReader? StandardOutputStreamReader { get; }
65-
66-
/// <summary>
67-
/// Gets a <see cref="StreamReader"/> object that can be used to read the contents
68-
/// printed to <c>stderr</c> by a driver service process.
69-
/// </summary>
70-
public StreamReader? StandardErrorStreamReader { get; }
7149
}

dotnet/src/webdriver/DriverService.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
using System.Net;
2727
using System.Net.Http;
2828
using System.Threading.Tasks;
29+
using OpenQA.Selenium.Internal.Logging;
2930

3031
namespace OpenQA.Selenium;
3132

@@ -37,6 +38,8 @@ public abstract class DriverService : ICommandServer
3738
private bool isDisposed;
3839
private Process? driverServiceProcess;
3940

41+
private static readonly ILogger _logger = Log.GetLogger(typeof(DriverService));
42+
4043
/// <summary>
4144
/// Initializes a new instance of the <see cref="DriverService"/> class.
4245
/// </summary>
@@ -243,6 +246,12 @@ public void Start()
243246
this.driverServiceProcess.StartInfo.UseShellExecute = false;
244247
this.driverServiceProcess.StartInfo.CreateNoWindow = this.HideCommandPromptWindow;
245248

249+
this.driverServiceProcess.StartInfo.RedirectStandardOutput = true;
250+
this.driverServiceProcess.StartInfo.RedirectStandardError = true;
251+
252+
this.driverServiceProcess.OutputDataReceived += this.OnDriverProcessDataReceived;
253+
this.driverServiceProcess.ErrorDataReceived += this.OnDriverProcessDataReceived;
254+
246255
DriverProcessStartingEventArgs eventArgs = new DriverProcessStartingEventArgs(this.driverServiceProcess.StartInfo);
247256
this.OnDriverProcessStarting(eventArgs);
248257

@@ -251,6 +260,9 @@ public void Start()
251260
DriverProcessStartedEventArgs processStartedEventArgs = new DriverProcessStartedEventArgs(this.driverServiceProcess);
252261
this.OnDriverProcessStarted(processStartedEventArgs);
253262

263+
this.driverServiceProcess.BeginOutputReadLine();
264+
this.driverServiceProcess.BeginErrorReadLine();
265+
254266
if (!serviceAvailable)
255267
{
256268
throw new WebDriverException($"Cannot start the driver service on {this.ServiceUrl}");
@@ -308,6 +320,23 @@ protected virtual void OnDriverProcessStarted(DriverProcessStartedEventArgs even
308320
this.DriverProcessStarted?.Invoke(this, eventArgs);
309321
}
310322

323+
/// <summary>
324+
/// Handles the output and error data received from the driver process.
325+
/// </summary>
326+
/// <param name="sender">The sender of the event.</param>
327+
/// <param name="args">The data received event arguments.</param>
328+
/// <param name="isError">A value indicating whether the data received is from the error stream.</param>
329+
protected virtual void OnDriverProcessDataReceived(object sender, DataReceivedEventArgs args)
330+
{
331+
if (string.IsNullOrEmpty(args.Data))
332+
return;
333+
334+
if (_logger.IsEnabled(LogEventLevel.Trace))
335+
{
336+
_logger.Trace(args.Data);
337+
}
338+
}
339+
311340
/// <summary>
312341
/// Stops the DriverService.
313342
/// </summary>

dotnet/src/webdriver/Firefox/FirefoxDriverService.cs

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919

2020
using OpenQA.Selenium.Internal;
2121
using System;
22+
using System.Diagnostics;
2223
using System.Globalization;
2324
using System.IO;
2425
using System.Text;
25-
using System.Threading.Tasks;
2626

2727
namespace OpenQA.Selenium.Firefox;
2828

@@ -223,11 +223,11 @@ protected override string CommandLineArguments
223223
}
224224

225225
/// <summary>
226-
/// Handles the event when the driver service process is starting.
226+
/// Called when the driver process is starting. This method sets up log file writing if a log path is specified.
227227
/// </summary>
228228
/// <param name="eventArgs">The event arguments containing information about the driver service process.</param>
229229
/// <remarks>
230-
/// This method initializes a log writer if a log path is specified and redirects output streams to capture logs.
230+
/// This method initializes a log writer if a log path is specified.
231231
/// </remarks>
232232
protected override void OnDriverProcessStarting(DriverProcessStartingEventArgs eventArgs)
233233
{
@@ -239,40 +239,37 @@ protected override void OnDriverProcessStarting(DriverProcessStartingEventArgs e
239239
Directory.CreateDirectory(directory);
240240
}
241241

242-
// Initialize the log writer
243242
logWriter = new StreamWriter(this.LogPath, append: true) { AutoFlush = true };
244-
245-
// Configure process to redirect output
246-
eventArgs.DriverServiceProcessStartInfo.RedirectStandardOutput = true;
247-
eventArgs.DriverServiceProcessStartInfo.RedirectStandardError = true;
248243
}
249244

250245
base.OnDriverProcessStarting(eventArgs);
251246
}
252247

253248
/// <summary>
254-
/// Handles the event when the driver process has started.
249+
/// Handles the output and error data received from the driver process and sends it to the log writer if available.
255250
/// </summary>
256-
/// <param name="eventArgs">The event arguments containing information about the started driver process.</param>
257-
/// <remarks>
258-
/// This method reads the output and error streams asynchronously and writes them to the log file if available.
259-
/// </remarks>
260-
protected override void OnDriverProcessStarted(DriverProcessStartedEventArgs eventArgs)
251+
/// <param name="sender">The sender of the event.</param>
252+
/// <param name="args">The data received event arguments.</param>
253+
/// <param name="isError">A value indicating whether the data received is from the error stream.</param>
254+
protected override void OnDriverProcessDataReceived(object sender, DataReceivedEventArgs args)
261255
{
262-
if (logWriter == null) return;
263-
if (eventArgs.StandardOutputStreamReader != null)
256+
if (string.IsNullOrEmpty(args.Data))
257+
return;
258+
259+
if (!string.IsNullOrEmpty(this.LogPath))
264260
{
265-
_ = Task.Run(() => ReadStreamAsync(eventArgs.StandardOutputStreamReader));
261+
if (logWriter != null)
262+
{
263+
logWriter.WriteLine(args.Data);
264+
}
266265
}
267-
268-
if (eventArgs.StandardErrorStreamReader != null)
266+
else
269267
{
270-
_ = Task.Run(() => ReadStreamAsync(eventArgs.StandardErrorStreamReader));
268+
base.OnDriverProcessDataReceived(sender, args);
271269
}
272-
273-
base.OnDriverProcessStarted(eventArgs);
274270
}
275271

272+
276273
/// <summary>
277274
/// Disposes of the resources used by the <see cref="FirefoxDriverService"/> instance.
278275
/// </summary>
@@ -372,24 +369,4 @@ private static string FirefoxDriverServiceFileName()
372369

373370
return fileName;
374371
}
375-
376-
private async Task ReadStreamAsync(StreamReader reader)
377-
{
378-
try
379-
{
380-
string? line;
381-
while ((line = await reader.ReadLineAsync()) != null)
382-
{
383-
if (logWriter != null)
384-
{
385-
logWriter.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {line}");
386-
}
387-
}
388-
}
389-
catch (Exception ex)
390-
{
391-
// Log or handle the exception appropriately
392-
System.Diagnostics.Debug.WriteLine($"Error reading stream: {ex.Message}");
393-
}
394-
}
395372
}

dotnet/test/firefox/FirefoxDriverServiceTest.cs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,52 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using System;
21+
using System.Collections.Generic;
2022
using NUnit.Framework;
2123
using System.IO;
24+
using System.Linq;
25+
using OpenQA.Selenium.Internal.Logging;
2226

2327
namespace OpenQA.Selenium.Firefox;
2428

2529
[TestFixture]
26-
public class FirefoxDriverServiceTest : DriverTestFixture
30+
public class FirefoxDriverServiceTest
2731
{
32+
private TestLogHandler testLogHandler;
33+
34+
private void ResetGlobalLog()
35+
{
36+
Log.SetLevel(LogEventLevel.Info);
37+
Log.Handlers.Clear().Handlers.Add(new TextWriterHandler(Console.Error));
38+
}
39+
40+
[SetUp]
41+
public void SetUp()
42+
{
43+
ResetGlobalLog();
44+
45+
testLogHandler = new TestLogHandler();
46+
}
47+
48+
[TearDown]
49+
public void TearDown()
50+
{
51+
ResetGlobalLog();
52+
}
53+
2854
[Test]
2955
public void ShouldRedirectGeckoDriverLogsToFile()
3056
{
3157
FirefoxOptions options = new FirefoxOptions();
3258
string logPath = Path.GetTempFileName();
33-
options.LogLevel = FirefoxDriverLogLevel.Trace;
59+
options.LogLevel = FirefoxDriverLogLevel.Info;
3460

3561
FirefoxDriverService service = FirefoxDriverService.CreateDefaultService();
3662
service.LogPath = logPath;
3763

38-
IWebDriver driver2 = new FirefoxDriver(service, options);
64+
IWebDriver firefoxDriver = new FirefoxDriver(service, options);
65+
firefoxDriver.Quit();
3966

4067
try
4168
{
@@ -45,9 +72,44 @@ public void ShouldRedirectGeckoDriverLogsToFile()
4572
}
4673
finally
4774
{
48-
driver2.Quit();
4975
File.Delete(logPath);
5076
}
5177
}
5278

79+
[Test]
80+
public void ShouldRedirectGeckoDriverLogsToConsole()
81+
{
82+
Log.SetLevel(LogEventLevel.Trace).Handlers.Add(testLogHandler);
83+
FirefoxOptions options = new FirefoxOptions();
84+
options.LogLevel = FirefoxDriverLogLevel.Info;
85+
86+
FirefoxDriverService service = FirefoxDriverService.CreateDefaultService();
87+
88+
IWebDriver firefoxDriver = new FirefoxDriver(service, options);
89+
90+
try
91+
{
92+
Assert.That(testLogHandler.Events, Has.Count.AtLeast(1));
93+
Assert.That(testLogHandler.Events.Any(e => e.Message.Contains("geckodriver")), Is.True);
94+
}
95+
finally
96+
{
97+
firefoxDriver.Quit();
98+
}
99+
}
100+
}
101+
102+
class TestLogHandler : ILogHandler
103+
{
104+
public ILogHandler Clone()
105+
{
106+
return this;
107+
}
108+
109+
public void Handle(LogEvent logEvent)
110+
{
111+
Events.Add(logEvent);
112+
}
113+
114+
public IList<LogEvent> Events { get; internal set; } = new List<LogEvent>();
53115
}

0 commit comments

Comments
 (0)