| File: src\Aspire.Hosting\Dcp\Process\ProcessOutputCapture.cs | Web Access |
| Project: src\src\Aspire.Hosting.Azure\Aspire.Hosting.Azure.csproj (Aspire.Hosting.Azure) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Aspire.Hosting.Utils; /// <summary> /// Retains a bounded tail of process output while tracking the total number of lines observed. /// </summary> internal sealed class ProcessOutputCapture(int maxRetainedLineCount) { private readonly object _lock = new(); private readonly Queue<string> _retainedLines = new(); private readonly int _maxRetainedLineCount = maxRetainedLineCount; /// <summary> /// Gets the total number of stdout and stderr lines observed. /// </summary> public int TotalLineCount { get; private set; } /// <summary> /// Adds a line of process output to the retained tail. /// </summary> /// <param name="line">The output line.</param> public void Add(string line) { lock (_lock) { TotalLineCount++; _retainedLines.Enqueue(line); while (_retainedLines.Count > _maxRetainedLineCount) { _retainedLines.Dequeue(); } } } /// <summary> /// Returns the retained output lines in order. /// </summary> /// <returns>The retained output lines.</returns> public string[] ToArray() { lock (_lock) { return [.. _retainedLines]; } } /// <summary> /// Returns the last <paramref name="maxLines"/> lines of output formatted for display. /// </summary> /// <param name="maxLines">The maximum number of lines to include.</param> /// <param name="outputDescription">The label used when the output is truncated.</param> /// <returns>The formatted output.</returns> public string GetFormattedOutput(int maxLines = 50, string outputDescription = "Command output") { return FormatOutput(ToArray(), TotalLineCount, maxLines, outputDescription); } /// <summary> /// Formats retained output lines for display. /// </summary> /// <param name="output">The retained output lines.</param> /// <param name="totalLineCount">The total number of lines observed.</param> /// <param name="maxLines">The maximum number of lines to include.</param> /// <param name="outputDescription">The label used when the output is truncated.</param> /// <returns>The formatted output.</returns> internal static string FormatOutput(IReadOnlyList<string> output, int totalLineCount, int maxLines = 50, string outputDescription = "Command output") { ArgumentOutOfRangeException.ThrowIfNegativeOrZero(maxLines); if (output.Count == 0) { return string.Empty; } var linesShown = Math.Min(maxLines, output.Count); IEnumerable<string> lines = output.Skip(output.Count - linesShown); var formattedOutput = string.Join(Environment.NewLine, lines); if (totalLineCount > linesShown) { return $"{outputDescription} truncated: showing last {linesShown} of {totalLineCount} lines.{Environment.NewLine}{formattedOutput}"; } return formattedOutput; } }