Last active
April 10, 2023 11:33
-
-
Save GeorgeTsiokos/b2443924c7d722859e6e0c3425e9af72 to your computer and use it in GitHub Desktop.
QuickFix/n daily log files
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal sealed class DailyFileLog : ILog | |
{ | |
readonly DailyFileLogWriter _eventLog; | |
readonly DailyFileLogWriter _messageLog; | |
public DailyFileLog(FileLogWriterOptions eventLog, FileLogWriterOptions messageLog) | |
{ | |
_eventLog = new DailyFileLogWriter(eventLog); | |
_messageLog = new DailyFileLogWriter(messageLog); | |
} | |
public void Dispose() | |
{ | |
_eventLog.Dispose(); | |
_messageLog.Dispose(); | |
} | |
public void Clear() | |
{ | |
_eventLog.Clear(); | |
_messageLog.Clear(); | |
} | |
public void OnIncoming(string msg) => _messageLog.Add(msg); | |
public void OnOutgoing(string msg) => _messageLog.Add(msg); | |
public void OnEvent(string s) => _eventLog.Add(s); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public sealed class DailyFileLogFactory : ILogFactory | |
{ | |
const int DefaultBufferSize = 16384; | |
const int DefaultFlushInterval = 1000; | |
readonly int _eventBufferSize; | |
readonly int _eventFlushInterval; | |
readonly int _messagesBufferSize; | |
readonly int _messagesFlushInterval; | |
readonly SessionSettings _settings; | |
readonly TimeZoneInfo _timeZoneInfo; | |
public DailyFileLogFactory(SessionSettings settings, int messagesBufferSize = DefaultBufferSize, int messagesFlushInterval = DefaultFlushInterval, int eventBufferSize = DefaultBufferSize, int eventFlushInterval = DefaultFlushInterval, TimeZoneInfo timeZoneInfo = null) | |
{ | |
_settings = settings; | |
_messagesBufferSize = messagesBufferSize; | |
_messagesFlushInterval = messagesFlushInterval; | |
_eventBufferSize = eventBufferSize; | |
_eventFlushInterval = eventFlushInterval; | |
_timeZoneInfo = timeZoneInfo ?? TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); | |
} | |
public ILog Create(SessionID sessionId) | |
{ | |
var prefix = FileLog.Prefix(sessionId); | |
var fileLogPath = GetFileLogPath(sessionId); | |
var encoding = new UTF8Encoding(false, false); | |
var eventLog = new FileLogWriterOptions(fileLogPath, prefix, "event.current.log", _eventBufferSize, _eventFlushInterval, encoding, _timeZoneInfo); | |
var messageLog = new FileLogWriterOptions(fileLogPath, prefix, "messages.current.log", _messagesBufferSize, _messagesFlushInterval, encoding, _timeZoneInfo); | |
return new DailyFileLog(eventLog, messageLog); | |
} | |
string GetFileLogPath(SessionID sessionId) | |
{ | |
var fileLogPath = _settings.Get(sessionId).GetString("FileLogPath"); | |
if (!Directory.Exists(fileLogPath)) | |
Directory.CreateDirectory(fileLogPath); | |
return fileLogPath; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal sealed class DailyFileLogWriter : IDisposable | |
{ | |
readonly BlockingCollection<(bool Clear, DateTime DateTime, string Msg)> _blockingCollection = new BlockingCollection<(bool Clear, DateTime DateTime, string Msg)>(); | |
readonly FileLogWriterOptions _options; | |
readonly ManualResetEventSlim _writerCompleteFlag = new ManualResetEventSlim(); | |
int _clearCounter; | |
public DailyFileLogWriter(FileLogWriterOptions options) | |
{ | |
_options = options; | |
Task.Factory.StartNew(Writer, TaskCreationOptions.LongRunning); | |
} | |
public void Dispose() | |
{ | |
if (_blockingCollection.IsAddingCompleted) | |
{ | |
_writerCompleteFlag.Wait(); | |
return; | |
} | |
_blockingCollection.CompleteAdding(); | |
_writerCompleteFlag.Wait(); | |
_blockingCollection.Dispose(); | |
} | |
public void Add(string value) => _blockingCollection.Add((false, DateTime.UtcNow, value)); | |
void Writer() | |
{ | |
var dateTime = DateTime.MinValue; | |
var streamWriter = StreamWriter.Null; | |
void Reset(DateTime logEntryDateTime) | |
{ | |
using (streamWriter) | |
streamWriter.Flush(); | |
dateTime = logEntryDateTime; | |
} | |
try | |
{ | |
try | |
{ | |
while (!_blockingCollection.IsCompleted) | |
{ | |
var isValue = _blockingCollection.TryTake(out var value, _options.FlushInterval); | |
if (!isValue) | |
{ | |
streamWriter.Flush(); | |
continue; | |
} | |
var logEntryDateTime = Convert(value.DateTime); | |
if (value.Clear) | |
{ | |
Reset(logEntryDateTime); | |
streamWriter = CreateStreamWriter(dateTime, Interlocked.Increment(ref _clearCounter) + _options.Suffix); | |
continue; | |
} | |
if (dateTime.Date != logEntryDateTime.Date) | |
{ | |
Reset(logEntryDateTime); | |
streamWriter = CreateStreamWriter(dateTime, _options.Suffix); | |
} | |
streamWriter.WriteLine(Format(logEntryDateTime, value.Msg)); | |
} | |
} | |
catch (OperationCanceledException) { } | |
finally | |
{ | |
using (streamWriter) | |
streamWriter.Flush(); | |
} | |
} | |
catch | |
{ | |
// Add will throw | |
_blockingCollection.CompleteAdding(); | |
throw; | |
} | |
finally | |
{ | |
_writerCompleteFlag.Set(); | |
} | |
} | |
static string Format(DateTime dateTime, string msg) => $"{dateTime:HH:mm:ss.fff} : {msg}"; | |
DateTime Convert(DateTime value) => TimeZoneInfo.ConvertTimeFromUtc(value, _options.ZoneInfo); | |
StreamWriter CreateStreamWriter(DateTime dateTime, string suffix) => StreamWriterFactory.Create(Path.Combine(_options.FileLogPath, $"{dateTime:yyyyMMdd}.{_options.Prefix}.{suffix}"), _options.Encoding, _options.BufferSize, _options.AutoFlush); | |
public void Clear() => _blockingCollection.Add((true, DateTime.UtcNow, string.Empty)); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal struct FileLogWriterOptions | |
{ | |
public string FileLogPath { get; } | |
public string Prefix { get; } | |
public string Suffix { get; } | |
public TimeZoneInfo ZoneInfo { get; } | |
public int FlushInterval { get; } | |
public int BufferSize { get; } | |
public bool AutoFlush => FlushInterval < 10; | |
public Encoding Encoding { get; } | |
public FileLogWriterOptions(string fileLogPath, string prefix, string suffix, int bufferSize, int flushInterval, Encoding encoding, TimeZoneInfo timeZoneInfo) | |
{ | |
FileLogPath = fileLogPath; | |
Prefix = prefix; | |
Suffix = suffix; | |
ZoneInfo = timeZoneInfo; | |
BufferSize = bufferSize; | |
FlushInterval = flushInterval; | |
Encoding = encoding; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// default | |
builder.RegisterType<FileLogFactory>().As<ILogFactory>().ExternallyOwned(); | |
// daily | |
builder.RegisterType<DailyFileLogFactory>().As<ILogFactory>().ExternallyOwned(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
internal static class StreamWriterFactory | |
{ | |
static FileStream CreateFileStream(string fileName, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, int bufferSize, FileOptions fileOptions) => new FileStream(fileName, fileMode, fileAccess, fileShare, bufferSize, fileOptions); | |
public static StreamWriter Create(string fileName, Encoding encoding, int bufferSize, bool autoFlush) | |
{ | |
var fileStream = CreateFileStream(fileName, FileMode.Append, FileAccess.Write, FileShare.Read, bufferSize, FileOptions.WriteThrough); | |
return new StreamWriter(fileStream, encoding, bufferSize, false) | |
{ | |
AutoFlush = autoFlush | |
}; | |
} | |
} |
Author
GeorgeTsiokos
commented
Oct 13, 2017
Method | SessionCount | Concurrency | MessageCount | Mean | Error | StdDev |
---|---|---|---|---|---|---|
QuickFix | 1 | 1 | 999 | 36.94 ms | 0.7098 ms | 1.298 ms |
Daily | 1 | 1 | 999 | 20.39 ms | 0.4906 ms | 1.408 ms |
QuickFix | 1 | 5 | 999 | 95.30 ms | 1.7894 ms | 1.757 ms |
Daily | 1 | 5 | 999 | 71.11 ms | 6.3368 ms | 18.585 ms |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment