Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Move to Microsoft.Extensions.Logging #899

Open
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

jkulubya
Copy link
Contributor

Hi,
We'd like to 1. pipe the QF logs through our existing log pipeline based on MS.Extensions.Logging, and 2. eventually add more context to the structured logs that will come out of QF when using MS Logging. It doesn't seem like these two issues (#205, #679) were resolved so here's a PR to get that moving along.

There's a new dependency on Microsoft.extensions.logging.abstractions, and all the logging is happening via an ILogger. When messages are logged, they're logged with the message type in the scope (where possible), to allow heartbeats and such to be filtered out downstream.

Instead of passing in an ILogFactory, end-users should now pass an ILoggerFactory. Exisiting ILogFactory's and ILog's should work, via an adapter, but all that has been marked obsolete with a note telling the user to migrate to MS Logging.

There are a few questions to resolve, like what levels things should be logged at, what event IDs should be used for logs, what category names should be used.

@VAllens
Copy link
Contributor

VAllens commented Dec 4, 2024

While I personally would love to do this,
it would be too destructive and invasive and cause QuickFIX/n to create external dependencies.

I prefer to use an extension library implementation,
e.g. QuickFIXn.Extensions.Logging.

@jkulubya
Copy link
Contributor Author

jkulubya commented Dec 4, 2024

If taking on an external dependency is a hard no, then that leaves two options, keep the current logging as-is, or implement an extension package QF.extensions.logging as you suggest.

The downside with making a "QF.Extensions.Logging" package that eventually hooks into ms.extensions is that it would require improving the current QF logging abstraction to produce all the data that a user of ms.extensions.logging might require, at which point you're rewriting ms.extensions.logging, without getting 100% there in terms of performance and ergonomics.

The problem with maintaining the status quo is that users are asking for better logging, and the current system isn't meeting their needs.

I'm partial to just going with ms.extensions.logging, and the .net world has coalesced around it, so I'd be comfortable with that choice. But even an extension package would be an improvement over what we have now.

@VAllens
Copy link
Contributor

VAllens commented Dec 4, 2024

If taking on an external dependency is a hard no, then that leaves two options, keep the current logging as-is, or implement an extension package QF.extensions.logging as you suggest.

The downside with making a "QF.Extensions.Logging" package that eventually hooks into ms.extensions is that it would require improving the current QF logging abstraction to produce all the data that a user of ms.extensions.logging might require, at which point you're rewriting ms.extensions.logging, without getting 100% there in terms of performance and ergonomics.

The problem with maintaining the status quo is that users are asking for better logging, and the current system isn't meeting their needs.

I'm partial to just going with ms.extensions.logging, and the .net world has coalesced around it, so I'd be comfortable with that choice. But even an extension package would be an improvement over what we have now.

Yes, either way, it's better than the status quo.

@gbirchmeier
Copy link
Member

I'm starting to digest this PR this week. I have to admit that I am not familiar with Microsoft.Extensions.Logging. (Can I call it MEL going forward?) My work with .NET is very FIX-centric, so somehow I'm able to stay oblivious to certain aspects of the greater .NET ecosystem. I need to download this branch and it and see how it works.

My first impulse is "aaughhh! So much change!" Which is of course not a legitimate criticism, and also betrays my ignorance of what this extension can offer.

Let me start with one dumb question, however: Why not just write an QF ILog implementation that defers all its functionality to MEL?

@jkulubya
Copy link
Contributor Author

jkulubya commented Dec 4, 2024

I'm starting to digest this PR this week. I have to admit that I am not familiar with Microsoft.Extensions.Logging. (Can I call it MEL going forward?) My work with .NET is very FIX-centric, so somehow I'm able to stay oblivious to certain aspects of the greater .NET ecosystem. I need to download this branch and it and see how it works.

My first impulse is "aaughhh! So much change!" Which is of course not a legitimate criticism, and also betrays my ignorance of what this extension can offer.

I'll try my best to help. The happy path of MEL is that library authors, write logs using an ILogger from MEL. The concrete implementation of ILogger will be provided by the library consumer/application developer. The benefit of this is that on the consumer side, there are purpose-built logging libraries the plug straight into MEL to do all you'd ever want to do with logs, like log to a Db, log to a file, log to a rolling file etc. The thinking is a quickfix developer wants to spend their time building quickfix, not become an expert in logging.

Let me start with one dumb question, however: Why not just write an QF ILog implementation that defers all its functionality to MEL?

A QF ILog that writes into MEL is easy. In fact, in this PR, there's an adapter to pipe logs from existing ILog and ILogFactory into MEL so as not to break folks. But that's just piping plain text logs, while MEL can do so much more (e.g. structured logging/scopes/high performance logging). A QF ILog that can do the extras that MEL can do is a bit more work (I'm not an expert so i can't say exactly how much) and ends up looking a whole lot like MEL.

@jkulubya
Copy link
Contributor Author

jkulubya commented Dec 4, 2024

Copy link
Contributor

@Rob-Hague Rob-Hague left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am in favour of this change. I skimmed it and left some comments inline

QuickFIXn/Logger/NullLogger.cs Outdated Show resolved Hide resolved
QuickFIXn/AbstractInitiator.cs Outdated Show resolved Hide resolved
QuickFIXn/Logger/NullLoggerProvider.cs Outdated Show resolved Hide resolved
QuickFIXn/QuickFix.csproj Outdated Show resolved Hide resolved
/// <returns></returns>
public bool Send(string message)
public bool Send(string message, string messageType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

breaking change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't supposed to be - it's been reverted. What do you think of what it is now together with #909?

QuickFIXn/SessionFactory.cs Outdated Show resolved Hide resolved
QuickFIXn/ThreadedSocketAcceptor.cs Outdated Show resolved Hide resolved
QuickFIXn/ThreadedSocketAcceptor.cs Outdated Show resolved Hide resolved
QuickFIXn/Logger/FileLogger.cs Outdated Show resolved Hide resolved
@jkulubya
Copy link
Contributor Author

Please verify that the levels at which stuff is logged makes sense. I went with Information for the inbound/outbound messages, and error/debug/trace for the rest

@Rob-Hague
Copy link
Contributor

I am seeing quite a lot of test failures locally which I don't seem to get on master, e.g.:

    C:\src\quickfixn\QuickFIXn\ThreadedSocketAcceptor.cs(79): error TESTERROR:
      Fix50sp2Test("SessionReset.def") (54ms): Error Message: OneTimeSetUp: QuickFix.ConfigError : Configuration failed:
       The process cannot access the file 'C:\src\quickfixn\AcceptanceTest\bin\Debug\net8.0\log\FIXT.1.1-ISLD-TW.message
      s.current.log' because it is being used by another process.
        ----> System.IO.IOException : The process cannot access the file 'C:\src\quickfixn\AcceptanceTest\bin\Debug\net8
      .0\log\FIXT.1.1-ISLD-TW.messages.current.log' because it is being used by another process.
      Stack Trace:
         at QuickFix.ThreadedSocketAcceptor..ctor(IApplication application, IMessageStoreFactory storeFactory, SessionSe
      ttings settings, ILoggerFactory loggerFactory, IMessageFactory messageFactory) in C:\src\quickfixn\QuickFIXn\Threa
      dedSocketAcceptor.cs:line 79
         at AcceptanceTest.TestBase.Setup() in C:\src\quickfixn\AcceptanceTest\TestBase.cs:line 38
         at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructo
      r)
         at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
      --IOException
         at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, Fil
      eShare share, FileOptions options)
         at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare
       share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
         at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare sha
      re, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
         at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, Fil
      eShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
         at System.IO.StreamWriter.ValidateArgsAndOpenPath(String path, Boolean append, Encoding encoding, Int32 bufferS
      ize)
         at System.IO.StreamWriter..ctor(String path, Boolean append)
         at QuickFix.Logger.FileLog..ctor(String fileLogPath, SessionID sessionId) in C:\src\quickfixn\QuickFIXn\Logger\
      FileLog.cs:line 42
         at QuickFix.Logger.FileLoggerProvider.CreateLogger(String categoryName) in C:\src\quickfixn\QuickFIXn\Logger\Fi
      leLoggerProvider.cs:line 30
         at Microsoft.Extensions.Logging.LoggerInformation..ctor(ILoggerProvider provider, String category)
         at Microsoft.Extensions.Logging.LoggerFactory.CreateLoggers(String categoryName)
         at Microsoft.Extensions.Logging.LoggerFactory.CreateLogger(String categoryName)
         at QuickFix.Session..ctor(Boolean isInitiator, IApplication app, IMessageStoreFactory storeFactory, SessionID s
      essId, DataDictionaryProvider dataDictProvider, SessionSchedule sessionSchedule, Int32 heartBtInt, ILoggerFactory
      loggerFactory, IMessageFactory msgFactory, String senderDefaultApplVerId) in C:\src\quickfixn\QuickFIXn\Session.cs
      :line 247
         at QuickFix.SessionFactory.Create(SessionID sessionId, SettingsDictionary settings) in C:\src\quickfixn\QuickFI
      Xn\SessionFactory.cs:line 103
         at QuickFix.ThreadedSocketAcceptor.CreateSession(SessionID sessionId, SettingsDictionary dict) in C:\src\quickf
      ixn\QuickFIXn\ThreadedSocketAcceptor.cs:line 151
         at QuickFix.ThreadedSocketAcceptor..ctor(IApplication application, IMessageStoreFactory storeFactory, SessionSe
      ttings settings, ILoggerFactory loggerFactory, IMessageFactory messageFactory) in C:\src\quickfixn\QuickFIXn\Threa
      dedSocketAcceptor.cs:line 74

