Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add DebugLogLevelEnabled property to default logger implementation #928

Merged
merged 7 commits into from
Jun 21, 2023
6 changes: 0 additions & 6 deletions src/Testcontainers/Builders/ContainerBuilder`3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,6 @@ public TBuilderEntity WithTmpfsMount(string destination, AccessMode accessMode)
return WithMount(new TmpfsMount(destination, accessMode));
}

/// <inheritdoc />
public TBuilderEntity WithNetwork(string id, string name)
{
return WithNetwork(name);
}

/// <inheritdoc />
public TBuilderEntity WithNetwork(string name)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Testcontainers/Clients/DockerContainerOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,13 @@ public Task RemoveAsync(string id, CancellationToken ct = default)

public Task ExtractArchiveToContainerAsync(string id, string path, Stream tarStream, CancellationToken ct = default)
{
_logger.ExtractArchiveToDockerContainer(id, path);
_logger.CopyArchiveToDockerContainer(id, path);
return Docker.Containers.ExtractArchiveToContainerAsync(id, new ContainerPathStatParameters { Path = path, AllowOverwriteDirWithFile = false }, tarStream, ct);
}

public async Task<Stream> GetArchiveFromContainerAsync(string id, string path, CancellationToken ct = default)
{
_logger.GetArchiveFromDockerContainer(id, path);
_logger.ReadArchiveFromDockerContainer(id, path);

var tarResponse = await Docker.Containers.GetArchiveFromContainerAsync(id, new GetArchiveFromContainerParameters { Path = path }, false, ct)
.ConfigureAwait(false);
Expand Down
6 changes: 3 additions & 3 deletions src/Testcontainers/Clients/TraceProgress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ public void Report(JSONMessage value)

if (!string.IsNullOrWhiteSpace(value.Status))
{
_logger.LogTrace(value.Status);
_logger.LogDebug(value.Status);
}

if (!string.IsNullOrWhiteSpace(value.Stream))
{
_logger.LogTrace(value.Stream);
_logger.LogDebug(value.Stream);
}

if (!string.IsNullOrWhiteSpace(value.ProgressMessage))
{
_logger.LogTrace(value.ProgressMessage);
_logger.LogDebug(value.ProgressMessage);
}

if (!string.IsNullOrWhiteSpace(value.ErrorMessage))
Expand Down
13 changes: 9 additions & 4 deletions src/Testcontainers/Configurations/TestcontainersSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace DotNet.Testcontainers.Configurations
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
Expand Down Expand Up @@ -167,7 +168,7 @@ static TestcontainersSettings()
/// </summary>
[NotNull]
public static ILogger Logger { get; set; }
= new Logger();
= ConsoleLogger.Instance;

/// <summary>
/// Gets or sets the host operating system.
Expand All @@ -184,12 +185,16 @@ public static WaitHandle SettingsInitialized
=> ManualResetEvent.WaitHandle;

/// <inheritdoc cref="PortForwardingContainer.ExposeHostPortsAsync" />
public static async Task ExposeHostPortsAsync(params ushort[] ports)
public static Task ExposeHostPortsAsync(ushort port, CancellationToken ct = default)
=> ExposeHostPortsAsync(new[] { port }, ct);

/// <inheritdoc cref="PortForwardingContainer.ExposeHostPortsAsync" />
public static async Task ExposeHostPortsAsync(IEnumerable<ushort> ports, CancellationToken ct = default)
{
await PortForwardingContainer.Instance.StartAsync()
await PortForwardingContainer.Instance.StartAsync(ct)
.ConfigureAwait(false);

await PortForwardingContainer.Instance.ExposeHostPortsAsync(ports)
await PortForwardingContainer.Instance.ExposeHostPortsAsync(ports, ct)
.ConfigureAwait(false);
}
}
Expand Down
4 changes: 4 additions & 0 deletions src/Testcontainers/Containers/DockerContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,16 @@ await WaitStrategy.WaitUntilAsync(CheckPortBindingsAsync, TimeSpan.FromSeconds(1
await _configuration.StartupCallback(this, ct)
.ConfigureAwait(false);

Logger.StartReadinessCheck(_container.ID);

foreach (var waitStrategy in _configuration.WaitStrategies)
{
await WaitStrategy.WaitUntilAsync(() => CheckWaitStrategyAsync(waitStrategy), TimeSpan.FromSeconds(1), Timeout.InfiniteTimeSpan, ct)
.ConfigureAwait(false);
}

Logger.CompleteReadinessCheck(_container.ID);

Started?.Invoke(this, EventArgs.Empty);
}

Expand Down
5 changes: 4 additions & 1 deletion src/Testcontainers/Containers/PortForwarding.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
namespace DotNet.Testcontainers.Containers
{
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Docker.DotNet.Models;
using DotNet.Testcontainers.Builders;
Expand Down Expand Up @@ -41,8 +43,9 @@ private PortForwardingContainer(PortForwardingConfiguration configuration, ILogg
/// Exposes the host ports using SSH port forwarding.
/// </summary>
/// <param name="ports">The host ports to forward.</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>A task that completes when the host ports are forwarded.</returns>
public Task ExposeHostPortsAsync(params ushort[] ports)
public Task ExposeHostPortsAsync(IEnumerable<ushort> ports, CancellationToken ct = default)
{
var sshClient = new SshClient(Hostname, GetMappedPublicPort(PortForwardingBuilder.SshdPort), _configuration.Username, _configuration.Password);
sshClient.Connect();
Expand Down
62 changes: 36 additions & 26 deletions src/Testcontainers/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ namespace DotNet.Testcontainers
{
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;

/// <summary>
Expand Down Expand Up @@ -55,54 +54,65 @@ namespace DotNet.Testcontainers
/// }
/// </code>
/// </example>
internal sealed class Logger : ILogger, IDisposable
[PublicAPI]
public sealed class ConsoleLogger : ILogger, IDisposable
{
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();

public Logger()
private LogLevel _minLogLevel = LogLevel.Information;

private ConsoleLogger()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !Console.IsOutputRedirected && !Console.IsErrorRedirected)
{
Console.BufferWidth = short.MaxValue - 1;
}
}

/// <summary>
/// Gets the <see cref="ConsoleLogger" /> instance.
/// </summary>
public static ConsoleLogger Instance { get; }
= new ConsoleLogger();

/// <summary>
/// Gets a value indicating whether the debug log level is enabled or not.
/// </summary>
public bool DebugLogLevelEnabled
{
get
{
return LogLevel.Debug.Equals(_minLogLevel);
}

set
{
_minLogLevel = value ? LogLevel.Debug : LogLevel.Information;
}
}

/// <inheritdoc />
public void Dispose()
{
// The default logger does not support scopes. We return itself as IDisposable implementation.
// The default console logger does not support scopes. We return itself as IDisposable implementation.
}

/// <inheritdoc />
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
TextWriter console;

switch (logLevel)
if (IsEnabled(logLevel))
{
case LogLevel.Information:
console = Console.Out;
break;
case LogLevel.Warning:
console = Console.Out;
break;
case LogLevel.Error:
console = Console.Error;
break;
case LogLevel.Critical:
console = Console.Error;
break;
default:
return;
Console.Out.WriteLine("[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception));
}

var message = string.Format(CultureInfo.CurrentCulture, "[testcontainers.org {0:hh\\:mm\\:ss\\.ff}] {1}", _stopwatch.Elapsed, formatter.Invoke(state, exception));
console.WriteLine(message);
}

/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel)
{
return true;
return logLevel >= _minLogLevel;
}

/// <inheritdoc />
public IDisposable BeginScope<TState>(TState state)
{
return this;
Expand Down
53 changes: 37 additions & 16 deletions src/Testcontainers/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ private static readonly Action<ILogger, string, Exception> _StopDockerContainer
private static readonly Action<ILogger, string, Exception> _DeleteDockerContainer
= LoggerMessage.Define<string>(LogLevel.Information, default, "Delete Docker container {Id}");

private static readonly Action<ILogger, string, string, Exception> _ExtractArchiveToDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Copy tar archive to \"{Path}\" at Docker container {Id}");
private static readonly Action<ILogger, string, Exception> _StartReadinessCheck
= LoggerMessage.Define<string>(LogLevel.Information, default, "Wait for Docker container {Id} to complete readiness checks");

private static readonly Action<ILogger, string, string, Exception> _GetArchiveFromDockerContainer
private static readonly Action<ILogger, string, Exception> _CompleteReadinessCheck
= LoggerMessage.Define<string>(LogLevel.Information, default, "Docker container {Id} ready");

private static readonly Action<ILogger, string, string, Exception> _CopyArchiveToDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Copy tar archive to \"{Path}\" to Docker container {Id}");

private static readonly Action<ILogger, string, string, Exception> _ReadArchiveFromDockerContainer
= LoggerMessage.Define<string, string>(LogLevel.Information, default, "Read \"{Path}\" from Docker container {Id}");

private static readonly Action<ILogger, Type, string, Exception> _AttachToDockerContainer
Expand Down Expand Up @@ -91,47 +97,57 @@ public static void IgnorePatternAdded(this ILogger logger, Regex ignorePattern)

public static void DockerContainerCreated(this ILogger logger, string id)
{
_DockerContainerCreated(logger, id, null);
_DockerContainerCreated(logger, TruncId(id), null);
}

public static void StartDockerContainer(this ILogger logger, string id)
{
_StartDockerContainer(logger, id, null);
_StartDockerContainer(logger, TruncId(id), null);
}

public static void StopDockerContainer(this ILogger logger, string id)
{
_StopDockerContainer(logger, id, null);
_StopDockerContainer(logger, TruncId(id), null);
}

public static void DeleteDockerContainer(this ILogger logger, string id)
{
_DeleteDockerContainer(logger, id, null);
_DeleteDockerContainer(logger, TruncId(id), null);
}

public static void StartReadinessCheck(this ILogger logger, string id)
{
_StartReadinessCheck(logger, TruncId(id), null);
}

public static void ExtractArchiveToDockerContainer(this ILogger logger, string id, string path)
public static void CompleteReadinessCheck(this ILogger logger, string id)
{
_ExtractArchiveToDockerContainer(logger, path, id, null);
_CompleteReadinessCheck(logger, TruncId(id), null);
}

public static void GetArchiveFromDockerContainer(this ILogger logger, string id, string path)
public static void CopyArchiveToDockerContainer(this ILogger logger, string id, string path)
{
_GetArchiveFromDockerContainer(logger, path, id, null);
_CopyArchiveToDockerContainer(logger, path, TruncId(id), null);
}

public static void ReadArchiveFromDockerContainer(this ILogger logger, string id, string path)
{
_ReadArchiveFromDockerContainer(logger, path, TruncId(id), null);
}

public static void AttachToDockerContainer(this ILogger logger, string id, Type type)
{
_AttachToDockerContainer(logger, type, id, null);
_AttachToDockerContainer(logger, type, TruncId(id), null);
}

public static void ConnectToDockerNetwork(this ILogger logger, string networkId, string containerId)
{
_ConnectToDockerNetwork(logger, containerId, networkId, null);
_ConnectToDockerNetwork(logger, TruncId(containerId), TruncId(networkId), null);
}

public static void ExecuteCommandInDockerContainer(this ILogger logger, string id, IEnumerable<string> command)
{
_ExecuteCommandInDockerContainer(logger, string.Join(" ", command), id, null);
_ExecuteCommandInDockerContainer(logger, string.Join(" ", command), TruncId(id), null);
}

public static void DockerImageCreated(this ILogger logger, IImage image)
Expand All @@ -151,12 +167,12 @@ public static void DeleteDockerImage(this ILogger logger, IImage image)

public static void DockerNetworkCreated(this ILogger logger, string id)
{
_DockerNetworkCreated(logger, id, null);
_DockerNetworkCreated(logger, TruncId(id), null);
}

public static void DeleteDockerNetwork(this ILogger logger, string id)
{
_DeleteDockerNetwork(logger, id, null);
_DeleteDockerNetwork(logger, TruncId(id), null);
}

public static void DockerVolumeCreated(this ILogger logger, string name)
Expand Down Expand Up @@ -205,5 +221,10 @@ public static void DockerRegistryCredentialFound(this ILogger logger, string doc
{
_DockerRegistryCredentialFound(logger, dockerRegistry, null);
}

private static string TruncId(string id)
{
return id.Substring(0, Math.Min(12, id.Length));
}
}
}