Browser logs - playwright chromium acquisition and usage
- Dominant language
- C#
- Stars
- 6.3k
- Forks
- 991
- Avg merge
- 2d 15h
- Merged PRs (30d)
- 196
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Is your feature request related to a problem? Please describe the problem.
We can't use neither chrome or edge for our browser due to organisation policy on both blocking the CDP channel from being created.
To still be able to use Browser logs, we had to manually install a chromium browser using playwright, and write custom code to lookup the location of the executable on disk, and we only think that it works for MacOS, haven't tested linux / windows.
### Describe the solution you'd like
Adding another well known browser / playwright integration so that aspire uses the playwright installed (or aspire installs) browser and then connects to it.
Something like:
`WithBrowserLogs(browserLogsResource => browserLogsResource.WithPlaywrightInstall("chrome", version: "latest"))`
That will use the playwright installation location and use the browser there.
### Additional context
This is our current workaround:
```csharp
///
/// Locates the Chromium executable that Playwright downloads into its browser cache.
///
/// We deliberately target Playwright's Chromium (branded "Google Chrome for Testing")
/// rather than a system Chrome/Edge: PANW org policy blocks the Chrome DevTools Protocol
/// pipe on managed Chrome/Edge installs, so Aspire's WithBrowserLogs() auto-detection
/// (which prefers system Chrome/Edge) connects but never receives CDP events. Playwright's
/// Chromium is not subject to that policy and its CDP pipe works.
///
///
/// Resolution is dynamic — no hardcoded user or version. It honours the
/// PLAYWRIGHT_BROWSERS_PATH override, falls back to the per-OS default cache root,
/// picks the highest chromium-<build> build number, and locates the platform
/// executable inside it. Returns when Playwright's Chromium is not
/// installed, so the caller can fall back to Aspire's built-in browser auto-detection.
///
///
/// Absolute path to the Chromium executable, or if not found.
public static string? FindPlaywrightChromium()
{
var cacheRoot = ResolvePlaywrightCacheRoot();
if (cacheRoot is null || !Directory.Exists(cacheRoot))
{
return null;
}
// Playwright layout: /chromium-/chrome-/.
// Sort by the numeric build so the newest install wins (lexical sort would rank
// "chromium-999" above "chromium-1237").
var newestBuildDir = Directory.EnumerateDirectories(cacheRoot, "chromium-*")
.Select(dir => (dir, build: ParseBuildNumber(Path.GetFileName(dir))))
.Where(x => x.build >= 0)
.OrderByDescending(x => x.build)
.Select(x => x.dir)
.FirstOrDefault();
if (newestBuildDir is null)
{
return null;
}
foreach (var relative in PlaywrightChromiumExecutableCandidates())
{
var candidate = Path.Combine(newestBuildDir, relative);
if (File.Exists(candidate))
{
return candidate;
}
}
return null;
}
private static string? ResolvePlaywrightCacheRoot()
{
// Playwright's own override takes precedence, matching how the Playwright CLI resolves it.
var overridePath = Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH");
if (!string.IsNullOrWhiteSpace(overridePath) && overridePath != "0")
{
return overridePath;
}
var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
if (string.IsNullOrEmpty(home))
{
return null;
}
if (OperatingSystem.IsMacOS())
{
return Path.Combine(home, "Library", "Caches", "ms-playwright");
}
if (OperatingSystem.IsWindows())
{
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
return string.IsNullOrEmpty(localAppData)
? null
: Path.Combine(localAppData, "ms-playwright");
}
// Linux and other Unixes.
return Path.Combine(home, ".cache", "ms-playwright");
}
private static IEnumerable PlaywrightChromiumExecutableCandidates()
{
if (OperatingSystem.IsMacOS())
{
// Newer builds ship as "Google Chrome for Testing"; older builds as "Chromium".
// Match both arm64 and x64 platform folder names.
yield return Path.Combine("chrome-mac-arm64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing");
yield return Path.Combine("chrome-mac-x64", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing");
yield return Path.Combine("chrome-mac", "Google Chrome for Testing.app", "Contents", "MacOS", "Google Chrome for Testing");
yield return Path.Combine("chrome-mac", "Chromium.app", "Contents", "MacOS", "Chromium");
}
else if (OperatingSystem.IsWindows())
{
yield return Path.Combine("chrome-win", "chrome.exe");
}
else
{
yield return Path.Combine("chrome-linux", "chrome");
}
}
private static int ParseBuildNumber(string chromiumDirName)
{
// chromiumDirName looks like "chromium-1237"; extract the trailing integer.
var dashIndex = chromiumDirName.LastIndexOf('-');
return dashIndex >= 0
&& int.TryParse(chromiumDirName.AsSpan(dashIndex + 1), out var build)
? build
: -1;
}
```
Contributor guide
Research direction
The issue names WithBrowserLogs and provides a Playwright Chromium cache lookup workaround, but no repository files or tests. Start by locating the WithBrowserLogs implementation and its browser detection path, then compare the proposed Playwright integration with the workaround across macOS, Linux, and Windows. Done means Aspire can use the Playwright-installed browser for browser logs without custom executable discovery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- observability-sre
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100