QuickFIXn/AbstractInitiator.cs Outdated Show resolved Hide resolved
QuickFIXn/ThreadedSocketAcceptor.cs Outdated Show resolved Hide resolved
QuickFIXn/QuickFix.csproj Outdated Show resolved Hide resolved
QuickFIXn/ThreadedSocketAcceptor.cs Outdated Show resolved Hide resolved
QuickFIXn/Logger/FileLoggerProvider.cs Outdated Show resolved Hide resolved
QuickFIXn/Logger/LogFactoryAdapter.cs Outdated Show resolved Hide resolved
@jkulubya
Copy link
Contributor Author

jkulubya commented Dec 12, 2024

I can't reproduce this using dotnet test on a fresh clone. Are you running the tests in VS/Rider?

@gbirchmeier
Copy link
Member

@jkulubya Are you running on windows? This looks like an error that would not occur on Linux/Mac, but would occur on Windows. (I ran into this myself with some recent PRs)

@jkulubya
Copy link
Contributor Author

@gbirchmeier I'm on linux. I presume you fixed the issues in #904? It was a helpful pointer.

@Rob-Hague do you mind trying again? I was missing a few usings and Disposes, but I can't verify without a windows machine. It's still all green on linux.

@gbirchmeier
Copy link
Member

@gbirchmeier I'm on linux. I presume you fixed the issues in #904? It was a helpful pointer.

