Skip to content

Specify LogPath variable in C# #7259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
rj-max opened this issue Jun 4, 2019 · 7 comments
Closed

Specify LogPath variable in C# #7259

rj-max opened this issue Jun 4, 2019 · 7 comments
Labels
C-dotnet .NET Bindings

Comments

@rj-max
Copy link

rj-max commented Jun 4, 2019

Is it possible to provide a way to specify LogPath in FirefoxDriverService class with dotnet bindings? I have a VM running tests using Selenium WebDriver and it crashes frequently. Without enabling these logs, it's not possible to dig deeper. I notice that this log_path is available in Python language bindings but not dotnet.

 var service = FirefoxDriverService.CreateDefaultService
 (Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
 service.LogPath = "C:\geckodriver.log";
@jimevans
Copy link
Member

jimevans commented Jun 4, 2019

The .NET bindings' DriverService classes are merely wrappers around the driver-specific executable files, and only provide properties that map to switches available via the command line of those executables. This is a design decision for the .NET bindings, and is unlikely to change. Geckodriver does not provide any native mechanism accessible via a command line argument to redirect its log output to a file. Other language bindings are likely doing this by capturing the content written to stdout and/or stderr, and redirecting that output to a file.

If you want to do this via the .NET bindings, you'll do so by using standard .NET techniques for capturing the output of console applications. That is to say, you need to modify the ProcessStartInfo object used to start the geckodriver.exe process, and get the streams for stdout and stderr from the Process object.

The first is easy, and is available today in 4.0-alpha1 of the .NET bindings. To modify this object, you'll need to attach to the FirefoxDriverService object's DriverProcessStarting event, and in your event handler, modify the ProcessStartInfo object returned by the DriverServiceProcessStartInfo property.

The second part is not possible in the latest releases of the .NET bindings, and will need to wait until 4.0-alpha2 to be available.

An example would look something like the following:

// NOTE: Below code written from memory, without the aid of Visual Studio or another
// C# IDE. The below code may not work (or even compile!) correctly. It is provided
// merely as a courtesy and example.
private StreamReader stdOut;
private StreamReader stdErr;

private void ExecuteWithFirefoxDriver()
{
    FirefoxDriverService service = FirefoxDriverService.CreateDefaultService();
    service.DriverProcessStarting += OnDriverProcessStarting;
    service.DriverProcessStarted += OnDriverProcessStarted;

    IWebDriver driver = new FirefoxDriver(service);

    // Do other things with the driver instance here

    driver.Quit();
    string stdOutContent = stdOut.ReadToEnd();
    string stdErrContent = stdErr.ReadToEnd();

    // Do whatever you want with the content. Stream to a file,
    // print to the console, whatever.
    Console.WriteLine("Execution complete.");
    Console.WriteLine("Contents of stdout: {0}", stdOutContent);
    Console.WriteLine("Contents of stderr: {0}", stdErrContent);
}

private void OnDriverProcessStarting(object sender, DriverProcessStartingEventArgs e)
{
    // Redirect both stdout and stderr to get the output to both locations.
    e.DriverServiceProcessStartInfo.UseShellExecute = false;
    e.RedirectStandardOutput = true;
    e.RedirectStandardError = true;
}

private void OnDriverProcessStarted(object sender, DriverProcessStartedEventArgs e)
{
    // Hook into the stream readers for both stdout and stderr.
    stdOut = e.StandardOutputStreamReader;
    stdErr = e.StandardErrorStreamReader;
}

@rj-max
Copy link
Author

rj-max commented Jun 10, 2019

Can I request this feature to be added with C#?

@jimevans
Copy link
Member

jimevans commented Jun 11, 2019

@rj-max What feature? I already mentioned that the full example provided above would be available in 4.0-alpha2 (already added in 9f90d99). There is no plan to release another 3.x release of the .NET bindings.

Given that the pattern of the .NET bindings’ Service classes are strictly intended to model the command-line switches of the service executable, and passing a path for a log file is not supported by a command-line switch for geckodriver, no, there are no plans to add a LogPath or similar property to the FirefoxDriverService class. This is in the interest of transparency and avoiding “magic” being done by the bindings.

@andreastt
Copy link
Member

If the service classes are meant to reflect the subprocess’ flags identically in their API, is it an option to introduce an intermediary abstraction that offers a more user-friendly API and that maps down to the right flags?

@barancev barancev added the C-dotnet .NET Bindings label Jun 29, 2019
@tdierickx
Copy link

tdierickx commented May 7, 2021

  • Selenium 3.141.0
  • C#, .NET Core 3.1.14 (actually, "dotnet script MyScript.csx")
  • geckodriver-v0.29.1-win64
  • Firefox Portable 88.0 (64-bit)

@rj-max, this probably doesn't help now since it's two years later, but the following does work for me in my initial testing to log all the lower-level, geckodriver output to a log file that was bubbling up and inter-mixing with all my C# program's higher-level, output and ruining all my own, nice Console.WriteLine("blah") output I was trying to log out to a file :-)

I'm trying to convert a Python script (has always worked great) to a stand-alone C# dotnet script file and the lack of direct logging in the C# implementation was quite the "surprise" to me and been the biggest headache of all in this for me.

note: I've been using a Python script for two years that uses geckodriver with Selenium and, of course, the Python library has the ability to specify a log_file for all the back-end geckodriver output built right into the constructor so I had been completely surprised, disappointed, and frustrated just like you not being able to do something so simple using C# library; anyway, I digress..

  1. I created a "geckodriver.bat" in the same folder as my "geckodriver.exe" with contents like so:
SET FOLDER=C:\Data\Test
@ECHO OFF
ECHO Starting geckodriver: %0 %*
"%FOLDER%\bin\webdriver\gecko\v0.29.1\geckodriver-v0.29.1-win64\geckodriver.exe" --log info %* >"%FOLDER%\logs\geckodriver.log" 2>&1

Inspiration for this came from stackoverflow

  1. In my C# code, I connect to the bat file instead of the exe like so:
FirefoxDriverService geckoService = FirefoxDriverService.CreateDefaultService(gecko_driver_folder, "geckodriver.bat");
geckoService.FirefoxBinaryPath = browser_exe_path;
geckoService.Host = "::1";
geckoService.HideCommandPromptWindow = true;

FirefoxDriver browser = new FirefoxDriver(geckoService, browserOptions);
...
browser.Quit();
  1. While that did the trick, it kept leaving an orphaned geckodriver.exe lingering around and, consequently, a lock on the geckodriver.log it has written to. I tried Dispose() and a few other things and nothing seemed to work except the following hack after the browser.Quit() call:
...
browser.Quit();

Process[] geckodriver_processes = Process.GetProcessesByName("geckodriver");
foreach (Process geckodriver_process in geckodriver_processes) {
    if (geckodriver_process.MainModule.FileName == $"{gecko_driver_folder}\\geckodriver.exe") {
        geckodriver_process.Kill();
        break;
    }
}
  1. For my actual dotnet script call I have a bat file that runs the following and logs all "my" output like so:
SET FOLDER=C:\Data\Test
del "%FOLDER%\logs\*.*" /q
dotnet script "%FOLDER%\DownloadFiles.csx" >"%FOLDER%\logs\DownloadFiles.log" 2>&1

So, now because of #2 above, all the low-level geckodriver output goes to a different log file ... just like you can do in Python with a very simple "log_path" property setting:

browser = selenium_webdriver.Firefox(
    options=browser_options,
    firefox_binary=path_to_firefox_exe,
    executable_path=path_to_geckodriver_exe,
    log_path=path_to_geckodriver_log,
    capabilities=capabilities_argument,
    firefox_profile=fp
)

I hope this helps somebody (and works for them), I searched the entire interwebs trying to find a way to do this!

And I agree with you, I would hope the C# implementation considers adding the functionality that Python has already afforded for some time.

@diemol
Copy link
Member

diemol commented Feb 10, 2022

Thank you for chiming in, but as mentioned at the beginning, this will be added to .NET when this https://bugzilla.mozilla.org/show_bug.cgi?id=1611003 is solved and released.

I understand the frustration when a binding offers a feature and the other one does not. This is something we are actively improving to avoid these types of situation, now we are consistently aiming to have cross binding feature parity.

@diemol diemol closed this as completed Feb 10, 2022
@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked and limited conversation to collaborators Mar 13, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
C-dotnet .NET Bindings
Projects
None yet
Development

No branches or pull requests

6 participants