diff --git a/.editorconfig b/.editorconfig
index c897d8a93..aebbfc80f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -704,6 +704,9 @@ dotnet_code_quality.CA1828.api_surface = all
# Similar to MA0053, but does not support public types and types that define (new) virtual members.
dotnet_diagnostic.CA1852.severity = none
+# CA1848: don't enforce LoggerMessage pattern
+dotnet_diagnostic.CA1848.severity = suggestion
+
# CA1859: Change return type for improved performance
#
# By default, this diagnostic is only reported for private members.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ce029a36f..db38582f4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,9 +34,9 @@ The repository makes use of continuous integration (CI) with GitHub Actions to v
## Good to know
-### TraceSource logging
+### Logging
-The Debug build of SSH.NET contains rudimentary logging functionality via `System.Diagnostics.TraceSource`. See `Renci.SshNet.Abstractions.DiagnosticAbstraction` for usage examples.
+The tests always log to the console. See the [Logging documentation](https://sshnet.github.io/SSH.NET/logging.html) on how to set a custom `ILoggerFactory`.
### Wireshark
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8aa6398e7..3964b45d7 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,6 +15,9 @@
+
+
+
diff --git a/README.md b/README.md
index 1daebcf5f..c308a6bfd 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,7 @@ The main types provided by this library are:
## Additional Documentation
* [Further examples](https://sshnet.github.io/SSH.NET/examples.html)
+* [Logging](https://sshnet.github.io/SSH.NET/logging.html)
* [API browser](https://sshnet.github.io/SSH.NET/api/Renci.SshNet.html)
## Encryption Methods
diff --git a/docfx/logging.md b/docfx/logging.md
new file mode 100644
index 000000000..9c0693412
--- /dev/null
+++ b/docfx/logging.md
@@ -0,0 +1,15 @@
+Logging
+=================
+
+SSH.NET uses the [Microsoft.Extensions.Logging](https://learn.microsoft.com/dotnet/core/extensions/logging) API to log diagnostic messages. In order to access the log messages of SSH.NET in your own application for diagnosis, register your own `ILoggerFactory` before using the SSH.NET APIs, for example:
+
+```cs
+ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
+{
+ builder.SetMinimumLevel(LogLevel.Debug);
+ builder.AddConsole();
+});
+
+Renci.SshNet.SshNetLoggingConfiguration.InitializeLogging(loggerFactory);
+
+All messages by SSH.NET are logged under the `Renci.SshNet` category.
diff --git a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs b/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs
deleted file mode 100644
index bc1248dc0..000000000
--- a/src/Renci.SshNet/Abstractions/DiagnosticAbstraction.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System.ComponentModel;
-using System.Diagnostics;
-
-namespace Renci.SshNet.Abstractions
-{
- ///
- /// Provides access to the internals of SSH.NET.
- ///
- [EditorBrowsable(EditorBrowsableState.Never)]
- public static class DiagnosticAbstraction
- {
- ///
- /// The instance used by SSH.NET.
- ///
- ///
- ///
- /// Currently, the library only traces events when compiled in Debug mode.
- ///
- ///
- /// Configuration on .NET Core must be done programmatically, e.g.
- ///
- /// DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", "Verbose");
- /// DiagnosticAbstraction.Source.Listeners.Remove("Default");
- /// DiagnosticAbstraction.Source.Listeners.Add(new ConsoleTraceListener());
- /// DiagnosticAbstraction.Source.Listeners.Add(new TextWriterTraceListener("trace.log"));
- ///
- ///
- ///
- /// On .NET Framework, it is possible to configure via App.config, e.g.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- ///
- public static readonly TraceSource Source = new TraceSource("SshNet.Logging");
-
- ///
- /// Logs a message to at the
- /// level.
- ///
- /// The message to log.
- /// The trace event type.
- [Conditional("DEBUG")]
- public static void Log(string text, TraceEventType type = TraceEventType.Verbose)
- {
- Source.TraceEvent(type,
- System.Environment.CurrentManagedThreadId,
- text);
- }
- }
-}
diff --git a/src/Renci.SshNet/BaseClient.cs b/src/Renci.SshNet/BaseClient.cs
index 3757878b4..876710db9 100644
--- a/src/Renci.SshNet/BaseClient.cs
+++ b/src/Renci.SshNet/BaseClient.cs
@@ -4,7 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
@@ -20,6 +21,7 @@ public abstract class BaseClient : IBaseClient
///
private readonly bool _ownsConnectionInfo;
+ private readonly ILogger _logger;
private readonly IServiceFactory _serviceFactory;
private readonly object _keepAliveLock = new object();
private TimeSpan _keepAliveInterval;
@@ -190,6 +192,7 @@ private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionI
_connectionInfo = connectionInfo;
_ownsConnectionInfo = ownsConnectionInfo;
_serviceFactory = serviceFactory;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
_keepAliveInterval = Timeout.InfiniteTimeSpan;
}
@@ -343,7 +346,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
/// The method was called after the client was disposed.
public void Disconnect()
{
- DiagnosticAbstraction.Log("Disconnecting client.");
+ _logger.LogInformation("Disconnecting client.");
CheckDisposed();
@@ -442,7 +445,7 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
- DiagnosticAbstraction.Log("Disposing client.");
+ _logger.LogDebug("Disposing client.");
Disconnect();
diff --git a/src/Renci.SshNet/Channels/Channel.cs b/src/Renci.SshNet/Channels/Channel.cs
index ca8e910fa..4c3569cef 100644
--- a/src/Renci.SshNet/Channels/Channel.cs
+++ b/src/Renci.SshNet/Channels/Channel.cs
@@ -2,7 +2,8 @@
using System.Net.Sockets;
using System.Threading;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Common;
using Renci.SshNet.Messages;
using Renci.SshNet.Messages.Connection;
@@ -18,6 +19,7 @@ internal abstract class Channel : IChannel
private readonly Lock _messagingLock = new Lock();
private readonly uint _initialWindowSize;
private readonly ISession _session;
+ private readonly ILogger _logger;
private EventWaitHandle _channelClosedWaitHandle = new ManualResetEvent(initialState: false);
private EventWaitHandle _channelServerWindowAdjustWaitHandle = new ManualResetEvent(initialState: false);
private uint? _remoteWindowSize;
@@ -81,6 +83,7 @@ protected Channel(ISession session, uint localChannelNumber, uint localWindowSiz
LocalChannelNumber = localChannelNumber;
LocalPacketSize = localPacketSize;
LocalWindowSize = localWindowSize;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
session.ChannelWindowAdjustReceived += OnChannelWindowAdjust;
session.ChannelDataReceived += OnChannelData;
@@ -555,7 +558,7 @@ protected virtual void Close()
var closeWaitResult = _session.TryWait(_channelClosedWaitHandle, ConnectionInfo.ChannelCloseTimeout);
if (closeWaitResult != WaitResult.Success)
{
- DiagnosticAbstraction.Log(string.Format("Wait for channel close not successful: {0:G}.", closeWaitResult));
+ _logger.LogInformation("Wait for channel close not successful: {CloseWaitResult}", closeWaitResult);
}
}
}
diff --git a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs
index 7b00c3bae..71108e309 100644
--- a/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs
+++ b/src/Renci.SshNet/Channels/ChannelDirectTcpip.cs
@@ -3,6 +3,8 @@
using System.Net.Sockets;
using System.Threading;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
@@ -15,7 +17,7 @@ namespace Renci.SshNet.Channels
internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip
{
private readonly Lock _socketLock = new Lock();
-
+ private readonly ILogger _logger;
private EventWaitHandle _channelOpen = new AutoResetEvent(initialState: false);
private EventWaitHandle _channelData = new AutoResetEvent(initialState: false);
private IForwardedPort _forwardedPort;
@@ -31,6 +33,7 @@ internal sealed class ChannelDirectTcpip : ClientChannel, IChannelDirectTcpip
public ChannelDirectTcpip(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize)
: base(session, localChannelNumber, localWindowSize, localPacketSize)
{
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
}
///
@@ -157,8 +160,7 @@ private void ShutdownSocket(SocketShutdown how)
}
catch (SocketException ex)
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
+ _logger.LogInformation(ex, "Failure shutting down socket");
}
}
}
diff --git a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs
index 2cc0c16f8..29808e2b4 100644
--- a/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs
+++ b/src/Renci.SshNet/Channels/ChannelForwardedTcpip.cs
@@ -5,6 +5,8 @@
using System.Threading;
#endif
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
@@ -17,6 +19,7 @@ namespace Renci.SshNet.Channels
internal sealed class ChannelForwardedTcpip : ServerChannel, IChannelForwardedTcpip
{
private readonly Lock _socketShutdownAndCloseLock = new Lock();
+ private readonly ILogger _logger;
private Socket _socket;
private IForwardedPort _forwardedPort;
@@ -45,6 +48,7 @@ internal ChannelForwardedTcpip(ISession session,
remoteWindowSize,
remotePacketSize)
{
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
}
///
@@ -142,8 +146,7 @@ private void ShutdownSocket(SocketShutdown how)
}
catch (SocketException ex)
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
+ _logger.LogInformation(ex, "Failure shutting down socket");
}
}
}
diff --git a/src/Renci.SshNet/Common/Extensions.cs b/src/Renci.SshNet/Common/Extensions.cs
index cf4deb077..2f3ae6ac0 100644
--- a/src/Renci.SshNet/Common/Extensions.cs
+++ b/src/Renci.SshNet/Common/Extensions.cs
@@ -351,5 +351,12 @@ internal static bool IsConnected(this Socket socket)
return socket.Connected;
}
+
+ internal static string Join(this IEnumerable values, string separator)
+ {
+ // Used to avoid analyzers asking to "use an overload with a char parameter"
+ // which is not available on all targets.
+ return string.Join(separator, values);
+ }
}
}
diff --git a/src/Renci.SshNet/Connection/ConnectorBase.cs b/src/Renci.SshNet/Connection/ConnectorBase.cs
index c36fae6df..ebea9aa80 100644
--- a/src/Renci.SshNet/Connection/ConnectorBase.cs
+++ b/src/Renci.SshNet/Connection/ConnectorBase.cs
@@ -4,6 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
@@ -12,11 +14,14 @@ namespace Renci.SshNet.Connection
{
internal abstract class ConnectorBase : IConnector
{
+ private readonly ILogger _logger;
+
protected ConnectorBase(ISocketFactory socketFactory)
{
ThrowHelper.ThrowIfNull(socketFactory);
SocketFactory = socketFactory;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
}
internal ISocketFactory SocketFactory { get; private set; }
@@ -34,7 +39,7 @@ protected ConnectorBase(ISocketFactory socketFactory)
/// An error occurred trying to establish the connection.
protected Socket SocketConnect(EndPoint endPoint, TimeSpan timeout)
{
- DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}'.", endPoint));
+ _logger.LogInformation("Initiating connection to '{EndPoint}'.", endPoint);
var socket = SocketFactory.Create(SocketType.Stream, ProtocolType.Tcp);
@@ -65,7 +70,7 @@ protected async Task SocketConnectAsync(EndPoint endPoint, CancellationT
{
cancellationToken.ThrowIfCancellationRequested();
- DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}'.", endPoint));
+ _logger.LogInformation("Initiating connection to '{EndPoint}'.", endPoint);
var socket = SocketFactory.Create(SocketType.Stream, ProtocolType.Tcp);
try
diff --git a/src/Renci.SshNet/ForwardedPortDynamic.cs b/src/Renci.SshNet/ForwardedPortDynamic.cs
index 7d0e5af96..5b8ff9d29 100644
--- a/src/Renci.SshNet/ForwardedPortDynamic.cs
+++ b/src/Renci.SshNet/ForwardedPortDynamic.cs
@@ -7,6 +7,8 @@
using System.Text;
using System.Threading;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
@@ -19,6 +21,7 @@ namespace Renci.SshNet
///
public class ForwardedPortDynamic : ForwardedPort
{
+ private readonly ILogger _logger;
private ForwardedPortStatus _status;
///
@@ -72,6 +75,7 @@ public ForwardedPortDynamic(string host, uint port)
BoundHost = host;
BoundPort = port;
_status = ForwardedPortStatus.Stopped;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
}
///
@@ -409,8 +413,7 @@ private void InternalStop(TimeSpan timeout)
if (!_pendingChannelCountdown.Wait(timeout))
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Timeout waiting for pending channels in dynamic forwarded port to close.");
+ _logger.LogInformation("Timeout waiting for pending channels in dynamic forwarded port to close.");
}
}
diff --git a/src/Renci.SshNet/ForwardedPortLocal.cs b/src/Renci.SshNet/ForwardedPortLocal.cs
index ec030be44..50d84dc74 100644
--- a/src/Renci.SshNet/ForwardedPortLocal.cs
+++ b/src/Renci.SshNet/ForwardedPortLocal.cs
@@ -3,7 +3,8 @@
using System.Net.Sockets;
using System.Threading;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Common;
namespace Renci.SshNet
@@ -13,6 +14,7 @@ namespace Renci.SshNet
///
public partial class ForwardedPortLocal : ForwardedPort
{
+ private readonly ILogger _logger;
private ForwardedPortStatus _status;
private bool _isDisposed;
private Socket _listener;
@@ -101,6 +103,7 @@ public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint po
Host = host;
Port = port;
_status = ForwardedPortStatus.Stopped;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
}
///
@@ -387,8 +390,7 @@ private void InternalStop(TimeSpan timeout)
if (!_pendingChannelCountdown.Wait(timeout))
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Timeout waiting for pending channels in local forwarded port to close.");
+ _logger.LogInformation("Timeout waiting for pending channels in local forwarded port to close.");
}
}
diff --git a/src/Renci.SshNet/ForwardedPortRemote.cs b/src/Renci.SshNet/ForwardedPortRemote.cs
index 07b234541..f51a3d82b 100644
--- a/src/Renci.SshNet/ForwardedPortRemote.cs
+++ b/src/Renci.SshNet/ForwardedPortRemote.cs
@@ -3,6 +3,8 @@
using System.Net;
using System.Threading;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
@@ -14,6 +16,7 @@ namespace Renci.SshNet
///
public class ForwardedPortRemote : ForwardedPort
{
+ private readonly ILogger _logger;
private ForwardedPortStatus _status;
private bool _requestStatus;
private EventWaitHandle _globalRequestResponse = new AutoResetEvent(initialState: false);
@@ -97,6 +100,7 @@ public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress
HostAddress = hostAddress;
Port = port;
_status = ForwardedPortStatus.Stopped;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
}
///
@@ -208,8 +212,7 @@ protected override void StopPort(TimeSpan timeout)
if (!_pendingChannelCountdown.Wait(timeout))
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Timeout waiting for pending channels in remote forwarded port to close.");
+ _logger.LogInformation("Timeout waiting for pending channels in remote forwarded port to close.");
}
_status = ForwardedPortStatus.Stopped;
diff --git a/src/Renci.SshNet/Renci.SshNet.csproj b/src/Renci.SshNet/Renci.SshNet.csproj
index d00f773fa..2936c0a7f 100644
--- a/src/Renci.SshNet/Renci.SshNet.csproj
+++ b/src/Renci.SshNet/Renci.SshNet.csproj
@@ -36,6 +36,7 @@
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Renci.SshNet/Security/KeyExchange.cs b/src/Renci.SshNet/Security/KeyExchange.cs
index 96eb326ca..1331098b0 100644
--- a/src/Renci.SshNet/Security/KeyExchange.cs
+++ b/src/Renci.SshNet/Security/KeyExchange.cs
@@ -3,7 +3,8 @@
using System.Linq;
using System.Security.Cryptography;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Common;
using Renci.SshNet.Compression;
using Renci.SshNet.Messages;
@@ -17,6 +18,7 @@ namespace Renci.SshNet.Security
///
public abstract class KeyExchange : Algorithm, IKeyExchange
{
+ private readonly ILogger _logger;
private CipherInfo _clientCipherInfo;
private CipherInfo _serverCipherInfo;
private HashInfo _clientHashInfo;
@@ -61,6 +63,11 @@ public byte[] ExchangeHash
///
public event EventHandler HostKeyReceived;
+ private protected KeyExchange()
+ {
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
+ }
+
///
public virtual void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
{
@@ -71,12 +78,23 @@ public virtual void Start(Session session, KeyExchangeInitMessage message, bool
SendMessage(session.ClientInitMessage);
}
- // Determine encryption algorithm
+ // Determine client encryption algorithm
var clientEncryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys
from a in message.EncryptionAlgorithmsClientToServer
where a == b
select a).FirstOrDefault();
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Encryption client to server: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.Encryptions.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] Encryption client to server: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.EncryptionAlgorithmsClientToServer.Join(","));
+ }
+
if (string.IsNullOrEmpty(clientEncryptionAlgorithmName))
{
throw new SshConnectionException("Client encryption algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -85,11 +103,23 @@ from a in message.EncryptionAlgorithmsClientToServer
session.ConnectionInfo.CurrentClientEncryption = clientEncryptionAlgorithmName;
_clientCipherInfo = session.ConnectionInfo.Encryptions[clientEncryptionAlgorithmName];
- // Determine encryption algorithm
+ // Determine server encryption algorithm
var serverDecryptionAlgorithmName = (from b in session.ConnectionInfo.Encryptions.Keys
from a in message.EncryptionAlgorithmsServerToClient
where a == b
select a).FirstOrDefault();
+
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Encryption server to client: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.Encryptions.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] Encryption server to client: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.EncryptionAlgorithmsServerToClient.Join(","));
+ }
+
if (string.IsNullOrEmpty(serverDecryptionAlgorithmName))
{
throw new SshConnectionException("Server decryption algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -105,6 +135,18 @@ from a in message.EncryptionAlgorithmsServerToClient
from a in message.MacAlgorithmsClientToServer
where a == b
select a).FirstOrDefault();
+
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] MAC client to server: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.HmacAlgorithms.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] MAC client to server: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.MacAlgorithmsClientToServer.Join(","));
+ }
+
if (string.IsNullOrEmpty(clientHmacAlgorithmName))
{
throw new SshConnectionException("Client HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -121,6 +163,18 @@ from a in message.MacAlgorithmsClientToServer
from a in message.MacAlgorithmsServerToClient
where a == b
select a).FirstOrDefault();
+
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] MAC server to client: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.HmacAlgorithms.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] MAC server to client: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.MacAlgorithmsServerToClient.Join(","));
+ }
+
if (string.IsNullOrEmpty(serverHmacAlgorithmName))
{
throw new SshConnectionException("Server HMAC algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -135,6 +189,18 @@ from a in message.MacAlgorithmsServerToClient
from a in message.CompressionAlgorithmsClientToServer
where a == b
select a).FirstOrDefault();
+
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Compression client to server: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.CompressionAlgorithms.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] Compression client to server: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.CompressionAlgorithmsClientToServer.Join(","));
+ }
+
if (string.IsNullOrEmpty(compressionAlgorithmName))
{
throw new SshConnectionException("Compression algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -148,6 +214,18 @@ from a in message.CompressionAlgorithmsClientToServer
from a in message.CompressionAlgorithmsServerToClient
where a == b
select a).FirstOrDefault();
+
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Compression server to client: we offer {WeOffer}",
+ Session.SessionIdHex,
+ session.ConnectionInfo.CompressionAlgorithms.Keys.Join(","));
+
+ _logger.LogTrace("[{SessionId}] Compression server to client: they offer {TheyOffer}",
+ Session.SessionIdHex,
+ message.CompressionAlgorithmsServerToClient.Join(","));
+ }
+
if (string.IsNullOrEmpty(decompressionAlgorithmName))
{
throw new SshConnectionException("Decompression algorithm not found", DisconnectReason.KeyExchangeFailed);
@@ -190,9 +268,9 @@ public Cipher CreateServerCipher(out bool isAead)
serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverCipherInfo.KeySize / 8);
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server cipher.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentServerEncryption));
+ _logger.LogDebug("[{SessionId}] Creating {ServerEncryption} server cipher.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentServerEncryption);
// Create server cipher
return _serverCipherInfo.Cipher(serverKey, serverVector);
@@ -218,9 +296,9 @@ public Cipher CreateClientCipher(out bool isAead)
clientKey = GenerateSessionKey(SharedKey, ExchangeHash, clientKey, _clientCipherInfo.KeySize / 8);
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client cipher.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentClientEncryption));
+ _logger.LogDebug("[{SessionId}] Creating {ClientEncryption} client cipher.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentClientEncryption);
// Create client cipher
return _clientCipherInfo.Cipher(clientKey, clientVector);
@@ -251,9 +329,9 @@ public HashAlgorithm CreateServerHash(out bool isEncryptThenMAC)
Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'F', sessionId)),
_serverHashInfo.KeySize / 8);
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server hmac algorithm.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentServerHmacAlgorithm));
+ _logger.LogDebug("[{SessionId}] Creating {ServerHmacAlgorithm} server hmac algorithm.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentServerHmacAlgorithm);
return _serverHashInfo.HashAlgorithm(serverKey);
}
@@ -283,9 +361,9 @@ public HashAlgorithm CreateClientHash(out bool isEncryptThenMAC)
Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'E', sessionId)),
_clientHashInfo.KeySize / 8);
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client hmac algorithm.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentClientHmacAlgorithm));
+ _logger.LogDebug("[{SessionId}] Creating {ClientHmacAlgorithm} client hmac algorithm.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentClientHmacAlgorithm);
return _clientHashInfo.HashAlgorithm(clientKey);
}
@@ -303,9 +381,9 @@ public Compressor CreateCompressor()
return null;
}
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} client compressor.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentClientCompressionAlgorithm));
+ _logger.LogDebug("[{SessionId}] Creating {CompressionAlgorithm} client compressor.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentClientCompressionAlgorithm);
var compressor = _compressorFactory();
@@ -327,9 +405,9 @@ public Compressor CreateDecompressor()
return null;
}
- DiagnosticAbstraction.Log(string.Format("[{0}] Creating {1} server decompressor.",
- Session.ToHex(Session.SessionId),
- Session.ConnectionInfo.CurrentServerCompressionAlgorithm));
+ _logger.LogDebug("[{SessionId}] Creating {ServerCompressionAlgorithm} server decompressor.",
+ Session.SessionIdHex,
+ Session.ConnectionInfo.CurrentServerCompressionAlgorithm);
var decompressor = _decompressorFactory();
diff --git a/src/Renci.SshNet/ServiceFactory.cs b/src/Renci.SshNet/ServiceFactory.cs
index d238b6eac..0b8e27f67 100644
--- a/src/Renci.SshNet/ServiceFactory.cs
+++ b/src/Renci.SshNet/ServiceFactory.cs
@@ -4,7 +4,8 @@
using System.Net.Sockets;
using System.Text;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Common;
using Renci.SshNet.Connection;
using Renci.SshNet.Messages.Transport;
@@ -25,6 +26,13 @@ internal sealed partial class ServiceFactory : IServiceFactory
///
private const int PartialSuccessLimit = 5;
+ private readonly ILogger _logger;
+
+ internal ServiceFactory()
+ {
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
+ }
+
///
/// Creates an .
///
@@ -152,7 +160,7 @@ public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSe
fileSize = null;
maxPendingReads = defaultMaxPendingReads;
- DiagnosticAbstraction.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending reads: {1}", maxPendingReads, ex));
+ _logger.LogInformation(ex, "Failed to obtain size of file. Allowing maximum {MaxPendingReads} pending reads", maxPendingReads);
}
return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize);
diff --git a/src/Renci.SshNet/Session.cs b/src/Renci.SshNet/Session.cs
index 63560b685..639a7dbba 100644
--- a/src/Renci.SshNet/Session.cs
+++ b/src/Renci.SshNet/Session.cs
@@ -5,10 +5,14 @@
using System.Linq;
using System.Net.Sockets;
using System.Security.Cryptography;
+#if !NET
using System.Text;
+#endif
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
@@ -75,6 +79,7 @@ public class Session : ISession
///
private readonly IServiceFactory _serviceFactory;
private readonly ISocketFactory _socketFactory;
+ private readonly ILogger _logger;
///
/// Holds an object that is used to ensure only a single thread can read from
@@ -288,13 +293,28 @@ public bool IsConnected
}
}
+ private byte[] _sessionId;
+
///
/// Gets the session id.
///
///
/// The session id, or if the client has not been authenticated.
///
- public byte[] SessionId { get; private set; }
+ public byte[] SessionId
+ {
+ get
+ {
+ return _sessionId;
+ }
+ private set
+ {
+ _sessionId = value;
+ SessionIdHex = ToHex(value);
+ }
+ }
+
+ internal string SessionIdHex { get; private set; }
///
/// Gets the client init message.
@@ -535,6 +555,7 @@ internal Session(ConnectionInfo connectionInfo, IServiceFactory serviceFactory,
ConnectionInfo = connectionInfo;
_serviceFactory = serviceFactory;
_socketFactory = socketFactory;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
_messageListenerCompleted = new ManualResetEvent(initialState: true);
}
@@ -577,7 +598,7 @@ public void Connect()
ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
ConnectionInfo.ClientVersion = ClientVersion;
- DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
+ _logger.LogInformation("Server version '{ServerIdentification}'.", serverIdentification);
if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
{
@@ -703,7 +724,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
ServerVersion = ConnectionInfo.ServerVersion = serverIdentification.ToString();
ConnectionInfo.ClientVersion = ClientVersion;
- DiagnosticAbstraction.Log(string.Format("Server version '{0}'.", serverIdentification));
+ _logger.LogInformation("Server version '{ServerIdentification}'.", serverIdentification);
if (!(serverIdentification.ProtocolVersion.Equals("2.0") || serverIdentification.ProtocolVersion.Equals("1.99")))
{
@@ -796,7 +817,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
///
public void Disconnect()
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting session.", ToHex(SessionId)));
+ _logger.LogInformation("[{SessionId}] Disconnecting session.", SessionIdHex);
// send SSH_MSG_DISCONNECT message, clear socket read buffer and dispose it
Disconnect(DisconnectReason.ByApplication, "Connection terminated by the client.");
@@ -1026,7 +1047,10 @@ internal void SendMessage(Message message)
WaitOnHandle(_keyExchangeCompletedWaitHandle.WaitHandle);
}
- DiagnosticAbstraction.Log(string.Format("[{0}] Sending message '{1}' to server: '{2}'.", ToHex(SessionId), message.GetType().Name, message));
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Sending message {MessageName}({MessageNumber}) to server: '{Message}'.", SessionIdHex, message.MessageName, message.MessageNumber, message.ToString());
+ }
var paddingMultiplier = _clientCipher is null ? (byte)8 : Math.Max((byte)8, _clientCipher.MinimumSize);
var packetData = message.GetPacket(paddingMultiplier, _clientCompression, _clientEtm || _clientAead);
@@ -1165,12 +1189,12 @@ private bool TrySendMessage(Message message)
}
catch (SshException ex)
{
- DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex));
+ _logger.LogInformation(ex, "Failure sending message {MessageName}({MessageNumber}) to server: '{Message}'", message.MessageName, message.MessageNumber, message.ToString());
return false;
}
catch (SocketException ex)
{
- DiagnosticAbstraction.Log(string.Format("Failure sending message '{0}' to server: '{1}' => {2}", message.GetType().Name, message, ex));
+ _logger.LogInformation(ex, "Failure sending message {MessageName}({MessageNumber}) to server: '{Message}'", message.MessageName, message.MessageNumber, message.ToString());
return false;
}
}
@@ -1380,7 +1404,7 @@ private void TrySendDisconnect(DisconnectReason reasonCode, string message)
/// message.
internal void OnDisconnectReceived(DisconnectMessage message)
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disconnect received: {1} {2}.", ToHex(SessionId), message.ReasonCode, message.Description));
+ _logger.LogInformation("[{SessionId}] Disconnect received: {ReasonCode} {MessageDescription}.", SessionIdHex, message.ReasonCode, message.Description);
// transition to disconnecting state to avoid throwing exceptions while cleaning up, and to
// ensure any exceptions that are raised do not overwrite the SshConnectionException that we
@@ -1475,7 +1499,7 @@ internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
{
_isStrictKex = true;
- DiagnosticAbstraction.Log(string.Format("[{0}] Enabling strict key exchange extension.", ToHex(SessionId)));
+ _logger.LogDebug("[{SessionId}] Enabling strict key exchange extension.", SessionIdHex);
if (_inboundPacketSequence != 1)
{
@@ -1491,7 +1515,7 @@ internal void OnKeyExchangeInitReceived(KeyExchangeInitMessage message)
ConnectionInfo.CurrentKeyExchangeAlgorithm = _keyExchange.Name;
- DiagnosticAbstraction.Log(string.Format("[{0}] Performing {1} key exchange.", ToHex(SessionId), ConnectionInfo.CurrentKeyExchangeAlgorithm));
+ _logger.LogDebug("[{SessionId}] Performing {KeyExchangeAlgorithm} key exchange.", SessionIdHex, ConnectionInfo.CurrentKeyExchangeAlgorithm);
_keyExchange.HostKeyReceived += KeyExchange_HostKeyReceived;
@@ -1807,34 +1831,33 @@ private Message LoadMessage(byte[] data, int offset, int count)
var message = _sshMessageFactory.Create(messageType);
message.Load(data, offset + 1, count - 1);
- DiagnosticAbstraction.Log(string.Format("[{0}] Received message '{1}' from server: '{2}'.", ToHex(SessionId), message.GetType().Name, message));
+ if (_logger.IsEnabled(LogLevel.Trace))
+ {
+ _logger.LogTrace("[{SessionId}] Received message {MessageName}({MessageNumber}) from server: '{Message}'.", SessionIdHex, message.MessageName, message.MessageNumber, message.ToString());
+ }
return message;
}
- private static string ToHex(byte[] bytes, int offset)
+ private static string ToHex(byte[] bytes)
{
- var byteCount = bytes.Length - offset;
-
- var builder = new StringBuilder(bytes.Length * 2);
-
- for (var i = offset; i < byteCount; i++)
+ if (bytes is null)
{
- var b = bytes[i];
- _ = builder.Append(b.ToString("X2"));
+ return null;
}
- return builder.ToString();
- }
+#if NET
+ return Convert.ToHexString(bytes);
+#else
+ var builder = new StringBuilder(bytes.Length * 2);
- internal static string ToHex(byte[] bytes)
- {
- if (bytes is null)
+ foreach (var b in bytes)
{
- return null;
+ builder.Append(b.ToString("X2"));
}
- return ToHex(bytes, 0);
+ return builder.ToString();
+#endif
}
///
@@ -1951,7 +1974,7 @@ private void SocketDisconnectAndDispose()
{
try
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Shutting down socket.", ToHex(SessionId)));
+ _logger.LogDebug("[{SessionId}] Shutting down socket.", SessionIdHex);
// Interrupt any pending reads; should be done outside of socket read lock as we
// actually want shutdown the socket to make sure blocking reads are interrupted.
@@ -1963,14 +1986,13 @@ private void SocketDisconnectAndDispose()
}
catch (SocketException ex)
{
- // TODO: log as warning
- DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
+ _logger.LogInformation(ex, "Failure shutting down socket");
}
}
- DiagnosticAbstraction.Log(string.Format("[{0}] Disposing socket.", ToHex(SessionId)));
+ _logger.LogDebug("[{SessionId}] Disposing socket.", SessionIdHex);
_socket.Dispose();
- DiagnosticAbstraction.Log(string.Format("[{0}] Disposed socket.", ToHex(SessionId)));
+ _logger.LogDebug("[{SessionId}] Disposed socket.", SessionIdHex);
_socket = null;
}
}
@@ -2054,7 +2076,7 @@ private void RaiseError(Exception exp)
{
var connectionException = exp as SshConnectionException;
- DiagnosticAbstraction.Log(string.Format("[{0}] Raised exception: {1}", ToHex(SessionId), exp));
+ _logger.LogInformation(exp, "[{SessionId}] Raised exception", SessionIdHex);
if (_isDisconnecting)
{
@@ -2081,7 +2103,7 @@ private void RaiseError(Exception exp)
if (connectionException != null)
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disconnecting after exception: {1}", ToHex(SessionId), exp));
+ _logger.LogInformation(exp, "[{SessionId}] Disconnecting after exception", SessionIdHex);
Disconnect(connectionException.DisconnectReason, exp.ToString());
}
}
@@ -2154,7 +2176,7 @@ protected virtual void Dispose(bool disposing)
if (disposing)
{
- DiagnosticAbstraction.Log(string.Format("[{0}] Disposing session.", ToHex(SessionId)));
+ _logger.LogDebug("[{SessionId}] Disposing session.", SessionIdHex);
Disconnect();
diff --git a/src/Renci.SshNet/Sftp/SftpFileReader.cs b/src/Renci.SshNet/Sftp/SftpFileReader.cs
index 40e13edd4..3c86fc31b 100644
--- a/src/Renci.SshNet/Sftp/SftpFileReader.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileReader.cs
@@ -4,6 +4,8 @@
using System.Runtime.ExceptionServices;
using System.Threading;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
@@ -22,6 +24,7 @@ internal sealed class SftpFileReader : ISftpFileReader
private readonly ManualResetEvent _readAheadCompleted;
private readonly Dictionary _queue;
private readonly WaitHandle[] _waitHandles;
+ private readonly ILogger _logger;
///
/// Holds the size of the file, when available.
@@ -68,6 +71,7 @@ public SftpFileReader(byte[] handle, ISftpSession sftpSession, uint chunkSize, i
_readAheadCompleted = new ManualResetEvent(initialState: false);
_disposingWaitHandle = new ManualResetEvent(initialState: false);
_waitHandles = _sftpSession.CreateWaitHandleArray(_disposingWaitHandle, _semaphore.AvailableWaitHandle);
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger();
StartReadAhead();
}
@@ -266,7 +270,7 @@ private void Dispose(bool disposing)
}
catch (Exception ex)
{
- DiagnosticAbstraction.Log("Failure closing handle: " + ex);
+ _logger.LogInformation(ex, "Failure closing handle");
}
}
}
diff --git a/src/Renci.SshNet/SshNetLoggingConfiguration.cs b/src/Renci.SshNet/SshNetLoggingConfiguration.cs
new file mode 100644
index 000000000..4add75c9f
--- /dev/null
+++ b/src/Renci.SshNet/SshNetLoggingConfiguration.cs
@@ -0,0 +1,26 @@
+#nullable enable
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet
+{
+ ///
+ /// Allows configuring the logging for internal logs of SSH.NET.
+ ///
+ public static class SshNetLoggingConfiguration
+ {
+ internal static ILoggerFactory LoggerFactory { get; private set; } = NullLoggerFactory.Instance;
+
+ ///
+ /// Initializes the logging for SSH.NET.
+ ///
+ /// The logger factory.
+ public static void InitializeLogging(ILoggerFactory loggerFactory)
+ {
+ ThrowHelper.ThrowIfNull(loggerFactory);
+ LoggerFactory = loggerFactory;
+ }
+ }
+}
diff --git a/src/Renci.SshNet/SubsystemSession.cs b/src/Renci.SshNet/SubsystemSession.cs
index 5ddd80718..bb3ddfc9b 100644
--- a/src/Renci.SshNet/SubsystemSession.cs
+++ b/src/Renci.SshNet/SubsystemSession.cs
@@ -4,7 +4,8 @@
using System.Threading;
using System.Threading.Tasks;
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
+
using Renci.SshNet.Channels;
using Renci.SshNet.Common;
@@ -22,6 +23,7 @@ internal abstract class SubsystemSession : ISubsystemSession
private const int SystemWaitHandleCount = 3;
private readonly string _subsystemName;
+ private readonly ILogger _logger;
private ISession _session;
private IChannelSession _channel;
private Exception _exception;
@@ -84,6 +86,7 @@ protected SubsystemSession(ISession session, string subsystemName, int operation
_session = session;
_subsystemName = subsystemName;
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
OperationTimeout = operationTimeout;
}
@@ -180,7 +183,7 @@ protected void RaiseError(Exception error)
{
_exception = error;
- DiagnosticAbstraction.Log("Raised exception: " + error);
+ _logger.LogInformation(error, "Raised exception");
_ = _errorOccuredWaitHandle?.Set();
diff --git a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
index 06ae83ea6..d9f89ac45 100644
--- a/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
+++ b/test/Renci.SshNet.IntegrationTests/Renci.SshNet.IntegrationTests.csproj
@@ -18,6 +18,8 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
+
+
diff --git a/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs b/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
index 4be520af6..7c095b867 100644
--- a/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
+++ b/test/Renci.SshNet.IntegrationTests/TestsFixtures/InfrastructureFixture.cs
@@ -2,23 +2,27 @@
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Images;
+using Microsoft.Extensions.Logging;
+
namespace Renci.SshNet.IntegrationTests.TestsFixtures
{
public sealed class InfrastructureFixture : IDisposable
{
private InfrastructureFixture()
{
+ _loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.SetMinimumLevel(LogLevel.Debug);
+ builder.AddFilter("testcontainers", LogLevel.Information);
+ builder.AddConsole();
+ });
+
+ SshNetLoggingConfiguration.InitializeLogging(_loggerFactory);
}
- private static readonly Lazy InstanceLazy = new Lazy(() => new InfrastructureFixture());
+ public static InfrastructureFixture Instance { get; } = new InfrastructureFixture();
- public static InfrastructureFixture Instance
- {
- get
- {
- return InstanceLazy.Value;
- }
- }
+ private readonly ILoggerFactory _loggerFactory;
private IContainer _sshServer;
@@ -34,11 +38,14 @@ public static InfrastructureFixture Instance
public async Task InitializeAsync()
{
+ var containerLogger = _loggerFactory.CreateLogger("testcontainers");
+
_sshServerImage = new ImageFromDockerfileBuilder()
.WithName("renci-ssh-tests-server-image")
.WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), Path.Combine("test", "Renci.SshNet.IntegrationTests"))
.WithDockerfile("Dockerfile.TestServer")
.WithDeleteIfExists(true)
+ .WithLogger(containerLogger)
.Build();
await _sshServerImage.CreateAsync();
@@ -47,6 +54,7 @@ public async Task InitializeAsync()
.WithHostname("renci-ssh-tests-server")
.WithImage(_sshServerImage)
.WithPortBinding(22, true)
+ .WithLogger(containerLogger)
.Build();
await _sshServer.StartAsync();
diff --git a/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs b/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
index d08578e4e..6838e54d7 100644
--- a/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
+++ b/test/Renci.SshNet.IntegrationTests/TestsFixtures/IntegrationTestBase.cs
@@ -1,6 +1,4 @@
-using System.Diagnostics;
-
-using Renci.SshNet.Abstractions;
+using Microsoft.Extensions.Logging;
namespace Renci.SshNet.IntegrationTests.TestsFixtures
{
@@ -10,6 +8,7 @@ namespace Renci.SshNet.IntegrationTests.TestsFixtures
public abstract class IntegrationTestBase
{
private readonly InfrastructureFixture _infrastructureFixture;
+ private readonly ILogger _logger;
///
/// The SSH Server host name.
@@ -58,13 +57,10 @@ public SshUser User
protected IntegrationTestBase()
{
_infrastructureFixture = InfrastructureFixture.Instance;
- ShowInfrastructureInformation();
- }
-
- private void ShowInfrastructureInformation()
- {
- Console.WriteLine($"SSH Server host name: {_infrastructureFixture.SshServerHostName}");
- Console.WriteLine($"SSH Server port: {_infrastructureFixture.SshServerPort}");
+ _logger = SshNetLoggingConfiguration.LoggerFactory.CreateLogger(GetType());
+ _logger.LogDebug("SSH Server: {Host}:{Port}",
+ _infrastructureFixture.SshServerHostName,
+ _infrastructureFixture.SshServerPort);
}
///
@@ -85,18 +81,5 @@ protected void CreateTestFile(string fileName, int size)
}
}
}
-
- protected void EnableTracing()
- {
- DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Verbose));
- DiagnosticAbstraction.Source.Listeners.Remove("Default");
- DiagnosticAbstraction.Source.Listeners.Add(new ConsoleTraceListener() { Name = "TestConsoleLogger" });
- }
-
- protected void DisableTracing()
- {
- DiagnosticAbstraction.Source.Switch = new SourceSwitch("sourceSwitch", nameof(SourceLevels.Off));
- DiagnosticAbstraction.Source.Listeners.Remove("TestConsoleLogger");
- }
}
}