@jkulubya I had help, but yes, that was it

@Rob-Hague
Copy link
Contributor

It is improved in the last commits, I am just getting 3 failures instead of ~300. Example:

 DynamicAcceptor
   Source: SessionDynamicTest.cs line 422
   Duration: 519 ms

  Message: 
QuickFix.ConfigError : Configuration failed: The process cannot access the file 'C:\src\quickfixn\UnitTests\bin\Debug\net8.0\log\FIX.4.2-dummy-acc01.messages.current.log' because it is being used by another process.
  ----> System.IO.IOException : The process cannot access the file 'C:\src\quickfixn\UnitTests\bin\Debug\net8.0\log\FIX.4.2-dummy-acc01.messages.current.log' because it is being used by another process.

  Stack Trace: 
ThreadedSocketAcceptor.ctor(IApplication application, IMessageStoreFactory storeFactory, SessionSettings settings, ILoggerFactory loggerFactory, IMessageFactory messageFactory) line 88
SessionDynamicTest.StartEngine(Boolean initiator, Boolean twoSessions) line 155
SessionDynamicTest.DynamicAcceptor() line 424
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
--IOException
SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
OSFileStreamStrategy.ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
StreamWriter.ctor(String path, Boolean append)
FileLog.ctor(String fileLogPath, SessionID sessionId) line 42
<>c__DisplayClass3_0.<CreateLogger>b__1(SessionID sId) line 30
ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
FileLoggerProvider.CreateLogger(String categoryName) line 30
LoggerInformation.ctor(ILoggerProvider provider, String category)
LoggerFactory.CreateLoggers(String categoryName)
LoggerFactory.CreateLogger(String categoryName)
Session.ctor(Boolean isInitiator, IApplication app, IMessageStoreFactory storeFactory, SessionID sessId, DataDictionaryProvider dataDictProvider, SessionSchedule sessionSchedule, Int32 heartBtInt, ILoggerFactory loggerFactory, IMessageFactory msgFactory, String senderDefaultApplVerId) line 247
SessionFactory.Create(SessionID sessionId, SettingsDictionary settings) line 103
ThreadedSocketAcceptor.CreateSession(SessionID sessionId, SettingsDictionary dict) line 160
ThreadedSocketAcceptor.ctor(IApplication application, IMessageStoreFactory storeFactory, SessionSettings settings, ILoggerFactory loggerFactory, IMessageFactory messageFactory) line 83

