Skip to content

Commit 4e8ca98

Browse files
committed
Improvements bases on feedback. Fixed tests. Added documentation.
1 parent d7e4e15 commit 4e8ca98

File tree

113 files changed

+283
-32
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+283
-32
lines changed

docfx/logging.md

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
11
Logging
22
=================
33

4-
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:
4+
SSH.NET uses the [Microsoft.Extensions.Logging](https://learn.microsoft.com/dotnet/core/extensions/logging) API to log diagnostic messages.
5+
6+
It is possible to specify a logger in the `ConnectionInfo`, for example:
7+
8+
```cs
9+
using Microsoft.Extensions.Logging;
10+
11+
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
12+
{
13+
builder.SetMinimumLevel(LogLevel.Debug);
14+
builder.AddConsole();
15+
});
16+
17+
Renci.SshNet.SshNetLoggingConfiguration.InitializeLogging(loggerFactory);
18+
```
19+
20+
```cs
21+
using Microsoft.Extensions.Logging;
22+
23+
ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
24+
{
25+
builder.SetMinimumLevel(LogLevel.Debug);
26+
builder.AddConsole();
27+
});
28+
29+
var connectionInfo = new ConnectionInfo("sftp.foo.com",
30+
"guest",
31+
new PasswordAuthenticationMethod("guest", "pwd"));
32+
33+
connectionInfo.LoggerFactory = loggerFactory;
34+
using (var client = new SftpClient(connectionInfo))
35+
{
36+
client.Connect();
37+
}
38+
```
39+
40+
You can also register an application-wide `ILoggerFactory` before using the SSH.NET APIs, this will be used as a fallback if the `ConnectionInfo` is not set, for example:
541

642
```cs
743
using Microsoft.Extensions.Logging;

src/Renci.SshNet/BaseClient.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public abstract class BaseClient : IBaseClient
2020
/// Holds value indicating whether the connection info is owned by this client.
2121
/// </summary>
2222
private readonly bool _ownsConnectionInfo;
23+
24+
private readonly ILogger _logger;
2325
private readonly IServiceFactory _serviceFactory;
2426
private readonly object _keepAliveLock = new object();
2527
private TimeSpan _keepAliveInterval;
@@ -35,14 +37,6 @@ public abstract class BaseClient : IBaseClient
3537
/// </value>
3638
internal ISession? Session { get; private set; }
3739

38-
internal ILogger? Logger
39-
{
40-
get
41-
{
42-
return Session?.SessionLoggerFactory?.CreateLogger(GetType());
43-
}
44-
}
45-
4640
/// <summary>
4741
/// Gets the factory for creating new services.
4842
/// </summary>
@@ -198,6 +192,7 @@ private protected BaseClient(ConnectionInfo connectionInfo, bool ownsConnectionI
198192
_connectionInfo = connectionInfo;
199193
_ownsConnectionInfo = ownsConnectionInfo;
200194
_serviceFactory = serviceFactory;
195+
_logger = (connectionInfo.LoggerFactory ?? SshNetLoggingConfiguration.LoggerFactory).CreateLogger(GetType());
201196
_keepAliveInterval = Timeout.InfiniteTimeSpan;
202197
}
203198

@@ -351,7 +346,7 @@ public async Task ConnectAsync(CancellationToken cancellationToken)
351346
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
352347
public void Disconnect()
353348
{
354-
Logger?.LogInformation("Disconnecting client.");
349+
_logger.LogInformation("Disconnecting client.");
355350

356351
CheckDisposed();
357352

@@ -450,7 +445,7 @@ protected virtual void Dispose(bool disposing)
450445

451446
if (disposing)
452447
{
453-
Logger?.LogDebug("Disposing client.");
448+
_logger.LogDebug("Disposing client.");
454449

455450
Disconnect();
456451

@@ -513,7 +508,7 @@ private void SendKeepAliveMessage()
513508
}
514509
catch (Exception ex)
515510
{
516-
Logger?.LogError(ex, "Error sending keepalive message");
511+
_logger.LogError(ex, "Error sending keepalive message");
517512
}
518513
finally
519514
{

src/Renci.SshNet/ForwardedPortDynamic.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ namespace Renci.SshNet
2121
/// </summary>
2222
public class ForwardedPortDynamic : ForwardedPort
2323
{
24-
private readonly ILogger _logger;
2524
private ForwardedPortStatus _status;
2625

2726
/// <summary>
@@ -75,7 +74,6 @@ public ForwardedPortDynamic(string host, uint port)
7574
BoundHost = host;
7675
BoundPort = port;
7776
_status = ForwardedPortStatus.Stopped;
78-
_logger = Session.SessionLoggerFactory.CreateLogger<ForwardedPortDynamic>();
7977
}
8078

8179
/// <summary>
@@ -416,7 +414,12 @@ private void InternalStop(TimeSpan timeout)
416414

417415
if (!_pendingChannelCountdown.Wait(timeout))
418416
{
419-
_logger.LogInformation("Timeout waiting for pending channels in dynamic forwarded port to close.");
417+
var session = Session;
418+
if (session != null)
419+
{
420+
var logger = session.SessionLoggerFactory.CreateLogger<ForwardedPortDynamic>();
421+
logger.LogInformation("Timeout waiting for pending channels in dynamic forwarded port to close.");
422+
}
420423
}
421424
}
422425

src/Renci.SshNet/ForwardedPortLocal.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ namespace Renci.SshNet
1414
/// </summary>
1515
public partial class ForwardedPortLocal : ForwardedPort
1616
{
17-
private readonly ILogger _logger;
1817
private ForwardedPortStatus _status;
1918
private bool _isDisposed;
2019
private Socket _listener;
@@ -103,7 +102,6 @@ public ForwardedPortLocal(string boundHost, uint boundPort, string host, uint po
103102
Host = host;
104103
Port = port;
105104
_status = ForwardedPortStatus.Stopped;
106-
_logger = Session.SessionLoggerFactory.CreateLogger<ForwardedPortLocal>();
107105
}
108106

109107
/// <summary>
@@ -390,7 +388,12 @@ private void InternalStop(TimeSpan timeout)
390388

391389
if (!_pendingChannelCountdown.Wait(timeout))
392390
{
393-
_logger.LogInformation("Timeout waiting for pending channels in local forwarded port to close.");
391+
var session = Session;
392+
if (session != null)
393+
{
394+
var logger = session.SessionLoggerFactory.CreateLogger<ForwardedPortLocal>();
395+
logger.LogInformation("Timeout waiting for pending channels in local forwarded port to close.");
396+
}
394397
}
395398
}
396399

src/Renci.SshNet/ForwardedPortRemote.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ namespace Renci.SshNet
1616
/// </summary>
1717
public class ForwardedPortRemote : ForwardedPort
1818
{
19-
private readonly ILogger _logger;
2019
private ForwardedPortStatus _status;
2120
private bool _requestStatus;
2221
private EventWaitHandle _globalRequestResponse = new AutoResetEvent(initialState: false);
@@ -100,7 +99,6 @@ public ForwardedPortRemote(IPAddress boundHostAddress, uint boundPort, IPAddress
10099
HostAddress = hostAddress;
101100
Port = port;
102101
_status = ForwardedPortStatus.Stopped;
103-
_logger = Session.SessionLoggerFactory.CreateLogger<ForwardedPortRemote>();
104102
}
105103

106104
/// <summary>
@@ -212,7 +210,12 @@ protected override void StopPort(TimeSpan timeout)
212210

213211
if (!_pendingChannelCountdown.Wait(timeout))
214212
{
215-
_logger.LogInformation("Timeout waiting for pending channels in remote forwarded port to close.");
213+
var session = Session;
214+
if (session != null)
215+
{
216+
var logger = session.SessionLoggerFactory.CreateLogger<ForwardedPortRemote>();
217+
logger.LogInformation("Timeout waiting for pending channels in remote forwarded port to close.");
218+
}
216219
}
217220

218221
_status = ForwardedPortStatus.Stopped;

src/Renci.SshNet/ISession.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal interface ISession : IDisposable
2828
/// Gets the logger factory for this session.
2929
/// </summary>
3030
/// <value>
31-
/// The logger factory for this session.
31+
/// The logger factory for this session. Will never return <see langword="null"/>.
3232
/// </value>
3333
public ILoggerFactory SessionLoggerFactory { get; }
3434

src/Renci.SshNet/ISubsystemSession.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ internal interface ISubsystemSession : IDisposable
1616
/// Gets the logger factory for this subsystem session.
1717
/// </summary>
1818
/// <value>
19-
/// The logger factory for this connection.
19+
/// The logger factory for this connection. Will never return <see langword="null"/>.
2020
/// </value>
2121
public ILoggerFactory SessionLoggerFactory { get; }
2222

src/Renci.SshNet/Security/KeyExchange.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Renci.SshNet.Security
1818
/// </summary>
1919
public abstract class KeyExchange : Algorithm, IKeyExchange
2020
{
21-
private readonly ILogger _logger;
21+
private ILogger _logger;
2222
private Func<byte[], KeyHostAlgorithm> _hostKeyAlgorithmFactory;
2323
private CipherInfo _clientCipherInfo;
2424
private CipherInfo _serverCipherInfo;
@@ -69,13 +69,13 @@ public byte[] ExchangeHash
6969
/// </summary>
7070
protected KeyExchange()
7171
{
72-
_logger = Session.SessionLoggerFactory.CreateLogger(GetType());
7372
}
7473

7574
/// <inheritdoc/>
7675
public virtual void Start(Session session, KeyExchangeInitMessage message, bool sendClientInitMessage)
7776
{
7877
Session = session;
78+
_logger = Session.SessionLoggerFactory.CreateLogger(GetType());
7979

8080
if (sendClientInitMessage)
8181
{

test/Renci.SshNet.Tests/Classes/BaseClientTestBase.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using Moq;
1+
using Microsoft.Extensions.Logging.Abstractions;
2+
3+
using Moq;
24

35
using Renci.SshNet.Connection;
46
using Renci.SshNet.Tests.Common;
@@ -16,6 +18,7 @@ protected virtual void CreateMocks()
1618
ServiceFactoryMock = new Mock<IServiceFactory>(MockBehavior.Strict);
1719
SocketFactoryMock = new Mock<ISocketFactory>(MockBehavior.Strict);
1820
SessionMock = new Mock<ISession>(MockBehavior.Strict);
21+
SessionMock.Setup(p => p.SessionLoggerFactory).Returns(NullLoggerFactory.Instance);
1922
}
2023

2124
protected virtual void SetupData()

test/Renci.SshNet.Tests/Classes/BaseClientTest_ConnectAsync_Timeout.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Threading;
33
using System.Threading.Tasks;
44

5+
using Microsoft.Extensions.Logging.Abstractions;
56
using Microsoft.VisualStudio.TestTools.UnitTesting;
67

78
using Moq;
@@ -23,6 +24,7 @@ public class BaseClientTest_ConnectAsync_Timeout
2324
public void Init()
2425
{
2526
var sessionMock = new Mock<ISession>();
27+
sessionMock.Setup(p => p.SessionLoggerFactory).Returns(NullLoggerFactory.Instance);
2628
var serviceFactoryMock = new Mock<IServiceFactory>();
2729
var socketFactoryMock = new Mock<ISocketFactory>();
2830

0 commit comments

Comments
 (0)