From 4e564b2955f293ddd0d6ffa9008424c7f9c7e138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dar=C3=ADo=20Kondratiuk?= Date: Fri, 9 Oct 2020 09:36:24 -0300 Subject: [PATCH] feat(playwright): Log debug info (#924) * feat(playwright): Log debug info * debug as string * debug note --- demos/PdfDemo/PdfDemo-Local.csproj | 4 +++ demos/PdfDemo/Program.cs | 10 ++++++- docfx_project/examples/Playwright.Logger.md | 29 +++++++++++++++++++ docfx_project/examples/toc.yml | 4 ++- src/PlaywrightSharp/Playwright.cs | 11 ++++++- src/PlaywrightSharp/Transport/Connection.cs | 9 ++++-- .../Transport/IConnectionTransport.cs | 5 ++++ .../Transport/LogReceivedEventArgs.cs | 22 ++++++++++++++ .../Transport/StdIOTransport.cs | 4 +++ 9 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 docfx_project/examples/Playwright.Logger.md create mode 100644 src/PlaywrightSharp/Transport/LogReceivedEventArgs.cs diff --git a/demos/PdfDemo/PdfDemo-Local.csproj b/demos/PdfDemo/PdfDemo-Local.csproj index 396181774a..a9749b3527 100644 --- a/demos/PdfDemo/PdfDemo-Local.csproj +++ b/demos/PdfDemo/PdfDemo-Local.csproj @@ -8,4 +8,8 @@ + + + + diff --git a/demos/PdfDemo/Program.cs b/demos/PdfDemo/Program.cs index 621ea131ad..eea9f22533 100644 --- a/demos/PdfDemo/Program.cs +++ b/demos/PdfDemo/Program.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using PlaywrightSharp; namespace PdfDemo @@ -12,7 +13,14 @@ static async Task Main(string[] args) { Console.WriteLine("Installing playwright"); await Playwright.InstallAsync(); - using var playwright = await Playwright.CreateAsync(); + ILoggerFactory loggerFactory = LoggerFactory.Create(builder => + { + builder.SetMinimumLevel(LogLevel.Debug); + builder.AddDebug(); + builder.AddFilter((f, _) => f == "PlaywrightSharp.Playwright"); + }); + + using var playwright = await Playwright.CreateAsync(loggerFactory, debug: "pw:api"); await using var browser = await playwright.Chromium.LaunchAsync(new LaunchOptions { Headless = true }); var page = await browser.NewPageAsync(); diff --git a/docfx_project/examples/Playwright.Logger.md b/docfx_project/examples/Playwright.Logger.md new file mode 100644 index 0000000000..8c789c7dc0 --- /dev/null +++ b/docfx_project/examples/Playwright.Logger.md @@ -0,0 +1,29 @@ +# How to get internal logs +_Contributors: [Dario Kondratiuk](https://www.hardkoded.com/)_ + +## Problem + +You need to get the internal log information to debug a problem. + +## Solution + +Playwright Sharp uses a [.NET Logger Factory](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.iloggerfactory?view=dotnet-plat-ext-3.1&WT.mc_id=DT-MVP-5003814) to log the communication between the library and the playwright driver. + +It uses two categories: + * All the communication between the library and the driver is under the `PlaywrightSharp.Transport.Connection` category. + * The debug information coming from the driver, when `debug: "pw:api"` is set, is under the `PlaywrightSharp.Playwright` category. The `debug` argument is a shortcut to the `DEBUG` environment variable Playwright uses to setup its logging tool. + +You create an `ILoggerFactory` in the same way you would build a logger in ASP.NET. You can also use extension methods, like [AddDebug](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.debugloggerfactoryextensions.adddebug?view=dotnet-plat-ext-3.1&WT.mc_id=DT-MVP-5003814) to send the log in the output window, or [AddConsole](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.consoleloggerextensions.addconsole?view=dotnet-plat-ext-3.1&WT.mc_id=DT-MVP-5003814) to send it to the console. + +```cs +ILoggerFactory loggerFactory = LoggerFactory.Create(builder => +{ + builder.SetMinimumLevel(LogLevel.Debug); + builder.AddDebug(); + builder.AddFilter((f, _) => f == "PlaywrightSharp.Playwright"); +}); + +using var playwright = await Playwright.CreateAsync(loggerFactory, debug: "pw:api"); +``` + +The `debug` is only needed for the `PlaywrightSharp.Playwright` category. `PlaywrightSharp.Transport.Connection` logs will come with no extra argument. \ No newline at end of file diff --git a/docfx_project/examples/toc.yml b/docfx_project/examples/toc.yml index 98aa3cd8fe..5ab2ed5531 100644 --- a/docfx_project/examples/toc.yml +++ b/docfx_project/examples/toc.yml @@ -1,4 +1,6 @@ - name: Basic Examples items: - name: How to take screenshots and save it as a file - href: Page.ScreenshotAsync.md \ No newline at end of file + href: Page.ScreenshotAsync.md + - name: How to get internal logs + href: Playwright.Logger.md \ No newline at end of file diff --git a/src/PlaywrightSharp/Playwright.cs b/src/PlaywrightSharp/Playwright.cs index 4d3c3304c9..45e2ba417e 100644 --- a/src/PlaywrightSharp/Playwright.cs +++ b/src/PlaywrightSharp/Playwright.cs @@ -97,14 +97,23 @@ public IBrowserType this[string browserType] /// Specify a shared folder that playwright will use to download browsers and to look for browsers when launching browser instances. /// It is a shortcut to the PLAYWRIGHT_BROWSERS_PATH environment variable. /// + /// Enables the playwright driver log. Pass `pw:api` to get the Playwright API log. + /// It is a shortcut to the DEBUG=pw:api environment variable. + /// /// A that completes when the playwright driver is ready to be used. public static async Task CreateAsync( ILoggerFactory loggerFactory = null, TransportTaskScheduler scheduler = null, string driversLocationPath = null, string driverExecutablePath = null, - string browsersPath = null) + string browsersPath = null, + string debug = null) { + if (!string.IsNullOrEmpty(debug)) + { + Environment.SetEnvironmentVariable("DEBUG", debug); + } + var connection = new Connection(loggerFactory, scheduler, driversLocationPath, driverExecutablePath, browsersPath); var playwright = await connection.WaitForObjectWithKnownName("Playwright").ConfigureAwait(false); diff --git a/src/PlaywrightSharp/Transport/Connection.cs b/src/PlaywrightSharp/Transport/Connection.cs index ce59bdbf85..3f2301e221 100644 --- a/src/PlaywrightSharp/Transport/Connection.cs +++ b/src/PlaywrightSharp/Transport/Connection.cs @@ -50,6 +50,10 @@ public Connection( Environment.SetEnvironmentVariable(BrowsersPathEnvironmentVariable, Path.GetFullPath(browsersPath)); } + _loggerFactory = loggerFactory; + _logger = _loggerFactory?.CreateLogger(); + var debugLogger = _loggerFactory?.CreateLogger(); + _rootObject = new ChannelOwnerBase(null, this, string.Empty); _playwrightServerProcess = GetProcess(driversLocationPath, driverExecutablePath); @@ -58,9 +62,8 @@ public Connection( _playwrightServerProcess.Exited += (sender, e) => Close("Process exited"); _transport = new StdIOTransport(_playwrightServerProcess, scheduler); _transport.MessageReceived += Transport_MessageReceived; + _transport.LogReceived += (s, e) => debugLogger?.LogInformation(e.Message); _transport.TransportClosed += (sender, e) => Close(e.CloseReason); - _loggerFactory = loggerFactory; - _logger = _loggerFactory?.CreateLogger(); } /// @@ -88,6 +91,7 @@ internal static async Task InstallAsync(string driverPath = null, string browser process.StartInfo.Arguments = "--install"; process.StartInfo.RedirectStandardOutput = false; process.StartInfo.RedirectStandardInput = false; + process.StartInfo.RedirectStandardError = false; process.EnableRaisingEvents = true; process.Exited += (sender, e) => tcs.TrySetResult(true); process.Start(); @@ -250,6 +254,7 @@ private static Process GetProcess(string driversLocationPath = null, string driv UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardInput = true, + RedirectStandardError = true, CreateNoWindow = true, }, }; diff --git a/src/PlaywrightSharp/Transport/IConnectionTransport.cs b/src/PlaywrightSharp/Transport/IConnectionTransport.cs index b3b325f59d..4513fd0ff7 100644 --- a/src/PlaywrightSharp/Transport/IConnectionTransport.cs +++ b/src/PlaywrightSharp/Transport/IConnectionTransport.cs @@ -13,6 +13,11 @@ public interface IConnectionTransport /// event EventHandler MessageReceived; + /// + /// Occurs when a log message is received. + /// + event EventHandler LogReceived; + /// /// Occurs when the transport is closed. /// diff --git a/src/PlaywrightSharp/Transport/LogReceivedEventArgs.cs b/src/PlaywrightSharp/Transport/LogReceivedEventArgs.cs new file mode 100644 index 0000000000..cd9232ca33 --- /dev/null +++ b/src/PlaywrightSharp/Transport/LogReceivedEventArgs.cs @@ -0,0 +1,22 @@ +using System; + +namespace PlaywrightSharp.Transport +{ + /// + /// Log received event arguments. + /// . + /// + public class LogReceivedEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// Message. + public LogReceivedEventArgs(string message) => Message = message; + + /// + /// Transport message. + /// + public string Message { get; } + } +} diff --git a/src/PlaywrightSharp/Transport/StdIOTransport.cs b/src/PlaywrightSharp/Transport/StdIOTransport.cs index 9c1a3b7766..b61aa10c3d 100644 --- a/src/PlaywrightSharp/Transport/StdIOTransport.cs +++ b/src/PlaywrightSharp/Transport/StdIOTransport.cs @@ -18,6 +18,8 @@ internal StdIOTransport(Process process, TransportTaskScheduler scheduler = null { _process = process; scheduler ??= ScheduleTransportTask; + process.ErrorDataReceived += (s, e) => LogReceived?.Invoke(this, new LogReceivedEventArgs(e.Data)); + process.BeginErrorReadLine(); scheduler(GetResponseAsync, _readerCancellationSource.Token); } @@ -29,6 +31,8 @@ internal StdIOTransport(Process process, TransportTaskScheduler scheduler = null public event EventHandler TransportClosed; + public event EventHandler LogReceived; + public bool IsClosed { get; private set; } ///