I am on Windows 11. Could be worth adding a Windows test run to Github Actions?

@jkulubya
Copy link
Contributor Author

If the 3 remaining failing tests are "DifferentPortForAcceptorTest", "DynamicAcceptor" and "TestRecreation", then they should be fixed now. And I agree with the windows runner suggestion.

Copy link
Contributor

@Rob-Hague Rob-Hague left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep runs green for me now. Was not aware about the different disposal behaviour with the constructors.

I think this concludes my feedback for now. I will just point out that e.g. ScreenLogFactory and ScreenLogProvider could probably be merged in a similar way to ScreenLog and ScreenLogger earlier on

QuickFIXn/Logger/NonSessionFileLogger.cs Outdated Show resolved Hide resolved
QuickFIXn/Logger/ScreenLog.cs Outdated Show resolved Hide resolved
@gbirchmeier
Copy link
Member

FYI, I'm planning to put out 1.13 sometime in January, but not with this feature in it yet.

After the holiday I'll get more involved with this PR, with the plan to put it in 1.14 for release probably in the summer.

@Rob-Hague
Copy link
Contributor

Ok I said I was done but I have had a thought about a change to the design. Instead of adapting the existing log types to fit as MEL providers (and obsoleting them), the idea would be to allow them to exist separately:

// A bridge between MEL and QF logging.
// This is what would be passed around the library instead of MEL ILoggerFactory.
// It would avoid the potentially awkward session lookup in the current Provider&LogFactoryAdaptor impls.
public interface IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId);

    MEL.ILogger CreateNonSessionLog();
}

internal class LoggerFactoryAdaptor(ILoggerFactory factory) : IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId)
        => factory.CreateLogger(FileLog.Prefix(sessionId)); // or whatever string derived from sessionId

    MEL.ILogger CreateNonSessionLog()
        => factory.CreateLogger("Non.session.log"); // or whatever string
}

internal class LogFactoryAdaptor(ILogFactory factory) : IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId)
        => new LogAdaptor(logFactory.Create(sessionId)); // LogAdaptor as in this PR

    MEL.ILogger CreateNonSessionLog()
        => new LogAdaptor(logFactory.CreateNonSessionLog());
}

then the Provider types added in this PR would be removed and we can ignore anything to do with providers; the existing Log types would not need to be changed and could be un-obsoleted.

One downside of this is that it would be nice to decorate the non-session logs with the class name as is common with MEL e.g. using loggerFactory.GetLogger(GetType()). So perhaps the non-session concept does not appear in the QF bridge:

public interface IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId);

    MEL.ILogger CreateLogger(Type type);
}

internal class LoggerFactoryAdaptor(ILoggerFactory factory) : IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId)
        => factory.CreateLogger(FileLog.Prefix(sessionId)); // or whatever string derived from sessionId

    MEL.ILogger CreateLogger(Type type)
        => factory.CreateLogger(type);
}

internal class LogFactoryAdaptor(ILogFactory factory) : IQFLoggerFactory
{
    private LogAdaptor? _nonSessionLog; // just uses one instance

    MEL.ILogger CreateLogger(SessionID sessionId)
        => new LogAdaptor(logFactory.Create(sessionId)); // LogAdaptor as in this PR

    MEL.ILogger CreateLogger(Type type)
        => _nonSessionLog ??= new LogAdaptor(logFactory.CreateNonSessionLog());
}

Hope it makes some sense, we can discuss it at a later date. Happy holidays!

@jkulubya
Copy link
Contributor Author

jkulubya commented Jan 5, 2025

Ok I said I was done but I have had a thought about a change to the design. Instead of adapting the existing log types to fit as MEL providers (and obsoleting them), the idea would be to allow them to exist separately:

