* Avoid null endpoint DTO metadata in tests * Enforce console logs hub read permission * Remove unused console logs hub import * Support mapped endpoint metadata in auth tests * Reduce console log capture throughput impact * Address Copilot console logs review * Refactor task scheduling to support tenant-level background work and enhance logging functionality. * Introduce ConsoleStreamHook for stdout/stderr tee and enhance logging validation. Adjust test cases and startup warnings for distributed lock provider usage. * Refactor console logging pipeline with capture optimization and new ConsoleLogsHost; update tests accordingly. * Add Ansi SGR parser for console logs and associated unit tests * Remove ANSI color renderings and parsers; integrate ConsoleLogScopeAccessor for improved logging context with workflow instance ID support. * Address console logs code quality feedback * Address PR review feedback * Preserve console logs extension points * Stabilize console logs host lifecycle * Address final automated review comments * Tighten console log capture shutdown * Address console log review feedback * Address follow-up review feedback * Cover final review feedback * Avoid recursive console provider initialization * Guard console host lease shutdown * Preserve console log scope and provider lifetime * Correlate console log scope fallback * Tighten console scope correlation * Expose host services during provider construction * Redact ANSI-normalized console lines * Add OpenTelemetry diagnostics backend foundation * Add OTLP HTTP ingestion parsing * Document OpenTelemetry diagnostics setup * Enforce OpenTelemetry hub permissions * Remove `ConsoleCaptureTee` and related services and tests * Add OpenTelemetry HTTP ingestion integration test * Use pipeline contributors for console log context * Update CShells package versions to 0.0.24-preview.132 * Add OpenTelemetry ingestion security tests * Add OpenTelemetry API authorization tests * Filter live console logs by workflow instance * Add OpenTelemetry hub tests * Add OpenTelemetry gRPC metadata hook * Assert OpenTelemetry workflow tags survive ingestion * Mark OpenTelemetry core build verified * Enhance console logging with activity execution metadata and extend test coverage. * Address console logs stream consumption comment * Wire OpenTelemetry diagnostics into core sample * Address Core diagnostics review feedback * Address Core Copilot follow-up feedback * Add OpenTelemetry metric instrument names * Address Core Copilot provider feedback * Address Core Copilot diagnostics follow-up * Address Core Copilot live feed feedback * Address Core Copilot store feedback * Integrate OpenTelemetry for logging, tracing, and metrics in ModularServer and update launch settings and docker-compose configuration. * Refactor to replace `ConsoleLogStream.Core` with `ConsoleLogStreaming.Core` across codebase and update `ConsoleStreamHook` installation. * Add diagnostics OpenTelemetry backend * Fix OpenTelemetry live hub subscription * Fix modular OpenTelemetry exporter endpoints * Add CShells logging configuration in appsettings.json * Remove obsolete unit tests and helper classes * Restore default activity exception handling * Simplify type serialization and alias management This commit refactors the internal type serialization and alias management system to reduce boilerplate, improve robustness, and simplify the developer experience: - Removed numerous explicit `ExpressionOptions` type alias registrations across various modules. - Updated `TypeJsonConverter` and polymorphic serialization to reliably handle types using assembly-qualified names when a short alias is not explicitly registered. - Streamlined `ExcludeFromHashConverter` to strictly adhere to `ExcludeFromHashAttribute` for hash calculations, removing complex `JsonIgnoreCondition` logic. - Eliminated several helper classes (`WorkflowJsonTypeResolver`, `WorkflowTypeValidator`, `IWorkflowTypeRegistry`, `WorkflowFactoryDictionary`, `JavaScriptExceptionTypeAliasRegistrar`, `WorkflowRuntimeTypeAliasRegistrar`) and their associated unit tests, simplifying the codebase. Additionally, this commit introduces a comprehensive markdown document (`product-website-feature-source.md`) outlining Elsa's core features, Studio capabilities, extension ecosystem, and architectural selling points, intended as source material for the product website. * Refine type serialization for improved robustness and alias handling This commit further enhances the type serialization and deserialization mechanisms: * Centralizes type resolution and alias management through `IWellKnownTypeRegistry` and `WorkflowJsonTypeResolver`. * Prioritizes registered type aliases when serializing type metadata in `PolymorphicObjectConverter`, resulting in more concise JSON output. * Enhances deserialization in `PolymorphicObjectConverter` and `VariableMapper` to gracefully handle unknown or non-instantiable types, providing fallbacks and logging warnings. * Simplifies `TypeJsonConverter` by delegating complex type resolution logic to the `WorkflowJsonTypeResolver`. * Adds `JsonArray` to the well-known type aliases for direct recognition. * Fix console logs packaging and workflow type resolution * Fix console log metadata and type resolution * Address Copilot review feedback * Enhance type resolution, improve console log handling, and update tests - Streamlined `WorkflowDictionaryExtensions` for better workflow registration validation. - Refined `ConsoleLogsAuthorizationTests` with the new `SetJsonRequest` helper to improve test requests handling. - Updated `OrderDefinition` to ignore JSON serialization for `KeySelector`. - Enhanced `WorkflowRuntimeFeature` for improved workflow registration and type alias configuration. - Added tests to ensure `ConsoleLogProvider` metadata filtration in various scenarios. - Improved type serialization logic in `WorkflowJsonTypeResolver`. - Updated README to fix references related to diagnostics. - Optimized `ExcludeFromHashConverter` for property serialization conditions. - Modified `TriggerIndexer` for streamlined trigger management. - Tested payload checks in `PublishEventTests`. - Adjusted `Endpoint` in `ConsoleLogs` for automatic JSON request handling. - Ensured registration of workflow type aliases in `WorkflowsFeature`. * Restore CLR workflow registration compatibility * Align JSON island serialization fixtures * Add Console Logs Services and Enhance Endpoint Handling - Introduced `ActivityExecutionsEndpointTests` to validate route exposure. - Added `ConsoleLogCaptureHostedService` for console log streaming. - Implemented `ConsoleStreamJsonConverter` for JSON conversion of console streams. - Developed `ElsaConsoleLogRecentBuffer` to handle recent log buffering. - Updated `ConsoleLogsAuthorizationTests` with new test cases for stream filter mapping. - Consolidated console log provider dependencies and registration, including recent buffering. - Enhanced `ElsaConsoleLogProvider` to use recent buffer for filtering. - Adjusted `Program.cs` for streamlined logging service setup. * Enhance type resolution and test coverage; streamline console log integration - Added `ConsoleStreamHook` for streamlined log streaming. - Updated `WorkflowJsonTypeResolverTests` to improve type resolution and test new scenarios. - Simplified type resolution by removing trusted assembly checks. * Fix CI smoke and package restore failures * Fix Docker smoke image project paths * Fix Docker Python runtime packages * Fix Docker CA smoke teardown * Refresh Elsa roadmap * Implement background processors and mediation coordination - Added `BackgroundCommandProcessor`, `BackgroundJobProcessor`, and `BackgroundNotificationProcessor` classes for handling commands, jobs, and notifications, respectively. - Introduced `MediatorBackgroundProcessingCoordinator` to coordinate the execution of all background processors. - Implemented `MediatorBackgroundTask` for wrapping `MediatorBackgroundProcessingCoordinator` in `BackgroundTask`. - Added unit tests for `MediatorBackgroundTask` to ensure proper start and stop behavior. - Refactored `BackgroundCommandSenderHostedService` to utilize `BackgroundCommandProcessor`. - Introduced 'elsa-roadmap-refresh' skill configuration for roadmap updates. * Address workflow type resolution review feedback * Address follow-up review feedback * Restore recent console logs execute path * Address Copilot follow-up review * Decouple workflow JSON aliases from expressions * Fix workflow management unit test setup * Fix console logs recent endpoint handler shape * Respect workflow JSON strict type aliases * Remove unused console log contracts reference * Address Copilot review feedback * Address Copilot follow-up comments * Synchronize ring buffer dropped count * Address background processor strategy replay
419 lines
17 KiB
C#
419 lines
17 KiB
C#
using ConsoleLogStreaming.Core;
|
|
using ConsoleLogStreaming.Core.Capture;
|
|
using ConsoleLogStreaming.Core.Models;
|
|
using CShells.Lifecycle;
|
|
using Elsa.Diagnostics.ConsoleLogs.Contracts;
|
|
using Elsa.Diagnostics.ConsoleLogs.Extensions;
|
|
using Elsa.Diagnostics.ConsoleLogs.RealTime;
|
|
using Elsa.Diagnostics.ConsoleLogs.Services;
|
|
using Elsa.Workflows;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Elsa.Diagnostics.ConsoleLogs.UnitTests;
|
|
|
|
[Collection(ConsoleHostStateCollection.Name)]
|
|
public class ConsoleLogsRegistrationTests : IAsyncLifetime
|
|
{
|
|
public async Task InitializeAsync()
|
|
{
|
|
await ConsoleLogStreamingHost.ShutdownAsync();
|
|
ConsoleStreamHook.Uninstall();
|
|
}
|
|
|
|
public async Task DisposeAsync()
|
|
{
|
|
await ConsoleLogStreamingHost.ShutdownAsync();
|
|
ConsoleStreamHook.Uninstall();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsServices_RegistersConsoleLogPipeline()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
services.AddConsoleLogsServices();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
Assert.NotNull(serviceProvider.GetRequiredService<IConsoleLogProvider>());
|
|
Assert.NotNull(serviceProvider.GetRequiredService<IConsoleLogCapture>());
|
|
Assert.NotNull(serviceProvider.GetRequiredService<IElsaConsoleLogHubAuthorizer>());
|
|
Assert.NotNull(serviceProvider.GetRequiredService<ElsaConsoleLogSubscriptionManager>());
|
|
Assert.Same(serviceProvider.GetRequiredService<ConsoleLogContextAccessor>(), serviceProvider.GetRequiredService<IConsoleLogContextAccessor>());
|
|
Assert.Same(serviceProvider.GetRequiredService<ConsoleLogContextAccessor>(), serviceProvider.GetRequiredService<IConsoleLogMetadataAccessor>());
|
|
Assert.Contains(serviceProvider.GetServices<IShellInitializer>(), x => x.GetType() == typeof(ConsoleLogCaptureShellInitializer));
|
|
Assert.Contains(serviceProvider.GetServices<IDrainHandler>(), x => x.GetType() == typeof(ConsoleLogCaptureShellDrainHandler));
|
|
AssertConsoleLogPipelineContributors(serviceProvider);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsServices_ShellInitializerStartsCapture()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
services.AddConsoleLogsServices();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
var initializer = serviceProvider.GetServices<IShellInitializer>().OfType<ConsoleLogCaptureShellInitializer>().Single();
|
|
|
|
await initializer.InitializeAsync(CancellationToken.None);
|
|
|
|
try
|
|
{
|
|
var line = $"console-shell-capture-{Guid.NewGuid():N}";
|
|
Console.WriteLine(line);
|
|
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
await AssertEventuallyAsync(async () =>
|
|
{
|
|
var result = await provider.GetRecentAsync(new()
|
|
{
|
|
Query = line,
|
|
Limit = 10
|
|
});
|
|
|
|
Assert.Contains(result.Items, x => x.Text.Contains(line, StringComparison.Ordinal));
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
var drainHandler = serviceProvider.GetServices<IDrainHandler>().OfType<ConsoleLogCaptureShellDrainHandler>().Single();
|
|
await drainHandler.DrainAsync(new NoopDrainExtensionHandle(), CancellationToken.None);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsHost_RegistersHostedServicesAndCaptureDependencies()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
services.AddConsoleLogsHost();
|
|
|
|
await using var serviceProvider = services.BuildServiceProvider();
|
|
Assert.NotNull(serviceProvider.GetRequiredService<IConsoleLogCapture>());
|
|
Assert.NotNull(serviceProvider.GetRequiredService<IConsoleLogProvider>());
|
|
Assert.Same(serviceProvider.GetRequiredService<ConsoleLogContextAccessor>(), serviceProvider.GetRequiredService<IConsoleLogContextAccessor>());
|
|
Assert.Same(serviceProvider.GetRequiredService<ConsoleLogContextAccessor>(), serviceProvider.GetRequiredService<IConsoleLogMetadataAccessor>());
|
|
Assert.Contains(serviceProvider.GetServices<IHostedService>(), x => x.GetType().Name == "ConsoleLogCaptureHostedService");
|
|
AssertConsoleLogPipelineContributors(serviceProvider);
|
|
}
|
|
|
|
[Fact]
|
|
public void AddConsoleLogsHost_AppliesConfiguration()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddLogging();
|
|
services.AddConsoleLogsHost(options => options.RecentCapacity = 17);
|
|
|
|
using var serviceProvider = services.BuildServiceProvider();
|
|
|
|
Assert.Equal(17, serviceProvider.GetRequiredService<IOptions<ConsoleLogStreaming.Core.Options.ConsoleLogOptions>>().Value.RecentCapacity);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DecoratedProvider_AttachesAmbientElsaMetadata()
|
|
{
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddConsoleLogsServices(options => options.SourceId = "test-source")
|
|
.BuildServiceProvider();
|
|
var contextAccessor = serviceProvider.GetRequiredService<IConsoleLogContextAccessor>();
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
using (contextAccessor.PushWorkflowInstanceId("workflow-console"))
|
|
await provider.PublishAsync(new ConsoleLogLine { Text = "message", Source = new ConsoleLogSource { Id = "test-source" } });
|
|
|
|
var result = await provider.GetRecentAsync(new ConsoleLogFilter
|
|
{
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = "workflow-console"
|
|
}
|
|
});
|
|
|
|
var line = Assert.Single(result.Items);
|
|
Assert.Equal("workflow-console", line.Metadata[ConsoleLogMetadataKeys.WorkflowInstanceId]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ConsoleCapture_AttachesAmbientElsaMetadataAtWriteTime()
|
|
{
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddConsoleLogsServices(options => options.SourceId = "test-source")
|
|
.BuildServiceProvider();
|
|
var contextAccessor = serviceProvider.GetRequiredService<IConsoleLogContextAccessor>();
|
|
var initializer = serviceProvider.GetServices<IShellInitializer>().OfType<ConsoleLogCaptureShellInitializer>().Single();
|
|
|
|
await initializer.InitializeAsync(CancellationToken.None);
|
|
|
|
try
|
|
{
|
|
var line = $"console-workflow-capture-{Guid.NewGuid():N}";
|
|
using (contextAccessor.PushWorkflowInstanceId("workflow-captured"))
|
|
Console.WriteLine(line);
|
|
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
await AssertEventuallyAsync(async () =>
|
|
{
|
|
var result = await provider.GetRecentAsync(new()
|
|
{
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = "workflow-captured"
|
|
},
|
|
Query = line,
|
|
Limit = 10
|
|
});
|
|
|
|
var capturedLine = Assert.Single(result.Items);
|
|
Assert.Equal("workflow-captured", capturedLine.Metadata[ConsoleLogMetadataKeys.WorkflowInstanceId]);
|
|
});
|
|
}
|
|
finally
|
|
{
|
|
var drainHandler = serviceProvider.GetServices<IDrainHandler>().OfType<ConsoleLogCaptureShellDrainHandler>().Single();
|
|
await drainHandler.DrainAsync(new NoopDrainExtensionHandle(), CancellationToken.None);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task AddConsoleLogsServices_DecoratesExistingProvider()
|
|
{
|
|
var innerProvider = new RecordingConsoleLogProvider();
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddSingleton<IConsoleLogProvider>(innerProvider)
|
|
.AddConsoleLogsServices()
|
|
.BuildServiceProvider();
|
|
var contextAccessor = serviceProvider.GetRequiredService<IConsoleLogContextAccessor>();
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
using (contextAccessor.PushWorkflowInstanceId("workflow-console"))
|
|
await provider.PublishAsync(new ConsoleLogLine { Text = "message", Source = new ConsoleLogSource { Id = "test-source" } });
|
|
|
|
Assert.NotNull(innerProvider.PublishedLine);
|
|
Assert.Equal("workflow-console", innerProvider.PublishedLine.Metadata[ConsoleLogMetadataKeys.WorkflowInstanceId]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DecoratedProvider_FiltersRecentRowsByMetadataWhenInnerProviderDoesNot()
|
|
{
|
|
var innerProvider = new MetadataIgnoringConsoleLogProvider();
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddSingleton<IConsoleLogProvider>(innerProvider)
|
|
.AddConsoleLogsServices()
|
|
.BuildServiceProvider();
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
await provider.PublishAsync(CreateLine("workflow-a", "a"));
|
|
await provider.PublishAsync(CreateLine("workflow-b", "b"));
|
|
|
|
var result = await provider.GetRecentAsync(new()
|
|
{
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = "workflow-b"
|
|
}
|
|
});
|
|
|
|
var line = Assert.Single(result.Items);
|
|
Assert.Equal("b", line.Text);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DecoratedProvider_FiltersRecentRowsByBufferedMetadataWhenInnerProviderDropsMetadata()
|
|
{
|
|
var innerProvider = new MetadataDroppingConsoleLogProvider();
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddSingleton<IConsoleLogProvider>(innerProvider)
|
|
.AddConsoleLogsServices()
|
|
.BuildServiceProvider();
|
|
var contextAccessor = serviceProvider.GetRequiredService<IConsoleLogContextAccessor>();
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
using (contextAccessor.PushWorkflowInstanceId("workflow-buffered"))
|
|
await provider.PublishAsync(new ConsoleLogLine { Text = "buffered", Source = new ConsoleLogSource { Id = "test-source" } });
|
|
|
|
Assert.Empty(Assert.Single(innerProvider.PublishedLines).Metadata);
|
|
|
|
var result = await provider.GetRecentAsync(new()
|
|
{
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = "workflow-buffered"
|
|
}
|
|
});
|
|
|
|
var line = Assert.Single(result.Items);
|
|
Assert.Equal("buffered", line.Text);
|
|
Assert.Equal("workflow-buffered", line.Metadata[ConsoleLogMetadataKeys.WorkflowInstanceId]);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DecoratedProvider_FiltersLiveRowsByMetadataWhenInnerProviderDoesNot()
|
|
{
|
|
var innerProvider = new MetadataIgnoringConsoleLogProvider();
|
|
innerProvider.LiveItems.Add(CreateLine("workflow-a", "a"));
|
|
innerProvider.LiveItems.Add(CreateLine("workflow-b", "b"));
|
|
|
|
await using var serviceProvider = new ServiceCollection()
|
|
.AddLogging()
|
|
.AddSingleton<IConsoleLogProvider>(innerProvider)
|
|
.AddConsoleLogsServices()
|
|
.BuildServiceProvider();
|
|
var provider = serviceProvider.GetRequiredService<IConsoleLogProvider>();
|
|
|
|
var lines = await provider.SubscribeAsync(new()
|
|
{
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = "workflow-b"
|
|
}
|
|
}).Where(x => x.Line != null).Select(x => x.Line!).ToListAsync();
|
|
|
|
var line = Assert.Single(lines);
|
|
Assert.Equal("b", line.Text);
|
|
}
|
|
|
|
private static void AssertConsoleLogPipelineContributors(IServiceProvider serviceProvider)
|
|
{
|
|
Assert.Contains(serviceProvider.GetServices<IWorkflowExecutionPipelineContributor>(), x => x.GetType() == typeof(ConsoleLogWorkflowExecutionPipelineContributor));
|
|
Assert.Contains(serviceProvider.GetServices<IActivityExecutionPipelineContributor>(), x => x.GetType() == typeof(ConsoleLogActivityExecutionPipelineContributor));
|
|
}
|
|
|
|
private static async Task AssertEventuallyAsync(Func<Task> assertion)
|
|
{
|
|
var deadline = DateTimeOffset.UtcNow.AddSeconds(3);
|
|
Exception? lastException = null;
|
|
|
|
while (DateTimeOffset.UtcNow < deadline)
|
|
{
|
|
try
|
|
{
|
|
await assertion();
|
|
return;
|
|
}
|
|
catch (Xunit.Sdk.XunitException e)
|
|
{
|
|
lastException = e;
|
|
await Task.Delay(25);
|
|
}
|
|
}
|
|
|
|
if (lastException != null)
|
|
throw lastException;
|
|
}
|
|
|
|
private static ConsoleLogLine CreateLine(string workflowInstanceId, string text) => new()
|
|
{
|
|
Text = text,
|
|
Source = new ConsoleLogSource { Id = "test-source" },
|
|
Metadata = new Dictionary<string, string>
|
|
{
|
|
[ConsoleLogMetadataKeys.WorkflowInstanceId] = workflowInstanceId
|
|
}
|
|
};
|
|
|
|
private sealed class RecordingConsoleLogProvider : IConsoleLogProvider
|
|
{
|
|
public ConsoleLogLine? PublishedLine { get; private set; }
|
|
|
|
public ValueTask PublishAsync(ConsoleLogLine line, CancellationToken cancellationToken = default)
|
|
{
|
|
PublishedLine = line;
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public ValueTask<RecentConsoleLogsResult> GetRecentAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult(new RecentConsoleLogsResult());
|
|
}
|
|
|
|
public IAsyncEnumerable<ConsoleLogStreamingItem> SubscribeAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
return AsyncEnumerable.Empty<ConsoleLogStreamingItem>();
|
|
}
|
|
|
|
public ValueTask<IReadOnlyCollection<ConsoleLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult<IReadOnlyCollection<ConsoleLogSource>>([]);
|
|
}
|
|
}
|
|
|
|
private sealed class MetadataIgnoringConsoleLogProvider : IConsoleLogProvider
|
|
{
|
|
public List<ConsoleLogLine> RecentItems { get; } = [];
|
|
public List<ConsoleLogLine> LiveItems { get; } = [];
|
|
|
|
public ValueTask PublishAsync(ConsoleLogLine line, CancellationToken cancellationToken = default)
|
|
{
|
|
RecentItems.Add(line);
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public ValueTask<RecentConsoleLogsResult> GetRecentAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult(new RecentConsoleLogsResult { Items = RecentItems });
|
|
}
|
|
|
|
public async IAsyncEnumerable<ConsoleLogStreamingItem> SubscribeAsync(
|
|
ConsoleLogFilter filter,
|
|
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
|
{
|
|
foreach (var line in LiveItems)
|
|
{
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
yield return ConsoleLogStreamingItem.FromLine(line);
|
|
await Task.Yield();
|
|
}
|
|
}
|
|
|
|
public ValueTask<IReadOnlyCollection<ConsoleLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult<IReadOnlyCollection<ConsoleLogSource>>([]);
|
|
}
|
|
}
|
|
|
|
private sealed class MetadataDroppingConsoleLogProvider : IConsoleLogProvider
|
|
{
|
|
public List<ConsoleLogLine> PublishedLines { get; } = [];
|
|
|
|
public ValueTask PublishAsync(ConsoleLogLine line, CancellationToken cancellationToken = default)
|
|
{
|
|
PublishedLines.Add(line with { Metadata = new Dictionary<string, string>() });
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
|
|
public ValueTask<RecentConsoleLogsResult> GetRecentAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult(new RecentConsoleLogsResult { Items = PublishedLines });
|
|
}
|
|
|
|
public IAsyncEnumerable<ConsoleLogStreamingItem> SubscribeAsync(ConsoleLogFilter filter, CancellationToken cancellationToken = default)
|
|
{
|
|
return AsyncEnumerable.Empty<ConsoleLogStreamingItem>();
|
|
}
|
|
|
|
public ValueTask<IReadOnlyCollection<ConsoleLogSource>> ListSourcesAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
return ValueTask.FromResult<IReadOnlyCollection<ConsoleLogSource>>([]);
|
|
}
|
|
}
|
|
|
|
private sealed class NoopDrainExtensionHandle : IDrainExtensionHandle
|
|
{
|
|
public bool TryExtend(TimeSpan requestedExtension, out TimeSpan grantedExtension)
|
|
{
|
|
grantedExtension = TimeSpan.Zero;
|
|
return false;
|
|
}
|
|
}
|
|
}
|