jetbrains/JetBrains.RenderDoc.RdClient/RenderDocHost.cs (11 lines of code) (raw):

using System.Diagnostics; using System.Text.RegularExpressions; using JetBrains.Diagnostics; using JetBrains.Lifetimes; namespace JetBrains.RenderDoc.RdClient; internal class RenderDocHost { // spdlog message in format [date-time] [group] [logLevel] msg private static readonly Regex HostMessageRegex = new(@"^\[[^]]+\] \[([^]]+)\] \[(trace|debug|info|warn|err|critical|off)\] (.*)", RegexOptions.Compiled); private static readonly Regex HostIntroductionRegex = new(@"^HOST_INTRODUCTION: PORT=(\d+)", RegexOptions.Compiled); private TaskCompletionSource<int>? myExitCodeSource; public Task<int> HostExitCode => myExitCodeSource?.Task ?? Task.FromException<int>(new InvalidOperationException("Host wasn't started")); /** * Starts host bounded to <paramref name="lifetime"/> and returns host port. * Host exit code may be awaited and obtained with <see cref="HostExitCode"/>. */ public Task<int> Start(Lifetime lifetime) => lifetime.Execute(() => { var startInfo = new ProcessStartInfo { FileName = "runtimes/osx-arm64/RenderDocHost", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }; var process = new Process { StartInfo = startInfo }; process.EnableRaisingEvents = true; process.StartInfo = startInfo; var exitCodeSource = new TaskCompletionSource<int>(); var portCompletionSource = new TaskCompletionSource<int>(); process.Exited += (src, _) => { var p = (Process)src; myExitCodeSource?.SetResult(p.ExitCode); portCompletionSource.TrySetCanceled(); }; var introduced = false; var serverLog = Log.GetLog<RenderDocClient>().GetSublogger("Host"); process.OutputDataReceived += (_, args) => { if (args.Data is not { } message) return; if (!introduced && HostIntroductionRegex.Match(message) is { Success: true } match) { portCompletionSource.SetResult(int.Parse(match.Groups[1].Value)); introduced = true; } else if (serverLog.IsVersboseEnabled()) LogHostMessage(serverLog, LoggingLevel.VERBOSE, message); }; process.ErrorDataReceived += (_, args) => { if (args.Data is { } message) LogHostMessage(serverLog, LoggingLevel.ERROR, message); }; if (!process.Start()) return Task.FromException<int>(new FileNotFoundException("Failed to start RenderDocHost process")); myExitCodeSource = exitCodeSource; process.BeginErrorReadLine(); process.BeginOutputReadLine(); lifetime.OnTermination(() => process.Kill()); return portCompletionSource.Task; }); private static void LogHostMessage(ILog serverLog, LoggingLevel defaultLoggingLevel, string message) { if (HostMessageRegex.Match(message) is { Success: true } match) { var loggingLevel = match.Groups[2].Value switch { "trace" => LoggingLevel.TRACE, "debug" => LoggingLevel.VERBOSE, "info" => LoggingLevel.INFO, "warn" => LoggingLevel.WARN, "err" => LoggingLevel.ERROR, "critical" => LoggingLevel.FATAL, _ => defaultLoggingLevel }; serverLog.Log(loggingLevel, $"{match.Groups[1]} | {match.Groups[3]}"); } else serverLog.Log(defaultLoggingLevel, message); } }