internal class LoggerFactoryAdaptor(ILoggerFactory factory) : IQFLoggerFactory
{
    MEL.ILogger CreateLogger(SessionID sessionId)
        => factory.CreateLogger(FileLog.Prefix(sessionId)); // or whatever string derived from sessionId

    MEL.ILogger CreateNonSessionLog()
        => factory.CreateLogger("Non.session.log"); // or whatever string
}

I like this but I think regardless of where we end up with the rest, we should still obsolete the logging functionality that currently exists. We can leave that to serilog and the rest.

The next question to answer is what should the logs be categorised as.

My suggestion is session logs are labelled "QuickFix.SessionLogs." and non-session logs labelled "QuickFix.NonSessionLogs" (or similar), regardless of source, consumers can direct filter by session ID, or whether session log/non session log.

I'm not attached to the name but we need to decide and go for it.

@Rob-Hague
Copy link
Contributor

The next question to answer is what should the logs be categorised as.

If it were to use loggerFactory.GetLogger(GetType()) for the "non-session" logs then these would take the name of the type as category, e.g. "QuickFix.ThreadedSocketReactor". I would agree the session logs should have at least a "QuickFix" prefix or similar

Don't use string parsing to decide whether to return session/non session log
Remove iloggerproviders that were added before
@jkulubya
Copy link
Contributor Author

jkulubya commented Jan 9, 2025

In the latest update, session logs will be tagged QuickFix.SessionLogs., and non session logs will take the name of the class that generated them, e.g QuickFix.ThreadedSocketAcceptor.

The two main entry points (ThreadedSocketAcceptor & SocketInitiator) accept either a QuickFix.Logger.ILogFactory (obsolete) or an MEL.ILoggerFactory.

@jkulubya
Copy link
Contributor Author

jkulubya commented Jan 9, 2025

I've updated the sample serilog project to log to console and rolling files (by the minute) and I get:

