Reputation:
I am writing using C#
, selenium
chromeWebDriver
. When I try to read the browser console log file with selenium
I get:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
private void button1_Click(object sender, EventArgs e)
{
ChromeOptions options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.Warning);
IWebDriver driver = new ChromeDriver(options);
driver.Url = "https://www.google.com/";
var entries = driver.Manage().Logs.GetLog(LogType.Browser); // System.NullReferenceException
foreach (var entry in entries)
{
Console.WriteLine(entry.ToString());
}
}
Upvotes: 10
Views: 3646
Reputation: 111
This is my solution until Selenium 4 is out (will work also with Selenium 4). It is quick and dirty and was design to demonstrate how it can be done. Feel free to alter and improve.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Text;
namespace GetChromeConsoleLog
{
internal static class Program
{
private static void Main()
{
// setup options
var options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.All);
// do whatever actions
var driver = new ChromeDriver(options)
{
Url = "https://www.yahoo.com/"
};
var logs = driver.GetBrowserLogs();
// extract logs (using the extension method GetBrowserLogs)
foreach (var log in driver.GetBrowserLogs())
{
Console.WriteLine($"{log["timestamp"]}: {log["message"]}");
}
// cleanup
driver.Dispose();
// hold console
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
public static class WebDriverExtensions
{
public static IEnumerable<IDictionary<string, object>> GetBrowserLogs(this IWebDriver driver)
{
// not a chrome driver
if(driver.GetType() != typeof(ChromeDriver))
{
return Array.Empty<IDictionary<string, object>>();
}
// setup
var endpoint = GetEndpoint(driver);
var session = GetSession(driver);
var resource = $"{endpoint}session/{session}/se/log";
const string jsonBody = @"{""type"":""browser""}";
// execute
using (var httpClient = new HttpClient())
{
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(resource, content).GetAwaiter().GetResult();
var responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return AsLogEntries(responseBody);
}
}
private static string GetEndpoint(IWebDriver driver)
{
// setup
const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic;
// get RemoteWebDriver type
var remoteWebDriver = GetRemoteWebDriver(driver.GetType());
// get this instance executor > get this instance internalExecutor
var executor = remoteWebDriver.GetField("executor", Flags).GetValue(driver) as ICommandExecutor;
// get URL
var uri = executor.GetType().GetField("remoteServerUri", Flags).GetValue(executor) as Uri;
// result
return uri.AbsoluteUri;
}
private static Type GetRemoteWebDriver(Type type)
{
if (!typeof(RemoteWebDriver).IsAssignableFrom(type))
{
return type;
}
while (type != typeof(RemoteWebDriver))
{
type = type.BaseType;
}
return type;
}
private static SessionId GetSession(IWebDriver driver)
{
if (driver is IHasSessionId id)
{
return id.SessionId;
}
return new SessionId($"gravity-{Guid.NewGuid()}");
}
private static IEnumerable<IDictionary<string, object>> AsLogEntries(string responseBody)
{
// setup
var value = $"{JToken.Parse(responseBody)["value"]}";
return JsonConvert.DeserializeObject<IEnumerable<Dictionary<string, object>>>(value);
}
}
}
Upvotes: 4
Reputation: 21
This issue was supposedly fixed as it was reported in GitHub issue #7323 here, but I have actually attempted to test this fix in ChromeDriver Nuget version 77.0.3865.4000
and it still proves to be an issue.
Further experimentation with newer Chromedriver version 78.0.3904.7000
(currently Latest Stable version at the time of writing) shows that the issue still exists.
I have also experimented with using workarounds provided in other Selenium issue #7335 here back in September, and while this workaround does allow the driver to be instantiated, the logs are still inaccessible (and null).
Workaround when creating chromedriver instance: typeof(CapabilityType).GetField(nameof(CapabilityType.LoggingPreferences), BindingFlags.Static | BindingFlags.Public).SetValue(null, "goog:loggingPrefs");
Based on what MatthewSteeples said in that issue (see quote below), the fix is in place just not yet fully released to Nuget. Hopefully it will come in with the next release.
"The issue has been resolved but the fix is not (yet) available on NuGet so you'll need to roll your own if you need it before the next release is out" - MatthewSteeples September 25'th 2019
Edit: It may be worth mentioning the reason for using an older ChromeDriver Nuget is so that running automated tests locally and in the Hosted Azure Devops release pipeline is possible without manually modifying the nuget version locally.
Upvotes: 1