Skip to content

Commit

Permalink
new NonSessionLog
Browse files Browse the repository at this point in the history
Initialized and passed through the stream/socket
hierarchy so that logging can be performed
for messages that cannot be tied to a session
  • Loading branch information
gbirchmeier committed Jun 10, 2024
1 parent e8bd11c commit 0bad6ed
Show file tree
Hide file tree
Showing 20 changed files with 199 additions and 125 deletions.
5 changes: 2 additions & 3 deletions QuickFIXn/AbstractInitiator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,10 @@ public abstract class AbstractInitiator : IInitiator
private readonly SessionFactory _sessionFactory;
private Thread? _thread;

#region Properties
protected readonly NonSessionLog _nonSessionLog;

public bool IsStopped { get; private set; } = true;

#endregion

protected AbstractInitiator(
IApplication app,
IMessageStoreFactory storeFactory,
Expand All @@ -38,6 +36,7 @@ protected AbstractInitiator(
var logFactory = logFactoryNullable ?? new NullLogFactory();
var msgFactory = messageFactoryNullable ?? new DefaultMessageFactory();
_sessionFactory = new SessionFactory(app, storeFactory, logFactory, msgFactory);
_nonSessionLog = new NonSessionLog(logFactory);

HashSet<SessionID> definedSessions = _settings.GetSessions();
if (0 == definedSessions.Count)
Expand Down
9 changes: 7 additions & 2 deletions QuickFIXn/AcceptorSocketDescriptor.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#nullable enable
using System.Collections.Generic;
using System.Net;
using QuickFix.Logger;

namespace QuickFix
{
Expand All @@ -11,10 +12,14 @@ internal class AcceptorSocketDescriptor

private readonly Dictionary<SessionID, Session> _acceptedSessions = new ();

public AcceptorSocketDescriptor(IPEndPoint socketEndPoint, SocketSettings socketSettings, QuickFix.SettingsDictionary sessionDict)
public AcceptorSocketDescriptor(
IPEndPoint socketEndPoint,
SocketSettings socketSettings,
SettingsDictionary sessionDict,
NonSessionLog nonSessionLog)
{
Address = socketEndPoint;
SocketReactor = new ThreadedSocketReactor(Address, socketSettings, sessionDict, this);
SocketReactor = new ThreadedSocketReactor(Address, socketSettings, sessionDict, this, nonSessionLog);
}

internal void AcceptSession(Session session)
Expand Down
12 changes: 8 additions & 4 deletions QuickFIXn/ClientHandlerThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ public ExitedEventArgs(ClientHandlerThread clientHandlerThread)
private volatile bool _isShutdownRequested = false;
private readonly SocketReader _socketReader;

internal ClientHandlerThread(TcpClient tcpClient, long clientId, QuickFix.SettingsDictionary settingsDict,
SocketSettings socketSettings, AcceptorSocketDescriptor? acceptorDescriptor)
{
internal ClientHandlerThread(
TcpClient tcpClient,
long clientId,
SocketSettings socketSettings,
AcceptorSocketDescriptor? acceptorDescriptor,
NonSessionLog nonSessionLog
) {
Id = clientId;
_socketReader = new SocketReader(tcpClient, socketSettings, this, acceptorDescriptor);
_socketReader = new SocketReader(tcpClient, socketSettings, this, acceptorDescriptor, nonSessionLog);
}

public void Start()
Expand Down
2 changes: 1 addition & 1 deletion QuickFIXn/IInitiator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public interface IInitiator : IDisposable
/// <param name="sessionID">ID of session to be added</param>
/// <param name="dict">session settings</param>
/// <returns>true if session added successfully, false if session already exists or is not an initiator</returns>
bool AddSession(SessionID sessionID, QuickFix.SettingsDictionary dict);
bool AddSession(SessionID sessionID, SettingsDictionary dict);

/// <summary>
/// Remove an existing session after initiator has been started
Expand Down
7 changes: 6 additions & 1 deletion QuickFIXn/Logger/CompositeLogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
namespace QuickFix.Logger;

/// <summary>
/// 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.
/// 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.
/// </summary>
public class CompositeLogFactory : ILogFactory
{
Expand All @@ -24,4 +25,8 @@ public ILog Create(SessionID sessionID)
{
return new CompositeLog(_factories.Select(f => f.Create(sessionID)).ToArray());
}

public ILog CreateNonSessionLog() {
return new CompositeLog(_factories.Select(f => f.Create(new SessionID("Non", "Session", "Log"))).ToArray());
}
}
12 changes: 7 additions & 5 deletions QuickFIXn/Logger/FileLogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,24 @@ public class FileLogFactory : ILogFactory
{
private readonly SessionSettings _settings;

#region LogFactory Members

public FileLogFactory(SessionSettings settings)
{
_settings = settings;
}

/// <summary>
/// Creates a file-based message store
/// Creates a file-based message log
/// </summary>
/// <param name="sessionId">session ID for the message store</param>
/// <param name="sessionId">session ID for the message log</param>
/// <returns></returns>
public ILog Create(SessionID sessionId)
{
return new FileLog(_settings.Get(sessionId).GetString(SessionSettings.FILE_LOG_PATH), sessionId);
}

#endregion
public ILog CreateNonSessionLog() {
return new FileLog(
_settings.Get().GetString(SessionSettings.FILE_LOG_PATH),
new SessionID("Non", "Session", "Log"));
}
}
15 changes: 13 additions & 2 deletions QuickFIXn/Logger/ILogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@
namespace QuickFix.Logger;

/// <summary>
/// Used by a session to create a log implementation
/// Creates a log instance
/// </summary>
public interface ILogFactory
{
/// <summary>
/// Create a log implementation
/// Create a log instance for a session
/// </summary>
/// <param name="sessionId">session ID usually used for configuration access</param>
/// <returns></returns>
ILog Create(SessionID sessionId);

/// <summary>
/// Create a log instance that is not tied to a session.
/// This log will
/// (1) only be used for messages that cannot be linked to a session
/// (2) only have its OnEvent() method called
/// (3) only be created when the first message is logged (to avoid e.g. empty log files)
/// This log is written to only on rare occasions. It's possible you may never see it created.
/// </summary>
/// <returns></returns>
ILog CreateNonSessionLog();
}
28 changes: 28 additions & 0 deletions QuickFIXn/Logger/NonSessionLog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#nullable enable
using System;

namespace QuickFix.Logger;

/// <summary>
/// A logger that can be used when the calling logic cannot identify a session (which is rare).
/// Does not create a file until first write.
/// </summary>
public class NonSessionLog {

private readonly ILogFactory _logFactory;
private ILog? _log;

private readonly object _sync = new();

internal NonSessionLog(ILogFactory logFactory) {
_logFactory = logFactory;
}

internal void OnEvent(string s) {
lock (_sync) {
_log ??= _logFactory.CreateNonSessionLog();
}
_log.OnEvent(s);
}
}

5 changes: 5 additions & 0 deletions QuickFIXn/Logger/NullLogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ public ILog Create(SessionID _x)
{
return new NullLog();
}

public ILog CreateNonSessionLog()
{
return new NullLog();
}
}
6 changes: 3 additions & 3 deletions QuickFIXn/Logger/ScreenLogFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public ScreenLogFactory(bool logIncoming, bool logOutgoing, bool logEvent)
_settings = new SessionSettings();
}

#region LogFactory Members

public ILog Create(SessionID sessionId) {
bool logIncoming = _logIncoming;
bool logOutgoing = _logOutgoing;
Expand All @@ -47,5 +45,7 @@ public ILog Create(SessionID sessionId) {
return new ScreenLog(logIncoming, logOutgoing, logEvent);
}

#endregion
public ILog CreateNonSessionLog() {
return new ScreenLog(true, true, true);
}
}
12 changes: 10 additions & 2 deletions QuickFIXn/SocketInitiatorThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using QuickFix.Logger;

namespace QuickFix
{
Expand All @@ -27,18 +28,25 @@ public class SocketInitiatorThread : IResponder
private readonly IPEndPoint _socketEndPoint;
private readonly SocketSettings _socketSettings;
private bool _isDisconnectRequested = false;
private readonly NonSessionLog _nonSessionLog;

/// <summary>
/// Keep a task for handling async read
/// </summary>
private Task<int>? _currentReadTask;

public SocketInitiatorThread(Transport.SocketInitiator initiator, Session session, IPEndPoint socketEndPoint, SocketSettings socketSettings)
public SocketInitiatorThread(
Transport.SocketInitiator initiator,
Session session,
IPEndPoint socketEndPoint,
SocketSettings socketSettings,
NonSessionLog nonSessionLog)
{
Initiator = initiator;
Session = session;
_socketEndPoint = socketEndPoint;
_socketSettings = socketSettings;
_nonSessionLog = nonSessionLog;
}

public void Start()
Expand Down Expand Up @@ -74,7 +82,7 @@ public void Connect()
/// <returns>Stream representing the (network)connection to the other party</returns>
protected virtual Stream SetupStream()
{
return Transport.StreamFactory.CreateClientStream(_socketEndPoint, _socketSettings, Session.Log);
return Transport.StreamFactory.CreateClientStream(_socketEndPoint, _socketSettings, _nonSessionLog);
}

public bool Read()
Expand Down
28 changes: 14 additions & 14 deletions QuickFIXn/SocketReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using QuickFix.Logger;

namespace QuickFix
{
Expand All @@ -19,6 +20,7 @@ public class SocketReader : IDisposable
private readonly TcpClient _tcpClient;
private readonly ClientHandlerThread _responder;
private readonly AcceptorSocketDescriptor? _acceptorDescriptor;
private readonly NonSessionLog _nonSessionLog;

/// <summary>
/// Keep a task for handling async read
Expand All @@ -29,12 +31,14 @@ internal SocketReader(
TcpClient tcpClient,
SocketSettings settings,
ClientHandlerThread responder,
AcceptorSocketDescriptor? acceptorDescriptor)
AcceptorSocketDescriptor? acceptorDescriptor,
NonSessionLog nonSessionLog)
{
_tcpClient = tcpClient;
_responder = responder;
_acceptorDescriptor = acceptorDescriptor;
_stream = Transport.StreamFactory.CreateServerStream(tcpClient, settings);
_stream = Transport.StreamFactory.CreateServerStream(tcpClient, settings, nonSessionLog);
_nonSessionLog = nonSessionLog;
}

public void Read()
Expand Down Expand Up @@ -121,7 +125,7 @@ private void OnMessageFound(string msg)
if (_qfSession is null || IsUnknownSession(_qfSession.SessionID))
{
_qfSession = null;
LogSessionEvent("ERROR: Disconnecting; received message for unknown session: " + msg);
_nonSessionLog.OnEvent("ERROR: Disconnecting; received message for unknown session: " + msg);
DisconnectClient();
return;
}
Expand Down Expand Up @@ -169,13 +173,12 @@ protected void HandleBadMessage(string msg, Exception e)
{
if (Fields.MsgType.LOGON.Equals(Message.GetMsgType(msg)))
{
LogSessionEvent($"ERROR: Invalid LOGON message, disconnecting: {e.Message}");
// TODO: else session-agnostic log
LogEvent($"ERROR: Invalid LOGON message, disconnecting: {e.Message}");
DisconnectClient();
}
else
{
LogSessionEvent($"ERROR: Invalid message: {e.Message}");
LogEvent($"ERROR: Invalid message: {e.Message}");
}
}
catch (InvalidMessage)
Expand Down Expand Up @@ -244,7 +247,7 @@ private void HandleExceptionInternal(Session? quickFixSession, Exception cause)
break;
}

LogSessionEvent($"SocketReader Error: {reason}");
LogEvent($"SocketReader Error: {reason}");

if (disconnectNeeded)
{
Expand All @@ -256,18 +259,15 @@ private void HandleExceptionInternal(Session? quickFixSession, Exception cause)
}

/// <summary>
/// Log event to session if known, else do... TODO
/// Log event to session log if session is known, else to nonSessionLog
/// </summary>
/// <param name="s"></param>
private void LogSessionEvent(string s)
private void LogEvent(string s)
{
if(_qfSession is not null)
_qfSession.Log.OnEvent(s);
else {
// Can't tie this to a session, need a generic log.
// TODO this is a temp console log until I do something better
Console.WriteLine(s);
}
else
_nonSessionLog.OnEvent(s);
}

public int Send(string data)
Expand Down
2 changes: 1 addition & 1 deletion QuickFIXn/SocketSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public class SocketSettings : ICloneable
public bool ValidateCertificates { get; internal set; }

/// <summary>
/// Gets the path the the client/server-certificate.
/// Gets the path to the client/server-certificate.
/// </summary>
/// <value>
/// The certificate path.
Expand Down
Loading

0 comments on commit 0bad6ed

Please sign in to comment.