Console logs
[08:46:45 INF] Starting host
[08:46:45 DBG] Hosting starting
[08:46:45 DBG] Created session
[08:46:45 DBG] Created session
[08:46:45 DBG] Created session
[08:46:45 INF] Application started. Press Ctrl+C to shut down.
[08:46:45 INF] Hosting environment: Production
[08:46:45 DBG] Hosting started
[08:46:45 DBG] Connecting to 127.0.0.1 on port 5001
[08:46:45 DBG] Connecting to 127.0.0.1 on port 5001
[08:46:45 DBG] Connecting to 127.0.0.1 on port 5001
[08:46:45 DBG] Connection succeeded
[08:46:45 DBG] Connection succeeded
[08:46:45 DBG] Connection succeeded
[08:46:45 DBG] Session reset: ResetOnLogon
[08:46:45 DBG] Session reset: ResetOnLogon
[08:46:45 DBG] Session reset: ResetOnLogon
[08:46:45 DBG] Session reset: ResetSeqNumFlag
[08:46:45 DBG] Session reset: ResetSeqNumFlag
[08:46:45 INF] 8=FIX.4.49=7635=A34=149=CLIENT252=20250109-13:46:45.75656=EXECUTOR98=0108=10141=Y10=154
[08:46:45 INF] 8=FIX.4.49=7635=A34=149=CLIENT152=20250109-13:46:45.75656=EXECUTOR98=0108=10141=Y10=153
[08:46:45 INF] 8=FIX.4.09=6635=A34=149=CLIENT152=20250109-13:46:4556=EXECUTOR98=0108=1010=151
[08:46:45 DBG] Initiated logon request
[08:46:45 INF] 8=FIX.4.49=7035=A34=149=EXECUTOR52=20250109-13:46:45.77156=CLIENT298=0108=1010=100
[08:46:45 DBG] Initiated logon request
[08:46:45 INF] 8=FIX.4.49=7035=A34=149=EXECUTOR52=20250109-13:46:45.77756=CLIENT198=0108=1010=105
[08:46:45 DBG] Initiated logon request
[08:46:45 INF] 8=FIX.4.09=6635=A34=149=EXECUTOR52=20250109-13:46:4556=CLIENT198=0108=1010=151
[08:46:45 DBG] Received logon
[08:46:45 DBG] Received logon
[08:46:45 DBG] Received logon
[08:46:55 INF] 8=FIX.4.49=5835=034=249=EXECUTOR52=20250109-13:46:55.77456=CLIENT210=071
[08:46:55 INF] 8=FIX.4.49=5835=034=249=CLIENT252=20250109-13:46:55.77556=EXECUTOR10=072
[08:46:55 INF] 8=FIX.4.49=5835=034=249=EXECUTOR52=20250109-13:46:55.77956=CLIENT110=075
[08:46:55 INF] 8=FIX.4.49=5835=034=249=CLIENT152=20250109-13:46:55.78156=EXECUTOR10=068
[08:46:55 INF] 8=FIX.4.09=5435=034=249=EXECUTOR52=20250109-13:46:5556=CLIENT110=110
[08:46:55 INF] 8=FIX.4.09=5435=034=249=CLIENT152=20250109-13:46:5556=EXECUTOR10=110
[08:47:05 INF] 8=FIX.4.49=5835=034=349=EXECUTOR52=20250109-13:47:05.77656=CLIENT210=070
[08:47:05 INF] 8=FIX.4.49=5835=034=349=CLIENT252=20250109-13:47:05.77956=EXECUTOR10=073
[08:47:05 INF] 8=FIX.4.49=5835=034=349=CLIENT152=20250109-13:47:05.78156=EXECUTOR10=065
[08:47:05 INF] 8=FIX.4.09=5435=034=349=EXECUTOR52=20250109-13:47:0556=CLIENT110=107
[08:47:05 INF] 8=FIX.4.49=5835=034=349=EXECUTOR52=20250109-13:47:05.78256=CLIENT110=066
[08:47:05 INF] 8=FIX.4.09=5435=034=349=CLIENT152=20250109-13:47:0556=EXECUTOR10=107
[08:47:15 INF] 8=FIX.4.49=5835=034=449=CLIENT252=20250109-13:47:15.78156=EXECUTOR10=068
[08:47:15 INF] 8=FIX.4.49=5835=034=449=EXECUTOR52=20250109-13:47:15.78156=CLIENT210=068
[08:47:15 INF] 8=FIX.4.49=5835=034=449=EXECUTOR52=20250109-13:47:15.78456=CLIENT110=070
[08:47:15 INF] 8=FIX.4.09=5435=034=449=CLIENT152=20250109-13:47:1556=EXECUTOR10=109
[08:47:15 INF] 8=FIX.4.49=5835=034=449=CLIENT152=20250109-13:47:15.78556=EXECUTOR10=071
[08:47:15 INF] 8=FIX.4.09=5435=034=449=EXECUTOR52=20250109-13:47:1556=CLIENT110=109
[08:47:25 INF] 8=FIX.4.49=5835=034=549=CLIENT252=20250109-13:47:25.78456=EXECUTOR10=073
[08:47:25 INF] 8=FIX.4.49=5835=034=549=EXECUTOR52=20250109-13:47:25.78456=CLIENT210=073
[08:47:25 INF] 8=FIX.4.09=5435=034=549=CLIENT152=20250109-13:47:2556=EXECUTOR10=111
[08:47:25 INF] 8=FIX.4.49=5835=034=549=CLIENT152=20250109-13:47:25.78656=EXECUTOR10=074
[08:47:25 INF] 8=FIX.4.49=5835=034=549=EXECUTOR52=20250109-13:47:25.78656=CLIENT110=074
[08:47:25 INF] 8=FIX.4.09=5435=034=549=EXECUTOR52=20250109-13:47:2556=CLIENT110=111
[08:47:35 INF] 8=FIX.4.49=5835=034=649=CLIENT252=20250109-13:47:35.78756=EXECUTOR10=078
[08:47:35 INF] 8=FIX.4.09=5435=034=649=CLIENT152=20250109-13:47:3556=EXECUTOR10=113
[08:47:35 INF] 8=FIX.4.49=5835=034=649=EXECUTOR52=20250109-13:47:35.78756=CLIENT210=078
[08:47:35 INF] 8=FIX.4.49=5835=034=649=CLIENT152=20250109-13:47:35.78856=EXECUTOR10=078
[08:47:35 INF] 8=FIX.4.09=5435=034=649=EXECUTOR52=20250109-13:47:3556=CLIENT110=113
[08:47:35 INF] 8=FIX.4.49=5835=034=649=EXECUTOR52=20250109-13:47:35.78856=CLIENT110=078
[08:47:45 INF] 8=FIX.4.49=5835=034=749=CLIENT252=20250109-13:47:45.79056=EXECUTOR10=074
[08:47:45 INF] 8=FIX.4.09=5435=034=749=CLIENT152=20250109-13:47:4556=EXECUTOR10=115
[08:47:45 INF] 8=FIX.4.49=5835=034=749=CLIENT152=20250109-13:47:45.79056=EXECUTOR10=073
[08:47:45 INF] 8=FIX.4.49=5835=034=749=EXECUTOR52=20250109-13:47:45.79056=CLIENT210=074
[08:47:45 INF] 8=FIX.4.09=5435=034=749=EXECUTOR52=20250109-13:47:4556=CLIENT110=115
[08:47:45 INF] 8=FIX.4.49=5835=034=749=EXECUTOR52=20250109-13:47:45.78956=CLIENT110=081
^C[08:47:46 INF] Application is shutting down...
[08:47:46 DBG] Hosting stopping
[08:47:46 DBG] Initiated logout request
[08:47:46 DBG] Initiated logout request
[08:47:46 DBG] Initiated logout request
[08:47:46 INF] 8=FIX.4.49=5835=534=849=CLIENT152=20250109-13:47:46.79256=EXECUTOR10=082
[08:47:46 INF] 8=FIX.4.09=5435=534=849=CLIENT152=20250109-13:47:4656=EXECUTOR10=122
[08:47:46 INF] 8=FIX.4.49=5835=534=849=CLIENT252=20250109-13:47:46.79256=EXECUTOR10=083
[08:47:46 INF] 8=FIX.4.49=5835=534=849=EXECUTOR52=20250109-13:47:46.79356=CLIENT110=083
[08:47:46 INF] 8=FIX.4.49=5835=534=849=EXECUTOR52=20250109-13:47:46.79356=CLIENT210=084
[08:47:46 INF] 8=FIX.4.09=5435=534=849=EXECUTOR52=20250109-13:47:4656=CLIENT110=122
[08:47:46 DBG] Received logout response
[08:47:46 DBG] Received logout response
[08:47:46 DBG] Received logout response
[08:47:46 DBG] Session FIX.4.0:CLIENT1->EXECUTOR disconnecting: Received logout response
[08:47:46 DBG] Session FIX.4.4:CLIENT2->EXECUTOR disconnecting: Received logout response
[08:47:46 DBG] Session FIX.4.4:CLIENT1->EXECUTOR disconnecting: Received logout response
[08:47:46 DBG] Session reset: ResetOnDisconnect
[08:47:46 DBG] Session reset: ResetOnDisconnect
[08:47:46 DBG] Session reset: ResetOnDisconnect
[08:47:47 DBG] Hosting stopped

