Skip to content

Commit

Permalink
SIANXSVC-1206: dotnet-e5e writes logging messages after output in non…
Browse files Browse the repository at this point in the history
…-keepalive mode (#19)
  • Loading branch information
nachtjasmin authored Feb 2, 2024
1 parent 25b1009 commit a63c6e9
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 7 deletions.
39 changes: 39 additions & 0 deletions src/Anexia.E5E.Tests/Integration/NonKeepAliveModeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Threading.Tasks;

using Anexia.E5E.Functions;
using Anexia.E5E.Tests.Helpers;
using Anexia.E5E.Tests.TestHelpers;

using Xunit;
using Xunit.Abstractions;

namespace Anexia.E5E.Tests.Integration;

public class NonKeepAliveModeTests : IntegrationTestBase
{
public NonKeepAliveModeTests(ITestOutputHelper outputHelper) : base(outputHelper)
{
Host.ConfigureEndpoints(cfg =>
{
cfg.RegisterEntrypoint(TestE5ERuntimeOptions.DefaultEntrypointName,
_ => Task.FromResult(E5EResponse.From("response")));
}, new[] { TestE5ERuntimeOptions.DefaultEntrypointName, "+++", "0", "---" });
}

public override Task InitializeAsync() => Host.StartAsync();


[Fact]
public async Task ShouldEndWithJsonResponse()
{
var (stdout, _) = await Host.WriteOnceAsync(builder => builder.WithData("request"));
Assert.EndsWith(@"{""result"":{""data"":""response"",""type"":""text""}}", stdout);
}

[Fact]
public async Task ShouldNotWriteExecutionEndToStderr()
{
var (_, stderr) = await Host.WriteOnceAsync(builder => builder.WithData("request"));
Assert.Empty(stderr);
}
}
2 changes: 1 addition & 1 deletion src/Anexia.E5E.Tests/TestHelpers/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ protected IntegrationTestBase(ITestOutputHelper outputHelper) : base(outputHelpe

protected TestHostBuilder Host { get; }

public Task InitializeAsync()
public virtual Task InitializeAsync()
{
return Task.CompletedTask;
}
Expand Down
13 changes: 13 additions & 0 deletions src/Anexia.E5E/Extensions/HostBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ public static IHostBuilder UseAnexiaE5E(this IHostBuilder hb, string[]? args,
private sealed class E5EHostBuilderWrapper : IHostBuilder
{
private readonly IHostBuilder _inner;
private readonly E5ERuntimeOptions _options;

public E5EHostBuilderWrapper(IHostBuilder inner, E5ERuntimeOptions runtimeOptions)
{
_inner = inner;
_options = runtimeOptions;
_inner.ConfigureServices((_, services) =>
{
services.AddHostedService<E5ECommunicationService>();
Expand Down Expand Up @@ -110,6 +112,17 @@ public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollect

public IHost Build()
{
if (_options.KeepAlive)
return new E5EHostWrapper(_inner.Build());

// If we are in the non-keepalive mode (should happen rarely), we *must* not write anything to stdout
// after a successful response. Because the default logging mechanism do not allow to set the logging
// level after startup, we enforce that all logs are written to stderr.
//
// This does **not** affect our ConsoleAbstraction. Why? The ConsoleLogger just references System.Console.Out,
// and our abstraction calls System.Console.OpenStandardOutput and is therefore using the actual /dev/stdout.
Console.SetOut(Console.Error);

return new E5EHostWrapper(_inner.Build());
}

Expand Down
14 changes: 8 additions & 6 deletions src/Anexia.E5E/Hosting/E5ECommunicationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,15 @@ private async Task ListenForIncomingMessagesAsync(CancellationToken stoppingToke
var response = await RespondToLineAsync(line, stoppingToken).ConfigureAwait(false);
await _console.WriteToStdoutAsync(response).ConfigureAwait(false);

// After every *successful* execution, we need to write the execution sequence. This applies to ping
// messages as well. We do not write it after errors, otherwise our logs would get sent into the void.
await _console.WriteToStdoutAsync(_options.DaemonExecutionTerminationSequence).ConfigureAwait(false);
await _console.WriteToStderrAsync(_options.DaemonExecutionTerminationSequence).ConfigureAwait(false);

// If we should keep it alive (which is notably the default), do that.
if (_options.KeepAlive) continue;
if (_options.KeepAlive)
{
// After every *successful* execution, we need to write the execution sequence. This applies to ping
// messages as well. We do not write it after errors, otherwise our logs would get sent into the void.
await _console.WriteToStdoutAsync(_options.DaemonExecutionTerminationSequence).ConfigureAwait(false);
await _console.WriteToStderrAsync(_options.DaemonExecutionTerminationSequence).ConfigureAwait(false);
continue;
}

// Otherwise, we just stop the processing at this point.
break;
Expand Down

0 comments on commit a63c6e9

Please sign in to comment.