diff --git a/AcceptanceTest/ATApplication.cs b/AcceptanceTest/ATApplication.cs
index 6fff73dd2..7b073597c 100755
--- a/AcceptanceTest/ATApplication.cs
+++ b/AcceptanceTest/ATApplication.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Microsoft.Extensions.Logging;
using QuickFix;
namespace AcceptanceTest
@@ -169,7 +170,8 @@ public void FromApp(Message message, SessionID sessionId)
}
catch (System.Exception e)
{
- Session.LookupSession(sessionId)?.Log.OnEvent($"Exception during FromApp: {e}\n while processing msg ({message})");
+ Session.LookupSession(sessionId)?.Log.Log(LogLevel.Error, e,
+ "Exception during FromApp: {Error}\n while processing msg ({Message})", e, message);
}
}
diff --git a/AcceptanceTest/AcceptanceTest.csproj b/AcceptanceTest/AcceptanceTest.csproj
index 50d1db736..4cfe0ec14 100644
--- a/AcceptanceTest/AcceptanceTest.csproj
+++ b/AcceptanceTest/AcceptanceTest.csproj
@@ -19,6 +19,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/AcceptanceTest/TestBase.cs b/AcceptanceTest/TestBase.cs
index cc9dba3dd..53cf98ceb 100644
--- a/AcceptanceTest/TestBase.cs
+++ b/AcceptanceTest/TestBase.cs
@@ -2,6 +2,8 @@
using QuickFix;
using System.IO;
using System.Net;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Logger;
using QuickFix.Store;
@@ -11,6 +13,7 @@ public abstract class TestBase
{
private int _port;
private ThreadedSocketAcceptor _acceptor;
+ private LoggerFactory? _loggerFactory;
protected abstract SessionSettings Settings { get; }
@@ -23,11 +26,17 @@ public void Setup()
var testApp = new ATApplication();
var storeFactory = new MemoryStoreFactory();
- ILogFactory? logFactory = settings.Get().Has("Verbose") && settings.Get().GetBool("Verbose")
- ? new FileLogFactory(settings)
- : new NullLogFactory();
+ _loggerFactory = new LoggerFactory();
+ if (settings.Get().Has("Verbose") && settings.Get().GetBool("Verbose"))
+ {
+ _loggerFactory.AddProvider(new FileLoggerProvider(settings));
+ }
+ else
+ {
+ _loggerFactory.AddProvider(NullLoggerProvider.Instance);
+ }
- _acceptor = new ThreadedSocketAcceptor(testApp, storeFactory, settings, logFactory);
+ _acceptor = new ThreadedSocketAcceptor(testApp, storeFactory, settings, _loggerFactory);
_acceptor.Start();
}
@@ -36,6 +45,7 @@ public void Setup()
public void TearDown()
{
_acceptor?.Dispose();
+ _loggerFactory?.Dispose();
}
protected void RunTest(string definitionPath)
diff --git a/Examples/Executor/Examples.Executor.csproj b/Examples/Executor/Examples.Executor.csproj
index 0ece262ad..6cf2f7a70 100644
--- a/Examples/Executor/Examples.Executor.csproj
+++ b/Examples/Executor/Examples.Executor.csproj
@@ -40,4 +40,8 @@
+
+
+
+
diff --git a/Examples/Executor/Program.cs b/Examples/Executor/Program.cs
index ab7583496..dabe621fc 100644
--- a/Examples/Executor/Program.cs
+++ b/Examples/Executor/Program.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Extensions.Logging;
using QuickFix;
using QuickFix.Logger;
using QuickFix.Store;
@@ -27,9 +28,14 @@ static void Main(string[] args)
SessionSettings settings = new SessionSettings(args[0]);
IApplication executorApp = new Executor();
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
- ILogFactory logFactory = new ScreenLogFactory(settings);
- //ILogFactory logFactory = new FileLogFactory(settings);
- ThreadedSocketAcceptor acceptor = new ThreadedSocketAcceptor(executorApp, storeFactory, settings, logFactory);
+ using var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.SetMinimumLevel(LogLevel.Trace);
+ builder.AddProvider(new ScreenLoggerProvider(settings));
+ builder.AddProvider(new FileLoggerProvider(settings));
+ });
+ ThreadedSocketAcceptor acceptor =
+ new ThreadedSocketAcceptor(executorApp, storeFactory, settings, loggerFactory);
HttpServer srv = new HttpServer(HttpServerPrefix, settings);
acceptor.Start();
diff --git a/Examples/SimpleAcceptor/Examples.SimpleAcceptor.csproj b/Examples/SimpleAcceptor/Examples.SimpleAcceptor.csproj
index fc954a4b0..bc3ece1f4 100644
--- a/Examples/SimpleAcceptor/Examples.SimpleAcceptor.csproj
+++ b/Examples/SimpleAcceptor/Examples.SimpleAcceptor.csproj
@@ -34,4 +34,8 @@
+
+
+
+
diff --git a/Examples/SimpleAcceptor/Program.cs b/Examples/SimpleAcceptor/Program.cs
index 136b637bb..3d9c6c6f1 100644
--- a/Examples/SimpleAcceptor/Program.cs
+++ b/Examples/SimpleAcceptor/Program.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Extensions.Logging;
using QuickFix;
using QuickFix.Logger;
using QuickFix.Store;
@@ -30,8 +31,11 @@ static void Main(string[] args)
SessionSettings settings = new SessionSettings(args[0]);
IApplication app = new SimpleAcceptorApp();
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
- ILogFactory logFactory = new FileLogFactory(settings);
- IAcceptor acceptor = new ThreadedSocketAcceptor(app, storeFactory, settings, logFactory);
+ using var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.AddProvider(new FileLoggerProvider(settings));
+ });
+ IAcceptor acceptor = new ThreadedSocketAcceptor(app, storeFactory, settings, loggerFactory);
acceptor.Start();
Console.WriteLine("press to quit");
diff --git a/Examples/TradeClient/Examples.TradeClient.csproj b/Examples/TradeClient/Examples.TradeClient.csproj
index 738994695..9da5513b0 100644
--- a/Examples/TradeClient/Examples.TradeClient.csproj
+++ b/Examples/TradeClient/Examples.TradeClient.csproj
@@ -35,4 +35,8 @@
+
+
+
+
diff --git a/Examples/TradeClient/Program.cs b/Examples/TradeClient/Program.cs
index b40e1913c..8a5f4063e 100644
--- a/Examples/TradeClient/Program.cs
+++ b/Examples/TradeClient/Program.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Extensions.Logging;
using QuickFix.Logger;
using QuickFix.Store;
@@ -31,9 +32,12 @@ static void Main(string[] args)
QuickFix.SessionSettings settings = new QuickFix.SessionSettings(file);
TradeClientApp application = new TradeClientApp();
IMessageStoreFactory storeFactory = new FileStoreFactory(settings);
- ILogFactory logFactory = new ScreenLogFactory(settings);
- //ILogFactory logFactory = new FileLogFactory(settings);
- QuickFix.Transport.SocketInitiator initiator = new QuickFix.Transport.SocketInitiator(application, storeFactory, settings, logFactory);
+ using var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.AddProvider(new ScreenLoggerProvider(settings));
+ // builder.AddProvider(new FileLogProvider(settings));
+ });
+ QuickFix.Transport.SocketInitiator initiator = new QuickFix.Transport.SocketInitiator(application, storeFactory, settings, loggerFactory);
// this is a developer-test kludge. do not emulate.
application.MyInitiator = initiator;
diff --git a/QuickFIXn/AbstractInitiator.cs b/QuickFIXn/AbstractInitiator.cs
index 7222d43d1..eea21ef9c 100644
--- a/QuickFIXn/AbstractInitiator.cs
+++ b/QuickFIXn/AbstractInitiator.cs
@@ -1,6 +1,8 @@
using System.Threading;
using System.Collections.Generic;
using System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Logger;
using QuickFix.Store;
@@ -20,22 +22,44 @@ public abstract class AbstractInitiator : IInitiator
private readonly SessionFactory _sessionFactory;
private Thread? _thread;
- protected readonly NonSessionLog _nonSessionLog;
+ protected readonly ILogger _nonSessionLog;
+ private readonly LogFactoryAdapter? _logFactoryAdapter;
public bool IsStopped { get; private set; } = true;
+ [Obsolete("Use \"Microsoft.Extensions.Logging.ILoggerFactory\" instead of \"QuickFix.Logger.ILogFactory\".")]
protected AbstractInitiator(
IApplication app,
IMessageStoreFactory storeFactory,
SessionSettings settings,
- ILogFactory? logFactoryNullable,
- IMessageFactory? messageFactoryNullable)
+ ILogFactory? logFactoryNullable = null,
+ IMessageFactory? messageFactoryNullable = null) : this(app, storeFactory, settings,
+ logFactoryNullable is null
+ ? NullLoggerFactory.Instance
+ : new LogFactoryAdapter(logFactoryNullable, settings), messageFactoryNullable)
+ {
+ }
+
+ protected AbstractInitiator(
+ IApplication app,
+ IMessageStoreFactory storeFactory,
+ SessionSettings settings,
+ ILoggerFactory? logFactoryNullable = null,
+ IMessageFactory? messageFactoryNullable = null)
{
_settings = settings;
- var logFactory = logFactoryNullable ?? new NullLogFactory();
+ var logFactory = logFactoryNullable ?? NullLoggerFactory.Instance;
+ if (logFactory is LogFactoryAdapter lfa)
+ {
+ // LogFactoryAdapter is only ever created in the constructor marked obsolete, which means we own it and
+ // must save a ref to it so we can dispose it later. Any other ILoggerFactory is owned by someone else
+ // so we'll leave the dispose up to them. This should be removed eventually together with the old ILog
+ // and ILogFactory.
+ _logFactoryAdapter = lfa;
+ }
var msgFactory = messageFactoryNullable ?? new DefaultMessageFactory();
_sessionFactory = new SessionFactory(app, storeFactory, logFactory, msgFactory);
- _nonSessionLog = new NonSessionLog(logFactory);
+ _nonSessionLog = logFactory.CreateLogger("QuickFix");
HashSet definedSessions = _settings.GetSessions();
if (0 == definedSessions.Count)
@@ -211,6 +235,8 @@ public void Stop(bool force)
_connected.Clear();
_disconnected.Clear();
}
+
+ _logFactoryAdapter?.Dispose();
}
public bool IsLoggedOn
diff --git a/QuickFIXn/AcceptorSocketDescriptor.cs b/QuickFIXn/AcceptorSocketDescriptor.cs
index 527ea062b..e7af0da82 100644
--- a/QuickFIXn/AcceptorSocketDescriptor.cs
+++ b/QuickFIXn/AcceptorSocketDescriptor.cs
@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
namespace QuickFix
{
@@ -15,7 +15,7 @@ internal class AcceptorSocketDescriptor
public AcceptorSocketDescriptor(
IPEndPoint socketEndPoint,
SocketSettings socketSettings,
- NonSessionLog nonSessionLog)
+ ILogger nonSessionLog)
{
Address = socketEndPoint;
SocketReactor = new ThreadedSocketReactor(Address, socketSettings, this, nonSessionLog);
@@ -26,7 +26,7 @@ public AcceptorSocketDescriptor(
IPEndPoint socketEndPoint,
SocketSettings socketSettings,
SettingsDictionary sessionDict,
- NonSessionLog nonSessionLog) : this(socketEndPoint, socketSettings, nonSessionLog)
+ ILogger nonSessionLog) : this(socketEndPoint, socketSettings, nonSessionLog)
{ }
internal void AcceptSession(Session session)
diff --git a/QuickFIXn/ClientHandlerThread.cs b/QuickFIXn/ClientHandlerThread.cs
index 323c339cb..3ada2174e 100755
--- a/QuickFIXn/ClientHandlerThread.cs
+++ b/QuickFIXn/ClientHandlerThread.cs
@@ -1,7 +1,7 @@
using System.Net.Sockets;
using System.Threading;
using System;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
namespace QuickFix
{
@@ -36,7 +36,7 @@ internal ClientHandlerThread(
long clientId,
SocketSettings socketSettings,
AcceptorSocketDescriptor? acceptorDescriptor,
- NonSessionLog nonSessionLog
+ ILogger nonSessionLog
) {
Id = clientId;
_socketReader = new SocketReader(tcpClient, socketSettings, this, acceptorDescriptor, nonSessionLog);
diff --git a/QuickFIXn/Logger/CompositeLog.cs b/QuickFIXn/Logger/CompositeLog.cs
index f204fc9dc..2c7f2d637 100644
--- a/QuickFIXn/Logger/CompositeLog.cs
+++ b/QuickFIXn/Logger/CompositeLog.cs
@@ -5,6 +5,7 @@ namespace QuickFix.Logger;
///
/// File log implementation
///
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
internal class CompositeLog : ILog
{
private readonly ILog[] _logs;
diff --git a/QuickFIXn/Logger/CompositeLogFactory.cs b/QuickFIXn/Logger/CompositeLogFactory.cs
index 9a51d14df..4d11665eb 100644
--- a/QuickFIXn/Logger/CompositeLogFactory.cs
+++ b/QuickFIXn/Logger/CompositeLogFactory.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
namespace QuickFix.Logger;
@@ -6,6 +7,7 @@ namespace QuickFix.Logger;
/// Allows multiple log factories to be used with QuickFIX/N.
/// For example, you could log events to the console and also log all events and messages to a file.
///
+[Obsolete("Use Microsoft.Extensions.Logging instead")]
public class CompositeLogFactory : ILogFactory
{
private readonly ILogFactory[] _factories;
diff --git a/QuickFIXn/Logger/FileLog.cs b/QuickFIXn/Logger/FileLog.cs
index 2041ed650..d88ca61ef 100755
--- a/QuickFIXn/Logger/FileLog.cs
+++ b/QuickFIXn/Logger/FileLog.cs
@@ -1,4 +1,5 @@
using System;
+using Microsoft.Extensions.Logging;
using QuickFix.Fields.Converters;
using QuickFix.Util;
@@ -7,7 +8,8 @@ namespace QuickFix.Logger;
///
/// File log implementation
///
-public class FileLog : ILog
+[Obsolete("Use Microsoft.Extensions.Logging instead")]
+public class FileLog : ILog, ILogger
{
private readonly object _sync = new();
@@ -119,6 +121,34 @@ public void OnEvent(string s)
}
}
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
+ Func formatter)
+ {
+ if (!IsEnabled(logLevel)) return;
+ if (eventId == LogEventIds.IncomingMessage || eventId == LogEventIds.OutgoingMessage)
+ {
+ lock (_sync)
+ {
+ _messageLog.WriteLine(
+ $"{DateTimeConverter.ToFIX(DateTime.UtcNow, TimeStampPrecision.Millisecond)} : {formatter(state, exception)}");
+ }
+ }
+ else
+ {
+ lock (_sync)
+ {
+ _eventLog.WriteLine(
+ $"{DateTimeConverter.ToFIX(DateTime.UtcNow, TimeStampPrecision.Millisecond)} : {formatter(state, exception)}");
+ }
+ }
+ }
+
+ public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
+
+#pragma warning disable CS8633
+ public IDisposable BeginScope(TState state) where TState : notnull => default!;
+#pragma warning restore CS8633
+
#endregion
#region IDisposable Members
diff --git a/QuickFIXn/Logger/FileLogFactory.cs b/QuickFIXn/Logger/FileLogFactory.cs
index fc9bfc568..b3051103e 100755
--- a/QuickFIXn/Logger/FileLogFactory.cs
+++ b/QuickFIXn/Logger/FileLogFactory.cs
@@ -1,8 +1,11 @@
-namespace QuickFix.Logger;
+using System;
+
+namespace QuickFix.Logger;
///
/// Creates a message store that stores messages in a file
///
+[Obsolete("Use Microsoft.Extensions.Logging instead")]
public class FileLogFactory : ILogFactory
{
private readonly SessionSettings _settings;
diff --git a/QuickFIXn/Logger/FileLoggerProvider.cs b/QuickFIXn/Logger/FileLoggerProvider.cs
new file mode 100644
index 000000000..453ca0a60
--- /dev/null
+++ b/QuickFIXn/Logger/FileLoggerProvider.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
+
+[Obsolete("This class is provided to ease migration from the old logging system to Microsoft.Extensions.Logging." +
+ "It is an attempt to maintain the behavior of the previous FileLog and FileLogFactory while plugging into the Microsoft.Extensions.Logging ecosystem. " +
+ "Consider using the more robust logging options available in the .NET ecosystem, like the MS Console logging provider, Serilog and NLog.")]
+public class FileLoggerProvider : ILoggerProvider
+{
+ private readonly SessionSettings _settings;
+ private readonly ConcurrentDictionary _loggers = new();
+
+ public FileLoggerProvider(SessionSettings settings)
+ {
+ _settings = settings;
+ }
+
+ public ILogger CreateLogger(string categoryName)
+ {
+ // category will be "QuickFix" for non-session logger and "QuickFix." for session logger
+ var sessionIdString = categoryName.Length > "QuickFix.".Length ? categoryName.Substring(9) : "";
+ var sessionId = _settings.GetSessions()
+ .SingleOrDefault(s =>
+ s.ToString().Equals(sessionIdString, StringComparison.InvariantCulture));
+ var defaultSessionId = new SessionID("Non", "Session", "Log");
+
+ return _loggers.GetOrAdd(sessionId ?? defaultSessionId, sId => sessionId is not null
+ ? new FileLog(_settings.Get(sId).GetString(SessionSettings.FILE_LOG_PATH), sId)
+ : new NonSessionFileLogger(_settings.Get().GetString(SessionSettings.FILE_LOG_PATH), sId));
+ }
+
+ public void Dispose()
+ {
+ foreach (var (_, logger) in _loggers)
+ {
+ try
+ {
+ if (logger is IDisposable disposable)
+ {
+ disposable.Dispose();
+ }
+ }
+ catch { }
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickFIXn/Logger/ILog.cs b/QuickFIXn/Logger/ILog.cs
index c77166dbc..c4ffafabb 100755
--- a/QuickFIXn/Logger/ILog.cs
+++ b/QuickFIXn/Logger/ILog.cs
@@ -5,6 +5,7 @@ namespace QuickFix.Logger;
///
/// Session log for messages and events
///
+[Obsolete("Use Microsoft.Extensions.Logging instead")]
public interface ILog : IDisposable
{
///
diff --git a/QuickFIXn/Logger/ILogFactory.cs b/QuickFIXn/Logger/ILogFactory.cs
index 231f1d566..89ba074b3 100755
--- a/QuickFIXn/Logger/ILogFactory.cs
+++ b/QuickFIXn/Logger/ILogFactory.cs
@@ -1,8 +1,11 @@
-namespace QuickFix.Logger;
+using System;
+
+namespace QuickFix.Logger;
///
/// Creates a log instance
///
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
public interface ILogFactory
{
///
diff --git a/QuickFIXn/Logger/LogEventIds.cs b/QuickFIXn/Logger/LogEventIds.cs
new file mode 100644
index 000000000..bc845c1c7
--- /dev/null
+++ b/QuickFIXn/Logger/LogEventIds.cs
@@ -0,0 +1,9 @@
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
+
+internal static class LogEventIds
+{
+ internal static readonly EventId IncomingMessage = 7702;
+ internal static readonly EventId OutgoingMessage = 7203;
+}
\ No newline at end of file
diff --git a/QuickFIXn/Logger/LogFactoryAdapter.cs b/QuickFIXn/Logger/LogFactoryAdapter.cs
new file mode 100644
index 000000000..874ff05a2
--- /dev/null
+++ b/QuickFIXn/Logger/LogFactoryAdapter.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Concurrent;
+using System.Linq;
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
+
+internal class LogFactoryAdapter : ILoggerFactory, IDisposable
+{
+ private readonly ILogFactory _logFactory;
+ private readonly SessionSettings _settings;
+ private readonly ConcurrentDictionary _loggers = new();
+
+ internal LogFactoryAdapter(ILogFactory logFactory, SessionSettings settings)
+ {
+ _logFactory = logFactory;
+ _settings = settings;
+ }
+
+ public ILogger CreateLogger(string categoryName)
+ {
+ // category will be "QuickFix" for non-session logger and "QuickFix." for session logger
+ var sessionIdString = categoryName.Length > "QuickFix.".Length ? categoryName.Substring(9) : "";
+ var sessionId = _settings.GetSessions()
+ .SingleOrDefault(s =>
+ s.ToString().Equals(sessionIdString, StringComparison.InvariantCulture));
+ var defaultSessionId = new SessionID("Non", "Session", "Log");
+
+ return _loggers.GetOrAdd(sessionId ?? defaultSessionId, sid => sessionId is not null
+ ? new LogAdapter(_logFactory.Create(sid))
+ : new LogAdapter(_logFactory.CreateNonSessionLog()));
+ }
+
+ public void AddProvider(ILoggerProvider provider)
+ {
+ throw new NotImplementedException();
+ }
+
+ public void Dispose()
+ {
+ foreach (var (_, logger) in _loggers)
+ {
+ if (logger is IDisposable disposable)
+ {
+ try
+ {
+ disposable.Dispose();
+ }
+ catch
+ {
+ }
+ }
+ }
+ }
+}
+
+internal class LogAdapter : ILogger
+{
+ private readonly ILog _log;
+
+ public LogAdapter(ILog log)
+ {
+ _log = log;
+ }
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
+ Func formatter)
+ {
+ if (!IsEnabled(logLevel)) return;
+ if (eventId == LogEventIds.IncomingMessage)
+ {
+ _log.OnIncoming(formatter(state, exception));
+ }
+ else if (eventId == LogEventIds.OutgoingMessage)
+ {
+ _log.OnOutgoing(formatter(state, exception));
+ }
+ else
+ {
+ _log.OnEvent(formatter(state, exception));
+ }
+ }
+
+ public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
+
+#pragma warning disable CS8633
+ public IDisposable BeginScope(TState state) where TState : notnull => default!;
+#pragma warning restore CS8633
+}
\ No newline at end of file
diff --git a/QuickFIXn/Logger/NonSessionFileLogger.cs b/QuickFIXn/Logger/NonSessionFileLogger.cs
new file mode 100644
index 000000000..013e923af
--- /dev/null
+++ b/QuickFIXn/Logger/NonSessionFileLogger.cs
@@ -0,0 +1,32 @@
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
+
+///
+/// Like the file logger, but only creates the files on first write
+///
+internal class NonSessionFileLogger : ILogger, IDisposable
+{
+ private readonly Lazy _fileLog;
+
+ internal NonSessionFileLogger(string fileLogPath, SessionID sessionId)
+ {
+ _fileLog = new Lazy(() => new FileLog(fileLogPath, sessionId));
+ }
+
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
+ Func formatter) =>
+ _fileLog.Value.Log(logLevel, eventId, state, exception, formatter);
+
+ public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
+
+#pragma warning disable CS8633
+ public IDisposable BeginScope(TState state) where TState : notnull => _fileLog.Value.BeginScope(state);
+#pragma warning restore CS8633
+
+ public void Dispose()
+ {
+ if (_fileLog.IsValueCreated) _fileLog.Value.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/QuickFIXn/Logger/NonSessionLog.cs b/QuickFIXn/Logger/NonSessionLog.cs
index 2fa3ae5d4..40370b521 100644
--- a/QuickFIXn/Logger/NonSessionLog.cs
+++ b/QuickFIXn/Logger/NonSessionLog.cs
@@ -1,9 +1,12 @@
+using System;
+
namespace QuickFix.Logger;
///
/// A logger that can be used when the calling logic cannot identify a session (which is rare).
/// Does not create a log artifact until first write.
///
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
public class NonSessionLog : System.IDisposable {
private readonly ILogFactory _logFactory;
diff --git a/QuickFIXn/Logger/NullLog.cs b/QuickFIXn/Logger/NullLog.cs
index cccf6cdac..381257e95 100755
--- a/QuickFIXn/Logger/NullLog.cs
+++ b/QuickFIXn/Logger/NullLog.cs
@@ -1,9 +1,12 @@
+using System;
+
namespace QuickFix.Logger;
///
/// Log implementation that does not do anything
///
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
public sealed class NullLog : ILog
{
#region ILog Members
diff --git a/QuickFIXn/Logger/NullLogFactory.cs b/QuickFIXn/Logger/NullLogFactory.cs
index 1b0e74f97..b446679cf 100644
--- a/QuickFIXn/Logger/NullLogFactory.cs
+++ b/QuickFIXn/Logger/NullLogFactory.cs
@@ -1,5 +1,8 @@
-namespace QuickFix.Logger;
+using System;
+namespace QuickFix.Logger;
+
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
public class NullLogFactory : ILogFactory
{
public NullLogFactory() { }
diff --git a/QuickFIXn/Logger/ScreenLog.cs b/QuickFIXn/Logger/ScreenLog.cs
index dd44516fc..bc83177a1 100755
--- a/QuickFIXn/Logger/ScreenLog.cs
+++ b/QuickFIXn/Logger/ScreenLog.cs
@@ -1,9 +1,13 @@
-namespace QuickFix.Logger;
+using System;
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
///
/// FIXME - needs to log sessionIDs, timestamps, etc.
///
-public class ScreenLog : ILog
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
+public class ScreenLog : ILog, ILogger
{
private readonly object _sync = new ();
private readonly bool _logIncoming;
@@ -56,16 +60,29 @@ public void OnEvent(string s)
}
#endregion
- #region IDisposable implementation
- public void Dispose()
- {
- Dispose(true);
- System.GC.SuppressFinalize(this);
- }
- protected virtual void Dispose(bool disposing)
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception,
+ Func formatter)
{
- // Nothing to dispose of...
+ if (!IsEnabled(logLevel)) return;
+ if (eventId == LogEventIds.IncomingMessage && _logIncoming)
+ {
+ Console.WriteLine($" {formatter(state, exception).Replace(Message.SOH, '|')}");
+ }
+ else if (eventId == LogEventIds.OutgoingMessage && _logOutgoing)
+ {
+ Console.WriteLine($" {formatter(state, exception).Replace(Message.SOH, '|')}");
+ }
+ else if (_logEvent)
+ {
+ Console.WriteLine($" {formatter(state, exception)}");
+ }
}
- ~ScreenLog() => Dispose(false);
- #endregion
+
+ public bool IsEnabled(LogLevel logLevel) => logLevel != LogLevel.None;
+
+#pragma warning disable CS8633
+ public IDisposable BeginScope(TState state) where TState : notnull => default!;
+#pragma warning restore CS8633
+
+ public void Dispose(){}
}
diff --git a/QuickFIXn/Logger/ScreenLogFactory.cs b/QuickFIXn/Logger/ScreenLogFactory.cs
index 2e426ac9c..771e855bb 100755
--- a/QuickFIXn/Logger/ScreenLogFactory.cs
+++ b/QuickFIXn/Logger/ScreenLogFactory.cs
@@ -1,5 +1,8 @@
-namespace QuickFix.Logger;
+using System;
+namespace QuickFix.Logger;
+
+[Obsolete("Use Microsoft.Extensions.Logging instead.")]
public class ScreenLogFactory : ILogFactory
{
private const string SCREEN_LOG_SHOW_INCOMING = "ScreenLogShowIncoming";
diff --git a/QuickFIXn/Logger/ScreenLoggerProvider.cs b/QuickFIXn/Logger/ScreenLoggerProvider.cs
new file mode 100755
index 000000000..ecb1f02ce
--- /dev/null
+++ b/QuickFIXn/Logger/ScreenLoggerProvider.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Linq;
+using Microsoft.Extensions.Logging;
+
+namespace QuickFix.Logger;
+
+[Obsolete("This class is provided to ease migration from the old logging system to Microsoft.Extensions.Logging." +
+ "It is an attempt to maintain the behavior of the previous ScreenLog and ScreenLogFactory while plugging into the Microsoft.Extensions.Logging ecosystem. " +
+ "Consider using the more robust logging options available in the .NET ecosystem, like the MS Console logging provider, Serilog and NLog.")]
+public class ScreenLoggerProvider : ILoggerProvider
+{
+ private const string SCREEN_LOG_SHOW_INCOMING = "ScreenLogShowIncoming";
+ private const string SCREEN_LOG_SHOW_OUTGOING = "ScreenLogShowOutgoing";
+ private const string SCREEN_LOG_SHOW_EVENTS = "ScreenLogShowEvents";
+ private readonly bool _logIncoming = false;
+ private readonly bool _logOutgoing = false;
+ private readonly bool _logEvent = false;
+ private readonly SessionSettings _settings;
+
+ public ScreenLoggerProvider(SessionSettings settings)
+ {
+ _settings = settings;
+ }
+
+ public ScreenLoggerProvider(bool logIncoming, bool logOutgoing, bool logEvent)
+ {
+ _logIncoming = logIncoming;
+ _logOutgoing = logOutgoing;
+ _logEvent = logEvent;
+ _settings = new SessionSettings();
+ }
+
+ public ILogger CreateLogger(string categoryName)
+ {
+ // category will be "QuickFix" for non-session logger and "QuickFix." for session logger
+ var sessionIdString = categoryName.Length > "QuickFix.".Length ? categoryName.Substring(9) : "";
+ bool logIncoming = _logIncoming;
+ bool logOutgoing = _logOutgoing;
+ bool logEvent = _logEvent;
+
+ var sessionId = _settings.GetSessions()
+ .SingleOrDefault(s => s.ToString().Equals(sessionIdString, StringComparison.InvariantCulture));
+ if (sessionId is not null)
+ {
+ SettingsDictionary dict = _settings.Get(sessionId);
+
+ logIncoming = _logIncoming || dict.IsBoolPresentAndTrue(SCREEN_LOG_SHOW_INCOMING);
+ logOutgoing = _logOutgoing || dict.IsBoolPresentAndTrue(SCREEN_LOG_SHOW_OUTGOING);
+ logEvent = _logEvent || dict.IsBoolPresentAndTrue(SCREEN_LOG_SHOW_EVENTS);
+ }
+
+ return new ScreenLog(logIncoming, logOutgoing, logEvent);
+ }
+
+ public void Dispose()
+ {
+ }
+}
\ No newline at end of file
diff --git a/QuickFIXn/QuickFix.csproj b/QuickFIXn/QuickFix.csproj
index 8e1f2bd7f..e82d837ed 100644
--- a/QuickFIXn/QuickFix.csproj
+++ b/QuickFIXn/QuickFix.csproj
@@ -43,4 +43,8 @@
<_Parameter1>UnitTests
+
+
+
+
diff --git a/QuickFIXn/Session.cs b/QuickFIXn/Session.cs
index d4e0c1a85..0bcb171dd 100755
--- a/QuickFIXn/Session.cs
+++ b/QuickFIXn/Session.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
+using Microsoft.Extensions.Logging;
using QuickFix.Fields;
using QuickFix.Fields.Converters;
using QuickFix.Logger;
@@ -36,7 +37,7 @@ public class Session : IDisposable
// state
public IMessageStore MessageStore => _state.MessageStore;
- public ILog Log => _state.Log;
+ public ILogger Log => _state.Log;
public bool IsInitiator => _state.IsInitiator;
public bool IsAcceptor => !_state.IsInitiator;
public bool IsEnabled => _state.IsEnabled;
@@ -225,7 +226,7 @@ public Session(
DataDictionaryProvider dataDictProvider,
SessionSchedule sessionSchedule,
int heartBtInt,
- ILogFactory logFactory,
+ ILoggerFactory loggerFactory,
IMessageFactory msgFactory,
string senderDefaultApplVerId)
{
@@ -243,9 +244,9 @@ public Session(
? DataDictionaryProvider.GetApplicationDataDictionary(SenderDefaultApplVerID)
: SessionDataDictionary;
- ILog log = logFactory.Create(sessId);
+ var logger = loggerFactory.CreateLogger($"QuickFix.{sessId}");
- _state = new SessionState(isInitiator, log, heartBtInt, storeFactory.Create(sessId));
+ _state = new SessionState(isInitiator, logger, heartBtInt, storeFactory.Create(sessId));
// Configuration defaults.
// Will be overridden by the SessionFactory with values in the user's configuration.
@@ -275,7 +276,7 @@ public Session(
}
Application.OnCreate(SessionID);
- Log.OnEvent("Created session");
+ Log.Log(LogLevel.Debug, "Created session");
}
#region Static Methods
@@ -355,7 +356,19 @@ public bool Send(string message)
{
if (_responder is null)
return false;
- Log.OnOutgoing(message);
+
+ const LogLevel messagesLogLevel = LogLevel.Information;
+ if (Log.IsEnabled(messagesLogLevel))
+ {
+ using (Log.BeginScope(new Dictionary
+ {
+ {"MessageType", Message.GetMsgType(message)}
+ }))
+ {
+ Log.Log(messagesLogLevel, LogEventIds.OutgoingMessage, "{Message}", message);
+ }
+ }
+
return _responder.Send(message);
}
}
@@ -389,13 +402,13 @@ public void Disconnect(string reason)
{
if (_responder is not null)
{
- Log.OnEvent($"Session {SessionID} disconnecting: {reason}");
+ Log.Log(LogLevel.Debug, "Session {SessionID} disconnecting: {Reason}", SessionID, reason);
_responder.Disconnect();
_responder = null;
}
else
{
- Log.OnEvent($"Session {SessionID} already disconnected: {reason}");
+ Log.Log(LogLevel.Debug, "Session {SessionID} already disconnected: {Reason}", SessionID, reason);
}
if (_state.ReceivedLogon || _state.SentLogon)
@@ -444,7 +457,7 @@ public void Next()
if (!_state.SentLogout)
{
- Log.OnEvent("Initiated logout request");
+ Log.Log(LogLevel.Debug, "Initiated logout request");
GenerateLogout(_state.LogoutReason);
}
}
@@ -454,9 +467,9 @@ public void Next()
if (_state.ShouldSendLogon && IsTimeToGenerateLogon())
{
if (GenerateLogon())
- Log.OnEvent("Initiated logon request");
+ Log.Log(LogLevel.Debug, "Initiated logon request");
else
- Log.OnEvent("Error during logon request initiation");
+ Log.Log(LogLevel.Error, "Error during logon request initiation");
}
else if (_state.SentLogon && _state.LogonTimedOut())
@@ -487,7 +500,7 @@ public void Next()
{
GenerateTestRequest("TEST");
_state.TestRequestCounter += 1;
- Log.OnEvent("Sent test request TEST");
+ Log.Log(LogLevel.Debug, "Sent test request TEST");
}
else if (_state.NeedHeartbeat())
{
@@ -512,7 +525,24 @@ public void Next(string msgStr)
///
private void NextMessage(string msgStr)
{
- Log.OnIncoming(msgStr);
+ const LogLevel messageLogLevel = LogLevel.Information;
+ try
+ {
+ if (Log.IsEnabled(messageLogLevel))
+ {
+ using (Log.BeginScope(new Dictionary
+ {
+ {"MessageType", Message.GetMsgType(msgStr)}
+ }))
+ {
+ Log.Log(messageLogLevel, LogEventIds.IncomingMessage, "{Message}", msgStr);
+ }
+ }
+ }
+ catch (Exception)
+ {
+ Log.Log(messageLogLevel, LogEventIds.IncomingMessage, "{Message}", msgStr);
+ }
MessageBuilder msgBuilder = new MessageBuilder(
msgStr,
@@ -595,7 +625,7 @@ internal void Next(MessageBuilder msgBuilder)
}
catch (InvalidMessage e)
{
- Log.OnEvent(e.Message);
+ Log.Log(LogLevel.Information, "{Message}", e.Message);
try
{
@@ -610,7 +640,7 @@ internal void Next(MessageBuilder msgBuilder)
catch (TagException e)
{
if (e.InnerException is not null)
- Log.OnEvent(e.InnerException.Message);
+ Log.Log(LogLevel.Error, "{Message}", e.InnerException.Message);
GenerateReject(msgBuilder, e.sessionRejectReason, e.Field);
}
catch (UnsupportedVersion uvx)
@@ -621,19 +651,19 @@ internal void Next(MessageBuilder msgBuilder)
}
else
{
- Log.OnEvent(uvx.ToString());
+ Log.Log(LogLevel.Error, uvx, "{Message}", uvx.ToString());
GenerateLogout(uvx.Message);
_state.IncrNextTargetMsgSeqNum();
}
}
catch (UnsupportedMessageType e)
{
- Log.OnEvent("Unsupported message type: " + e.Message);
+ Log.Log(LogLevel.Error, e, "Unsupported message type: {Message}", e.Message);
GenerateBusinessMessageReject(message!, Fields.BusinessRejectReason.UNKNOWN_MESSAGE_TYPE, 0);
}
catch (FieldNotFoundException e)
{
- Log.OnEvent("Rejecting invalid message, field not found: " + e.Message);
+ Log.Log(LogLevel.Information, e, "Rejecting invalid message, field not found: {Message}", e.Message);
if (string.CompareOrdinal(SessionID.BeginString, FixValues.BeginString.FIX42) >= 0 && message!.IsApp())
{
GenerateBusinessMessageReject(message, Fields.BusinessRejectReason.CONDITIONALLY_REQUIRED_FIELD_MISSING, e.Field);
@@ -642,7 +672,7 @@ internal void Next(MessageBuilder msgBuilder)
{
if (MsgType.LOGON.Equals(msgBuilder.MsgType.Value))
{
- Log.OnEvent("Required field missing from logon");
+ Log.Log(LogLevel.Warning, "Required field missing from logon");
Disconnect("Required field missing from logon");
}
else
@@ -664,7 +694,7 @@ protected void NextLogon(Message logon)
if (_state.ReceivedReset)
{
- Log.OnEvent("Sequence numbers reset due to ResetSeqNumFlag=Y");
+ Log.Log(LogLevel.Warning, "Sequence numbers reset due to ResetSeqNumFlag=Y");
if (!_state.SentReset)
{
_state.Reset("Reset requested by counterparty");
@@ -681,19 +711,19 @@ protected void NextLogon(Message logon)
if (!IsGoodTime(logon))
{
- Log.OnEvent("Logon has bad sending time");
+ Log.Log(LogLevel.Error, "Logon has bad sending time");
Disconnect("bad sending time");
return;
}
_state.ReceivedLogon = true;
- Log.OnEvent("Received logon");
+ Log.Log(LogLevel.Warning, "Received logon");
if (IsAcceptor)
{
int heartBtInt = logon.GetInt(Fields.Tags.HeartBtInt);
_state.HeartBtInt = heartBtInt;
GenerateLogon(logon);
- Log.OnEvent($"Responding to logon request; heartbeat is {heartBtInt} seconds");
+ Log.Log(LogLevel.Warning, $"Responding to logon request; heartbeat is {heartBtInt} seconds");
}
_state.SentReset = false;
@@ -731,7 +761,7 @@ protected void NextResendRequest(Message resendReq)
{
SeqNumType begSeqNo = resendReq.GetULong(Fields.Tags.BeginSeqNo);
SeqNumType endSeqNo = resendReq.GetULong(Fields.Tags.EndSeqNo);
- Log.OnEvent("Got resend request from " + begSeqNo + " to " + endSeqNo);
+ Log.Log(LogLevel.Information, "Got resend request from {BeginSeqNo} to {EndSeqNo}", begSeqNo, endSeqNo);
if (endSeqNo == 999999 || endSeqNo == 0)
{
@@ -819,7 +849,7 @@ protected void NextResendRequest(Message resendReq)
}
catch (Exception e)
{
- Log.OnEvent("ERROR during resend request " + e.Message);
+ Log.Log(LogLevel.Error, e, "ERROR during resend request {Message}", e.Message);
}
}
private bool ResendApproved(Message msg, SessionID sessionId)
@@ -846,14 +876,14 @@ protected void NextLogout(Message logout)
if (!_state.SentLogout)
{
disconnectReason = "Received logout request";
- Log.OnEvent(disconnectReason);
+ Log.Log(LogLevel.Debug, "{Message}", disconnectReason);
GenerateLogout(logout);
- Log.OnEvent("Sending logout response");
+ Log.Log(LogLevel.Debug, "Sending logout response");
}
else
{
disconnectReason = "Received logout response";
- Log.OnEvent(disconnectReason);
+ Log.Log(LogLevel.Debug, "{Message}", disconnectReason);
}
_state.IncrNextTargetMsgSeqNum();
@@ -881,7 +911,7 @@ protected void NextSequenceReset(Message sequenceReset)
if (sequenceReset.IsSetField(Fields.Tags.NewSeqNo))
{
SeqNumType newSeqNo = sequenceReset.GetULong(Fields.Tags.NewSeqNo);
- Log.OnEvent("Received SequenceReset FROM: " + _state.NextTargetMsgSeqNum + " TO: " + newSeqNo);
+ Log.Log(LogLevel.Debug, "Received SequenceRequest FROM: {NextTargetMsgSeqNum} TO: {NewSeqNo}", _state.NextTargetMsgSeqNum, newSeqNo);
if (newSeqNo > _state.NextTargetMsgSeqNum)
{
@@ -932,12 +962,14 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
ResendRange range = _state.GetResendRange();
if (msgSeqNum >= range.EndSeqNo)
{
- Log.OnEvent("ResendRequest for messages FROM: " + range.BeginSeqNo + " TO: " + range.EndSeqNo + " has been satisfied.");
+ Log.Log(LogLevel.Debug, "ResendRequest for messages FROM: {BeginSeqNo} TO: {EndSeqNo} has been satisfied.", range.BeginSeqNo, range.EndSeqNo);
_state.SetResendRange(0, 0);
}
else if (msgSeqNum >= range.ChunkEndSeqNo)
{
- Log.OnEvent("Chunked ResendRequest for messages FROM: " + range.BeginSeqNo + " TO: " + range.ChunkEndSeqNo + " has been satisfied.");
+ Log.Log(LogLevel.Warning,
+ "Chunked ResendRequest for messages FROM: {BeginSeqNo} TO: {EndSeqNo} has been satisfied.",
+ range.BeginSeqNo, range.ChunkEndSeqNo);
SeqNumType newChunkEndSeqNo = Math.Min(range.EndSeqNo, range.ChunkEndSeqNo + MaxMessagesInResendRequest);
GenerateResendRequestRange(msg.Header.GetString(Fields.Tags.BeginString), range.ChunkEndSeqNo + 1, newChunkEndSeqNo);
range.ChunkEndSeqNo = newChunkEndSeqNo;
@@ -946,7 +978,7 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
if (!IsGoodTime(msg))
{
- Log.OnEvent("Sending time accuracy problem");
+ Log.Log(LogLevel.Warning, "Sending time accuracy problem");
GenerateReject(msg, FixValues.SessionRejectReason.SENDING_TIME_ACCURACY_PROBLEM);
GenerateLogout();
return false;
@@ -954,7 +986,7 @@ public bool Verify(Message msg, bool checkTooHigh = true, bool checkTooLow = tru
}
catch (Exception e)
{
- Log.OnEvent("Verify failed: " + e.Message);
+ Log.Log(LogLevel.Error, e, "Verify failed: {Message}", e.Message);
Disconnect("Verify failed: " + e.Message);
return false;
}
@@ -1044,7 +1076,7 @@ protected void DoTargetTooHigh(Message msg, SeqNumType msgSeqNum)
{
string beginString = msg.Header.GetString(Fields.Tags.BeginString);
- Log.OnEvent("MsgSeqNum too high, expecting " + _state.NextTargetMsgSeqNum + " but received " + msgSeqNum);
+ Log.Log(LogLevel.Warning, "MsgSeqNum too high, expecting {NextSeqNum} but received {MsgSeqNum}", _state.NextTargetMsgSeqNum, msgSeqNum);
_state.Queue(msgSeqNum, msg);
if (_state.ResendRequested())
@@ -1053,7 +1085,7 @@ protected void DoTargetTooHigh(Message msg, SeqNumType msgSeqNum)
if (!SendRedundantResendRequests && msgSeqNum >= range.BeginSeqNo)
{
- Log.OnEvent("Already sent ResendRequest FROM: " + range.BeginSeqNo + " TO: " + range.EndSeqNo + ". Not sending another.");
+ Log.Log(LogLevel.Debug, "Already sent ResendRequest FROM: {BeginSeqNo} TO: {EndSeqNo}. Not sending another.", range.BeginSeqNo, range.EndSeqNo);
return;
}
}
@@ -1133,7 +1165,7 @@ protected void GenerateBusinessMessageReject(Message message, int err, int field
reject.SetField(new Text(reason));
- Log.OnEvent("Reject sent for Message: " + msgSeqNum + " Reason:" + reason);
+ Log.Log(LogLevel.Warning, "Reject sent for Message: {MsgSeqNum} Reason: {Reason}", msgSeqNum, reason);
SendRaw(reject, 0);
}
@@ -1147,11 +1179,11 @@ protected bool GenerateResendRequestRange(string beginString, SeqNumType startSe
InitializeHeader(resendRequest);
if (SendRaw(resendRequest, 0))
{
- Log.OnEvent("Sent ResendRequest FROM: " + startSeqNum + " TO: " + endSeqNum);
+ Log.Log(LogLevel.Debug, "Sent ResendRequest FROM: {StartSeqNum} TO: {EndSeqNum}", startSeqNum, endSeqNum);
return true;
}
- Log.OnEvent("Error sending ResendRequest (" + startSeqNum + " ," + endSeqNum + ")");
+ Log.Log(LogLevel.Error, "Error sending ResendRequest ({StartSeqNum}, {EndSeqNum})", startSeqNum, endSeqNum);
return false;
}
@@ -1277,9 +1309,9 @@ private void ImplGenerateLogout(Message? other = null, string? text = null)
{
logout.Header.SetField(new Fields.LastMsgSeqNumProcessed(other.Header.GetULong(Tags.MsgSeqNum)));
}
- catch (FieldNotFoundException)
+ catch (FieldNotFoundException e)
{
- Log.OnEvent("Error: No message sequence number: " + other);
+ Log.Log(LogLevel.Error, e, "Error: No message sequence number: {Other}", other);
}
}
_state.SentLogout = SendRaw(logout, 0);
@@ -1335,7 +1367,7 @@ public void GenerateReject(Message message, FixValues.SessionRejectReason reason
}
catch (Exception ex)
{
- Log.OnEvent($"Exception while setting RefSeqNum: {ex}");
+ Log.Log(LogLevel.Error, ex, "Exception while setting RefSeqNum: {Exception}", ex);
}
}
@@ -1368,12 +1400,12 @@ public void GenerateReject(Message message, FixValues.SessionRejectReason reason
else
PopulateSessionRejectReason(reject, field, reason.Description, true);
- Log.OnEvent("Message " + msgSeqNum + " Rejected: " + reason.Description + " (Field=" + field + ")");
+ Log.Log(LogLevel.Warning, "Message {MsgSeqNum} Rejected: {Reason} (Field={Field})", msgSeqNum, reason.Description, field);
}
else
{
PopulateRejectReason(reject, reason.Description);
- Log.OnEvent("Message " + msgSeqNum + " Rejected: " + reason.Value);
+ Log.Log(LogLevel.Error, "Message {MsgSeqNum} Rejected: {Reason}", msgSeqNum, reason.Value);
}
if (!_state.ReceivedLogon)
@@ -1478,13 +1510,13 @@ private void GenerateSequenceReset(Message receivedMessage, SeqNumType beginSeqN
{
sequenceReset.Header.SetField(new Fields.LastMsgSeqNumProcessed(receivedMessage.Header.GetULong(Tags.MsgSeqNum)));
}
- catch (FieldNotFoundException)
+ catch (FieldNotFoundException e)
{
- Log.OnEvent("Error: Received message without MsgSeqNum: " + receivedMessage);
+ Log.Log(LogLevel.Error, e, "Error: Received message without MsgSeqNum: {ReceivedMessage}", receivedMessage);
}
}
SendRaw(sequenceReset, beginSeqNo);
- Log.OnEvent("Sent SequenceReset TO: " + newSeqNo);
+ Log.Log(LogLevel.Debug, "Sent SequenceReset TO: {NewSeqNo}", newSeqNo);
}
protected void InsertOrigSendingTime(FieldMap header, DateTime sendingTime)
@@ -1507,7 +1539,7 @@ protected bool NextQueued(SeqNumType num)
if (msg is not null)
{
- Log.OnEvent("Processing queued message: " + num);
+ Log.Log(LogLevel.Debug, "Processing queued message: {Num}", num);
string msgType = msg.Header.GetString(Tags.MsgType);
if (msgType.Equals(MsgType.LOGON) || msgType.Equals(MsgType.RESEND_REQUEST))
diff --git a/QuickFIXn/SessionFactory.cs b/QuickFIXn/SessionFactory.cs
index 867aeb170..83f408b52 100755
--- a/QuickFIXn/SessionFactory.cs
+++ b/QuickFIXn/SessionFactory.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Store;
using QuickFix.Util;
@@ -13,14 +14,14 @@ public class SessionFactory
{
protected IApplication _application;
protected IMessageStoreFactory _messageStoreFactory;
- protected ILogFactory _logFactory;
+ protected ILoggerFactory _loggerFactory;
protected IMessageFactory _messageFactory;
protected Dictionary _dictionariesByPath = new();
public SessionFactory(
IApplication app,
IMessageStoreFactory storeFactory,
- ILogFactory? logFactory = null,
+ ILoggerFactory? loggerFactory = null,
IMessageFactory? messageFactory = null)
{
// TODO: for V2, consider ONLY instantiating MessageFactory in the Create() method,
@@ -31,7 +32,7 @@ public SessionFactory(
_application = app;
_messageStoreFactory = storeFactory;
- _logFactory = logFactory ?? new NullLogFactory();
+ _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance;
_messageFactory = messageFactory ?? new DefaultMessageFactory();
}
@@ -107,7 +108,7 @@ public Session Create(SessionID sessionId, SettingsDictionary settings)
dd,
new SessionSchedule(settings),
heartBtInt,
- _logFactory,
+ _loggerFactory,
sessionMsgFactory,
senderDefaultApplVerId);
diff --git a/QuickFIXn/SessionState.cs b/QuickFIXn/SessionState.cs
index 76206f8e6..97462a7eb 100755
--- a/QuickFIXn/SessionState.cs
+++ b/QuickFIXn/SessionState.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
using QuickFix.Store;
using MessagesBySeqNum = System.Collections.Generic.Dictionary;
@@ -46,7 +46,7 @@ public bool IsInitiator
public bool ShouldSendLogon => IsInitiator && !SentLogon;
- public ILog Log { get; }
+ public ILogger Log { get; }
#endregion
@@ -154,9 +154,9 @@ private MessagesBySeqNum MsgQueue
#endregion
- public SessionState(bool isInitiator, ILog log, int heartBtInt, IMessageStore messageStore)
+ public SessionState(bool isInitiator, ILogger logger, int heartBtInt, IMessageStore messageStore)
{
- Log = log;
+ Log = logger;
HeartBtInt = heartBtInt;
IsInitiator = isInitiator;
_lastReceivedTimeDt = DateTime.UtcNow;
@@ -395,7 +395,7 @@ public void Reset(string reason)
lock (_sync)
{
MessageStore.Reset();
- Log.OnEvent("Session reset: " + reason);
+ Log.Log(LogLevel.Debug, "Session reset: {Reason}", reason);
}
}
@@ -418,7 +418,6 @@ protected virtual void Dispose(bool disposing)
if (_disposed) return;
if (disposing)
{
- Log.Dispose();
MessageStore.Dispose();
}
_disposed = true;
diff --git a/QuickFIXn/SocketInitiatorThread.cs b/QuickFIXn/SocketInitiatorThread.cs
index f37669493..b78aa7158 100755
--- a/QuickFIXn/SocketInitiatorThread.cs
+++ b/QuickFIXn/SocketInitiatorThread.cs
@@ -5,7 +5,7 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
namespace QuickFix
{
@@ -16,7 +16,7 @@ public class SocketInitiatorThread : IResponder
{
public Session Session { get; }
public Transport.SocketInitiator Initiator { get; }
- public NonSessionLog NonSessionLog { get; }
+ public ILogger NonSessionLog { get; }
public const int BUF_SIZE = 512;
@@ -39,7 +39,7 @@ public SocketInitiatorThread(
Session session,
IPEndPoint socketEndPoint,
SocketSettings socketSettings,
- NonSessionLog nonSessionLog)
+ ILogger nonSessionLog)
{
Initiator = initiator;
Session = session;
@@ -105,7 +105,7 @@ public bool Read()
}
catch (Exception e)
{
- Session.Log.OnEvent(e.ToString());
+ Session.Log.Log(LogLevel.Error, e, "{Exception}", e);
Disconnect();
}
return false;
diff --git a/QuickFIXn/SocketReader.cs b/QuickFIXn/SocketReader.cs
index 32ea6a332..b92007c3d 100755
--- a/QuickFIXn/SocketReader.cs
+++ b/QuickFIXn/SocketReader.cs
@@ -4,6 +4,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
using QuickFix.Logger;
namespace QuickFix
@@ -19,7 +20,15 @@ public class SocketReader : IDisposable
private readonly TcpClient _tcpClient;
private readonly ClientHandlerThread _responder;
private readonly AcceptorSocketDescriptor? _acceptorDescriptor;
- private readonly NonSessionLog _nonSessionLog;
+ private readonly ILogger _nonSessionLog;
+ private ILogger UnconditionalLogger
+ {
+ get
+ {
+ if (_qfSession?.Log is { } logger) return logger;
+ return _nonSessionLog;
+ }
+ }
///
/// Keep a task for handling async read
@@ -31,7 +40,7 @@ internal SocketReader(
SocketSettings settings,
ClientHandlerThread responder,
AcceptorSocketDescriptor? acceptorDescriptor,
- NonSessionLog nonSessionLog)
+ ILogger nonSessionLog)
{
_tcpClient = tcpClient;
_responder = responder;
@@ -124,21 +133,26 @@ private void OnMessageFound(string msg)
if (_qfSession is null || IsUnknownSession(_qfSession.SessionID))
{
_qfSession = null;
- _nonSessionLog.OnEvent("ERROR: Disconnecting; received message for unknown session: " + msg);
+ _nonSessionLog.Log(LogLevel.Error,
+ "ERROR: Disconnecting; received message for unknown session: {Message}", msg);
DisconnectClient();
return;
}
if (_qfSession.HasResponder)
{
- _qfSession.Log.OnIncoming(msg);
- _qfSession.Log.OnEvent("Multiple logons/connections for this session are not allowed (" + _tcpClient.Client.RemoteEndPoint + ")");
+ _qfSession.Log.Log(LogLevel.Information, LogEventIds.IncomingMessage, "{Message}", msg);
+ _qfSession.Log.Log(LogLevel.Error,
+ "Multiple logons/connections for this session are not allowed ({Endpoint})",
+ _tcpClient.Client.RemoteEndPoint);
_qfSession = null;
DisconnectClient();
return;
}
- _qfSession.Log.OnEvent(_qfSession.SessionID + " Socket Reader " + GetHashCode() + " accepting session " + _qfSession.SessionID + " from " + _tcpClient.Client.RemoteEndPoint);
+ _qfSession.Log.Log(LogLevel.Debug,
+ "{SessionId} Socket Reader {ReaderId} accepting session {AcceptedSessionId} from {Endpoint}",
+ _qfSession.SessionID, GetHashCode(), _qfSession.SessionID, _tcpClient.Client.RemoteEndPoint);
_qfSession.SetResponder(_responder);
}
@@ -148,7 +162,8 @@ private void OnMessageFound(string msg)
}
catch (Exception e)
{
- _qfSession.Log.OnEvent($"Error on Session '{_qfSession.SessionID}': {e}");
+ _qfSession.Log.Log(LogLevel.Error, e, "Error on Session '{SessionId}': {Exception}",
+ _qfSession.SessionID, e);
}
}
/*
@@ -172,12 +187,13 @@ protected void HandleBadMessage(string msg, Exception e)
{
if (Fields.MsgType.LOGON.Equals(Message.GetMsgType(msg)))
{
- LogEvent($"ERROR: Invalid LOGON message, disconnecting: {e.Message}");
+ UnconditionalLogger.Log(LogLevel.Error, e, "ERROR: Invalid LOGON message, disconnecting: {Message}",
+ e.Message);
DisconnectClient();
}
else
{
- LogEvent($"ERROR: Invalid message: {e.Message}");
+ UnconditionalLogger.Log(LogLevel.Error, e, "ERROR: Invalid message: {Message}", e.Message);
}
}
catch (InvalidMessage)
@@ -246,7 +262,7 @@ private void HandleExceptionInternal(Session? quickFixSession, Exception cause)
break;
}
- LogEvent($"SocketReader Error: {reason}");
+ UnconditionalLogger.Log(LogLevel.Error, realCause, "SocketReader Error: {Reason}", reason);
if (disconnectNeeded)
{
@@ -257,18 +273,6 @@ private void HandleExceptionInternal(Session? quickFixSession, Exception cause)
}
}
- ///
- /// Log event to session log if session is known, else to nonSessionLog
- ///
- ///
- private void LogEvent(string s)
- {
- if(_qfSession is not null)
- _qfSession.Log.OnEvent(s);
- else
- _nonSessionLog.OnEvent(s);
- }
-
public int Send(string data)
{
byte[] rawData = CharEncoding.DefaultEncoding.GetBytes(data);
diff --git a/QuickFIXn/ThreadedSocketAcceptor.cs b/QuickFIXn/ThreadedSocketAcceptor.cs
index 2f3e69daf..b6bab13be 100755
--- a/QuickFIXn/ThreadedSocketAcceptor.cs
+++ b/QuickFIXn/ThreadedSocketAcceptor.cs
@@ -2,6 +2,8 @@
using System.Linq;
using System.Net;
using System;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Logger;
using QuickFix.Store;
@@ -20,10 +22,10 @@ public class ThreadedSocketAcceptor : IAcceptor
private bool _isStarted = false;
private bool _disposed = false;
private readonly object _sync = new();
- private readonly NonSessionLog _nonSessionLog;
+ private readonly ILogger _nonSessionLog;
+ private readonly LogFactoryAdapter? _logFactoryAdapter;
#region Constructors
-
///
/// Create a ThreadedSocketAcceptor
///
@@ -32,18 +34,46 @@ public class ThreadedSocketAcceptor : IAcceptor
///
/// If null, a NullFactory will be used.
/// If null, a DefaultMessageFactory will be created (using settings parameters)
+ [Obsolete("Use \"Microsoft.Extensions.Logging.ILoggerFactory\" instead of \"QuickFix.Logger.ILogFactory\".")]
public ThreadedSocketAcceptor(
IApplication application,
IMessageStoreFactory storeFactory,
SessionSettings settings,
ILogFactory? logFactory = null,
+ IMessageFactory? messageFactory = null) : this(application, storeFactory, settings,
+ logFactory is null ? NullLoggerFactory.Instance : new LogFactoryAdapter(logFactory, settings),
+ messageFactory)
+ {
+ }
+
+ ///
+ /// Create a ThreadedSocketAcceptor
+ ///
+ ///
+ ///
+ ///
+ /// If null, a NullFactory will be used.
+ /// If null, a DefaultMessageFactory will be created (using settings parameters)
+ public ThreadedSocketAcceptor(
+ IApplication application,
+ IMessageStoreFactory storeFactory,
+ SessionSettings settings,
+ ILoggerFactory? loggerFactory = null,
IMessageFactory? messageFactory = null)
{
- ILogFactory lf = logFactory ?? new NullLogFactory();
+ var lf = loggerFactory ?? NullLoggerFactory.Instance;
+ if (lf is LogFactoryAdapter lfa)
+ {
+ // LogFactoryAdapter is only ever created in the constructor marked obsolete, which means we own it and
+ // must save a ref to it so we can dispose it later. Any other ILoggerFactory is owned by someone else
+ // so we'll leave the dispose up to them. This should be removed eventually together with the old ILog
+ // and ILogFactory.
+ _logFactoryAdapter = lfa;
+ }
IMessageFactory mf = messageFactory ?? new DefaultMessageFactory();
_settings = settings;
_sessionFactory = new SessionFactory(application, storeFactory, lf, mf);
- _nonSessionLog = new NonSessionLog(lf);
+ _nonSessionLog = lf.CreateLogger("QuickFix");
try
{
@@ -176,7 +206,8 @@ private void LogoutAllSessions(bool force)
}
catch (Exception e)
{
- session.Log.OnEvent($"Error during logout of Session {session.SessionID}: {e.Message}");
+ session.Log.Log(LogLevel.Error, e, "Error during logout of Session {SessionID}: {Message}",
+ session.SessionID, e.Message);
}
}
@@ -191,7 +222,8 @@ private void LogoutAllSessions(bool force)
}
catch (Exception e)
{
- session.Log.OnEvent($"Error during disconnect of Session {session.SessionID}: {e.Message}");
+ session.Log.Log(LogLevel.Error, e, "Error during disconnect of Session {SessionID}: {Message}",
+ session.SessionID, e.Message);
}
}
}
@@ -274,8 +306,8 @@ public void Stop(bool force)
LogoutAllSessions(force);
DisposeSessions();
_sessions.Clear();
- _nonSessionLog.Dispose();
_isStarted = false;
+ _logFactoryAdapter?.Dispose();
// FIXME StopSessionTimer();
// FIXME Session.UnregisterSessions(GetSessions());
diff --git a/QuickFIXn/ThreadedSocketReactor.cs b/QuickFIXn/ThreadedSocketReactor.cs
index daaceec06..b342f46a4 100755
--- a/QuickFIXn/ThreadedSocketReactor.cs
+++ b/QuickFIXn/ThreadedSocketReactor.cs
@@ -3,7 +3,7 @@
using System.Net.Sockets;
using System.Threading;
using System;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
namespace QuickFix
{
@@ -31,13 +31,13 @@ public State ReactorState
private readonly SocketSettings _socketSettings;
private readonly IPEndPoint _serverSocketEndPoint;
private readonly AcceptorSocketDescriptor? _acceptorSocketDescriptor;
- private readonly NonSessionLog _nonSessionLog;
+ private readonly ILogger _nonSessionLog;
internal ThreadedSocketReactor(
IPEndPoint serverSocketEndPoint,
SocketSettings socketSettings,
AcceptorSocketDescriptor? acceptorSocketDescriptor,
- NonSessionLog nonSessionLog)
+ ILogger nonSessionLog)
{
_socketSettings = socketSettings;
_serverSocketEndPoint = serverSocketEndPoint;
@@ -211,7 +211,14 @@ private void ShutdownClientHandlerThreads()
///
///
private void LogError(string s, Exception? ex = null) {
- _nonSessionLog.OnEvent(ex is null ? $"{s}" : $"{s}: {ex}");
+ if (ex is null)
+ {
+ _nonSessionLog.LogError("{Message}", s);
+ }
+ else
+ {
+ _nonSessionLog.LogError(ex, "{Message}: {Error}", s, ex);
+ }
}
}
}
diff --git a/QuickFIXn/Transport/SocketInitiator.cs b/QuickFIXn/Transport/SocketInitiator.cs
index 30984e86b..66ec081f3 100644
--- a/QuickFIXn/Transport/SocketInitiator.cs
+++ b/QuickFIXn/Transport/SocketInitiator.cs
@@ -5,6 +5,7 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
+using Microsoft.Extensions.Logging;
using QuickFix.Logger;
using QuickFix.Store;
@@ -26,7 +27,8 @@ public class SocketInitiator : AbstractInitiator
private readonly Dictionary _threads = new();
private readonly Dictionary _sessionToHostNum = new();
private readonly object _sync = new();
-
+
+ [Obsolete("Use \"Microsoft.Extensions.Logging.ILoggerFactory\" instead of \"QuickFix.Logger.ILogFactory\".")]
public SocketInitiator(
IApplication application,
IMessageStoreFactory storeFactory,
@@ -36,6 +38,15 @@ public SocketInitiator(
: base(application, storeFactory, settings, logFactoryNullable, messageFactoryNullable)
{ }
+ public SocketInitiator(
+ IApplication application,
+ IMessageStoreFactory storeFactory,
+ SessionSettings settings,
+ ILoggerFactory? logFactoryNullable = null,
+ IMessageFactory? messageFactoryNullable = null)
+ : base(application, storeFactory, settings, logFactoryNullable, messageFactoryNullable)
+ { }
+
public static void SocketInitiatorThreadStart(object? socketInitiatorThread)
{
SocketInitiatorThread? t = socketInitiatorThread as SocketInitiatorThread;
@@ -45,7 +56,7 @@ public static void SocketInitiatorThreadStart(object? socketInitiatorThread)
{
t.Connect();
t.Initiator.SetConnected(t.Session.SessionID);
- t.Session.Log.OnEvent("Connection succeeded");
+ t.Session.Log.Log(LogLevel.Debug, "Connection succeeded");
t.Session.Next();
while (t.Read()) {
}
@@ -78,11 +89,13 @@ public static void SocketInitiatorThreadStart(object? socketInitiatorThread)
}
private static void LogThreadStartConnectionFailed(SocketInitiatorThread t, Exception e) {
- if (t.Session.Disposed) {
- t.NonSessionLog.OnEvent($"Connection failed [session {t.Session.SessionID}]: {e}");
+ if (t.Session.Disposed)
+ {
+ t.NonSessionLog.Log(LogLevel.Error, e, "Connection failed [session {SessionID}]: {Message}",
+ t.Session.SessionID, e);
return;
}
- t.Session.Log.OnEvent($"Connection failed: {e}");
+ t.Session.Log.Log(LogLevel.Error, e, "Connection failed: {Error}", e);
}
private void AddThread(SocketInitiatorThread thread)
@@ -185,7 +198,7 @@ protected override void OnStart()
}
catch (Exception e)
{
- _nonSessionLog.OnEvent($"Failed to start: {e}");
+ _nonSessionLog.Log(LogLevel.Error, e, "Failed to start: {Error}", e);
}
Thread.Sleep(1 * 1000);
@@ -220,7 +233,7 @@ protected override void DoConnect(Session session, SettingsDictionary settings)
IPEndPoint socketEndPoint = GetNextSocketEndPoint(session.SessionID, settings);
SetPending(session.SessionID);
- session.Log.OnEvent($"Connecting to {socketEndPoint.Address} on port {socketEndPoint.Port}");
+ session.Log.Log(LogLevel.Debug, "Connecting to {Address} on port {Port}", socketEndPoint.Address, socketEndPoint.Port);
//Setup socket settings based on current section
var socketSettings = _socketSettings.Clone();
@@ -233,7 +246,7 @@ protected override void DoConnect(Session session, SettingsDictionary settings)
AddThread(t);
}
catch (Exception e) {
- session.Log.OnEvent(e.Message);
+ session.Log.Log(LogLevel.Error, e, "{Exception}", e);
}
}
diff --git a/QuickFIXn/Transport/SslStreamFactory.cs b/QuickFIXn/Transport/SslStreamFactory.cs
index b5c5e9d64..6ed6a4ef4 100644
--- a/QuickFIXn/Transport/SslStreamFactory.cs
+++ b/QuickFIXn/Transport/SslStreamFactory.cs
@@ -4,6 +4,7 @@
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.Logging;
using QuickFix.Logger;
using QuickFix.Util;
@@ -15,11 +16,11 @@ namespace QuickFix.Transport;
internal sealed class SslStreamFactory
{
private readonly SocketSettings _socketSettings;
- private readonly NonSessionLog _nonSessionLog;
+ private readonly ILogger _nonSessionLog;
private const string CLIENT_AUTHENTICATION_OID = "1.3.6.1.5.5.7.3.2";
private const string SERVER_AUTHENTICATION_OID = "1.3.6.1.5.5.7.3.1";
- public SslStreamFactory(SocketSettings settings, NonSessionLog nonSessionLog)
+ public SslStreamFactory(SocketSettings settings, ILogger nonSessionLog)
{
_socketSettings = settings;
_nonSessionLog = nonSessionLog;
@@ -56,7 +57,8 @@ public Stream CreateClientStreamAndAuthenticate(Stream innerStream)
}
catch (AuthenticationException ex)
{
- _nonSessionLog.OnEvent($"Unable to perform authentication against server: {ex.GetFullMessage()}");
+ _nonSessionLog.Log(LogLevel.Error, ex, "Unable to perform authentication against server: {Message}",
+ ex.GetFullMessage());
throw;
}
@@ -101,7 +103,8 @@ public Stream CreateServerStreamAndAuthenticate(Stream innerStream)
}
catch (AuthenticationException ex)
{
- _nonSessionLog.OnEvent($"Unable to perform authentication against server: {ex.GetFullMessage()}");
+ _nonSessionLog.Log(LogLevel.Error, ex, "Unable to perform authentication against server: {Message}",
+ ex.GetFullMessage());
throw;
}
@@ -171,13 +174,14 @@ private bool VerifyRemoteCertificate(
// Validate enhanced key usage
if (!ContainsEnhancedKeyUsage(certificate, enhancedKeyUsage)) {
var role = enhancedKeyUsage == CLIENT_AUTHENTICATION_OID ? "client" : "server";
- _nonSessionLog.OnEvent(
- $"Remote certificate is not intended for {role} authentication: It is missing enhanced key usage {enhancedKeyUsage}");
+ _nonSessionLog.Log(LogLevel.Warning,
+ "Remote certificate is not intended for {Role} authentication: It is missing enhanced key usage {KeyUsage}",
+ role, enhancedKeyUsage);
return false;
}
if (string.IsNullOrEmpty(_socketSettings.CACertificatePath)) {
- _nonSessionLog.OnEvent("CACertificatePath is not specified");
+ _nonSessionLog.Log(LogLevel.Warning, "CACertificatePath is not specified");
return false;
}
@@ -185,9 +189,11 @@ private bool VerifyRemoteCertificate(
// If CA Certificate is specified then validate against the CA certificate, otherwise it is validated against the installed certificates
X509Certificate2? cert = SslCertCache.LoadCertificate(caCertPath, null);
- if (cert is null) {
- _nonSessionLog.OnEvent(
- $"Certificate '{caCertPath}' could not be loaded from store or path '{Directory.GetCurrentDirectory()}'");
+ if (cert is null)
+ {
+ _nonSessionLog.Log(LogLevel.Warning,
+ "Certificate '{CertificatePath}' could not be loaded from store or path '{Directory}'", caCertPath,
+ Directory.GetCurrentDirectory());
return false;
}
@@ -212,7 +218,8 @@ private bool VerifyRemoteCertificate(
// Any basic authentication check failed, do after checking CA
if (sslPolicyErrors != SslPolicyErrors.None)
{
- _nonSessionLog.OnEvent($"Remote certificate was not recognized as a valid certificate: {sslPolicyErrors}");
+ _nonSessionLog.Log(LogLevel.Warning,
+ "Remote certificate was not recognized as a valid certificate: {Errors}", sslPolicyErrors);
return false;
}
diff --git a/QuickFIXn/Transport/StreamFactory.cs b/QuickFIXn/Transport/StreamFactory.cs
index 66117a7d0..ea1f453d1 100644
--- a/QuickFIXn/Transport/StreamFactory.cs
+++ b/QuickFIXn/Transport/StreamFactory.cs
@@ -4,7 +4,7 @@
using System.Net;
using System.Net.Sockets;
using System.Text;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
namespace QuickFix.Transport
{
@@ -66,7 +66,7 @@ internal static class StreamFactory
/// The socket settings.
/// Logger that is not tied to a particular session
/// an opened and initiated stream which can be read and written to
- internal static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings settings, NonSessionLog nonSessionLog)
+ internal static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings settings, ILogger nonSessionLog)
{
Socket? socket = null;
@@ -116,7 +116,7 @@ internal static Stream CreateClientStream(IPEndPoint endpoint, SocketSettings se
/// Logger that is not tied to a particular session
/// an opened and initiated stream which can be read and written to
/// tcp client must be connected in order to get stream;tcpClient
- internal static Stream CreateServerStream(TcpClient tcpClient, SocketSettings settings, NonSessionLog nonSessionLog)
+ internal static Stream CreateServerStream(TcpClient tcpClient, SocketSettings settings, ILogger nonSessionLog)
{
if (tcpClient.Connected == false)
throw new ArgumentException("tcp client must be connected in order to get stream", nameof(tcpClient));
diff --git a/UnitTests/Logger/FileLogTests.cs b/UnitTests/Logger/FileLogTests.cs
index 9b0f6f980..d671f0066 100644
--- a/UnitTests/Logger/FileLogTests.cs
+++ b/UnitTests/Logger/FileLogTests.cs
@@ -1,4 +1,5 @@
using System.IO;
+using Microsoft.Extensions.Logging;
using NUnit.Framework;
using QuickFix.Logger;
@@ -57,12 +58,12 @@ public void TestGeneratedFileName()
settings.Set(sessionId, config);
- FileLogFactory factory = new FileLogFactory(settings);
- _log = (FileLog)factory.Create(sessionId);
+ var fileLogProvider = new FileLoggerProvider(settings);
+ _log = (FileLog) fileLogProvider.CreateLogger($"QuickFix.{sessionId}");
- _log.OnEvent("some event");
- _log.OnIncoming("some incoming");
- _log.OnOutgoing("some outgoing");
+ _log.Log(LogLevel.Debug, "some event");
+ _log.Log(LogLevel.Information, LogEventIds.IncomingMessage, "some incoming");
+ _log.Log(LogLevel.Information, LogEventIds.OutgoingMessage, "some outgoing");
Assert.That(File.Exists(Path.Combine(logDirectory, "FIX.4.2-SENDERCOMP-TARGETCOMP.event.current.log")));
Assert.That(File.Exists(Path.Combine(logDirectory, "FIX.4.2-SENDERCOMP-TARGETCOMP.messages.current.log")));
@@ -82,8 +83,8 @@ public void TestThrowsIfNoConfig()
QuickFix.SessionSettings settings = new QuickFix.SessionSettings();
settings.Set(sessionId, config);
- FileLogFactory factory = new FileLogFactory(settings);
+ using var loggerFactory = new LoggerFactory([new FileLoggerProvider(settings)]);
- Assert.Throws(delegate { factory.Create(sessionId); });
+ Assert.Throws(() => loggerFactory.CreateLogger($"QuickFix.{sessionId}"));
}
}
diff --git a/UnitTests/SessionDynamicTest.cs b/UnitTests/SessionDynamicTest.cs
index 5db1598b1..88d768268 100644
--- a/UnitTests/SessionDynamicTest.cs
+++ b/UnitTests/SessionDynamicTest.cs
@@ -5,7 +5,7 @@
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
-
+using Microsoft.Extensions.Logging;
using NUnit.Framework;
using QuickFix;
using QuickFix.Logger;
@@ -73,6 +73,7 @@ public SocketState(Socket s)
private string _logPath = "unset";
private SocketInitiator? _initiator;
private ThreadedSocketAcceptor? _acceptor;
+ private ILoggerFactory? _loggerFactory;
private Dictionary _sessions = new();
private HashSet _loggedOnCompIDs = new();
private Socket? _listenSocket;
@@ -127,13 +128,13 @@ private void StartEngine(bool initiator, bool twoSessions = false)
defaults.SetString(SessionSettings.SOCKET_ACCEPT_PORT, AcceptPort.ToString());
settings.Set(defaults);
- ILogFactory logFactory = new FileLogFactory(settings);
+ _loggerFactory = new LoggerFactory([new FileLoggerProvider(settings)]);
if (initiator)
{
defaults.SetString(SessionSettings.RECONNECT_INTERVAL, "1");
settings.Set(CreateSessionId(StaticInitiatorCompId), CreateSessionConfig(true));
- _initiator = new SocketInitiator(application, storeFactory, settings, logFactory);
+ _initiator = new SocketInitiator(application, storeFactory, settings, _loggerFactory);
_initiator.Start();
}
else
@@ -151,7 +152,7 @@ private void StartEngine(bool initiator, bool twoSessions = false)
settings.Set(id, conf);
}
- _acceptor = new ThreadedSocketAcceptor(application, storeFactory, settings, logFactory);
+ _acceptor = new ThreadedSocketAcceptor(application, storeFactory, settings, _loggerFactory);
_acceptor.Start();
}
}
@@ -361,9 +362,11 @@ public void TearDown()
_listenSocket?.Close();
_initiator?.Stop(true);
_acceptor?.Stop(true);
+ _loggerFactory?.Dispose();
_initiator = null;
_acceptor = null;
+ _loggerFactory = null;
Thread.Sleep(500);
ClearLogs();
diff --git a/UnitTests/SessionStateTest.cs b/UnitTests/SessionStateTest.cs
index 90f912333..83e713470 100755
--- a/UnitTests/SessionStateTest.cs
+++ b/UnitTests/SessionStateTest.cs
@@ -1,11 +1,10 @@
-using System;
-using NUnit.Framework;
+using NUnit.Framework;
using QuickFix;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Store;
namespace UnitTests
@@ -151,10 +150,8 @@ public void ThreadSafeSetAndGet() {
FileStore store = (FileStore)factory.Create(sessionId);
- NullLog log = new NullLog();
-
//Set up sessionstate
- SessionState state = new SessionState(true, log, 1, store);
+ SessionState state = new SessionState(true, NullLogger.Instance, 1, store);
Hashtable errorsTable = Hashtable.Synchronized(new Hashtable());//used in more than 1 thread at a time
Hashtable setTable = new Hashtable(1000);//only used in 1 thread at a time
diff --git a/UnitTests/SessionTest.cs b/UnitTests/SessionTest.cs
index afe1762f7..fce5be3ae 100755
--- a/UnitTests/SessionTest.cs
+++ b/UnitTests/SessionTest.cs
@@ -4,7 +4,8 @@
using System.Text.RegularExpressions;
using NUnit.Framework;
using System.Threading;
-using QuickFix.Logger;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
using QuickFix.Store;
namespace UnitTests;
@@ -39,17 +40,17 @@ public void Setup()
_config.SetString(QuickFix.SessionSettings.END_TIME, "00:00:00");
_settings.Set(_sessionId, _config);
- var logFactory = new NullLogFactory(); // use QuickFix.ScreenLogFactory(settings) if you need to see output
+ var loggerFactory = new LoggerFactory([NullLoggerProvider.Instance]); // use QuickFix.ScreenLogFactory(settings) if you need to see output
// acceptor
_session = new QuickFix.Session(false, _application, new MemoryStoreFactory(), _sessionId,
- new QuickFix.DataDictionaryProvider(),new QuickFix.SessionSchedule(_config), 0, logFactory, new QuickFix.DefaultMessageFactory(), "blah");
+ new QuickFix.DataDictionaryProvider(),new QuickFix.SessionSchedule(_config), 0, loggerFactory, new QuickFix.DefaultMessageFactory(), "blah");
_session.SetResponder(_responder);
_session.CheckLatency = false;
// initiator
_session2 = new QuickFix.Session(true, _application, new MemoryStoreFactory(), new QuickFix.SessionID("FIX.4.2", "OTHER_SENDER", "OTHER_TARGET"),
- new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(_config), 0, logFactory, new QuickFix.DefaultMessageFactory(), "blah");
+ new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(_config), 0, loggerFactory, new QuickFix.DefaultMessageFactory(), "blah");
_session2.SetResponder(_responder);
_session2.CheckLatency = false;
@@ -758,7 +759,8 @@ public void TestApplicationExtension()
{
var mockApp = new SessionTestSupport.MockApplicationExt();
_session = new QuickFix.Session(true, mockApp, new MemoryStoreFactory(), _sessionId,
- new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(_config), 0, new NullLogFactory(), new QuickFix.DefaultMessageFactory(), "blah");
+ new QuickFix.DataDictionaryProvider(), new QuickFix.SessionSchedule(_config), 0,
+ new LoggerFactory([NullLoggerProvider.Instance]), new QuickFix.DefaultMessageFactory(), "blah");
_session.SetResponder(_responder);
_session.CheckLatency = false;
diff --git a/UnitTests/ThreadedSocketAcceptorTests.cs b/UnitTests/ThreadedSocketAcceptorTests.cs
index 1f334b31c..77c6d039b 100644
--- a/UnitTests/ThreadedSocketAcceptorTests.cs
+++ b/UnitTests/ThreadedSocketAcceptorTests.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Text;
+using Microsoft.Extensions.Logging;
using NUnit.Framework;
using QuickFix;
using QuickFix.Logger;
@@ -35,16 +36,6 @@ private static SessionSettings CreateSettings()
return new SessionSettings(new StringReader(Config));
}
- private static ThreadedSocketAcceptor CreateAcceptor()
- {
- var settings = CreateSettings();
- return new ThreadedSocketAcceptor(
- new NullApplication(),
- new FileStoreFactory(settings),
- settings,
- new FileLogFactory(settings));
- }
-
[Test]
public void TestRecreation()
{
@@ -55,7 +46,14 @@ public void TestRecreation()
private static void StartStopAcceptor()
{
- var acceptor = CreateAcceptor();
+ var settings = CreateSettings();
+ using var lf = new LoggerFactory([new FileLoggerProvider(settings)]);
+
+ var acceptor = new ThreadedSocketAcceptor(
+ new NullApplication(),
+ new FileStoreFactory(settings),
+ settings,
+ lf);
acceptor.Start();
acceptor.Dispose();
}
diff --git a/UnitTests/ThreadedSocketReactorTests.cs b/UnitTests/ThreadedSocketReactorTests.cs
index 199304490..05c81fdb9 100644
--- a/UnitTests/ThreadedSocketReactorTests.cs
+++ b/UnitTests/ThreadedSocketReactorTests.cs
@@ -49,7 +49,7 @@ public void TestStartOnBusyPort()
new IPEndPoint(IPAddress.Loopback, port),
settings,
acceptorSocketDescriptor: null,
- new NonSessionLog(new ScreenLogFactory(true, true, true)));
+ new ScreenLog(true, true, true));
var stdOut = GetStdOut();
var ex = Assert.Throws(delegate { testingObject.Run(); })!;
diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj
index 92f40b7ca..443e7bd12 100644
--- a/UnitTests/UnitTests.csproj
+++ b/UnitTests/UnitTests.csproj
@@ -18,6 +18,7 @@
+