and

Log files
$ ls
FIX.4.0-CLIENT1-EXECUTOR.event202501090846.log     FIX.4.4-CLIENT1-EXECUTOR.event202501090846.log     FIX.4.4-CLIENT2-EXECUTOR.event202501090846.log     Non-Session-Log.event.current.log
FIX.4.0-CLIENT1-EXECUTOR.messages202501090846.log  FIX.4.4-CLIENT1-EXECUTOR.messages202501090846.log  FIX.4.4-CLIENT2-EXECUTOR.messages202501090846.log
FIX.4.0-CLIENT1-EXECUTOR.messages202501090847.log  FIX.4.4-CLIENT1-EXECUTOR.messages202501090847.log  FIX.4.4-CLIENT2-EXECUTOR.messages202501090847.log

$ cat FIX.4.0-CLIENT1-EXECUTOR.event202501090846.log
Created session
Connecting to 127.0.0.1 on port 5001
Connection succeeded
Session reset: ResetOnLogon
Initiated logon request
Received logon

$ cat FIX.4.0-CLIENT1-EXECUTOR.messages202501090846.log 
8=FIX.4.09=6635=A34=149=CLIENT152=20250109-13:46:4556=EXECUTOR98=0108=1010=151
8=FIX.4.09=6635=A34=149=EXECUTOR52=20250109-13:46:4556=CLIENT198=0108=1010=151
8=FIX.4.09=5435=034=249=EXECUTOR52=20250109-13:46:5556=CLIENT110=110
8=FIX.4.09=5435=034=249=CLIENT152=20250109-13:46:5556=EXECUTOR10=110

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants