Skip to content

Commit 1f282bb

Browse files
author
BRUNER Patrick
committed
logrotator test tool
1 parent a1c8dc8 commit 1f282bb

5 files changed

Lines changed: 227 additions & 101 deletions

File tree

src/LogExpert.Core/Classes/Log/LogfileReader.cs

Lines changed: 105 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -331,144 +331,150 @@ public int ShiftBuffers ()
331331
_logger.Info(CultureInfo.InvariantCulture, "ShiftBuffers() begin for {0}{1}", _fileName, IsMultiFile ? " (MultiFile)" : "");
332332

333333
AcquireBufferListWriterLock();
334-
ClearBufferState();
335-
336-
var offset = 0;
337-
_isLineCountDirty = true;
338334

339-
lock (_monitor)
335+
try
340336
{
341-
RolloverFilenameHandler rolloverHandler = new(_watchedILogFileInfo, _multiFileOptions);
342-
var fileNameList = rolloverHandler.GetNameList(_pluginRegistry);
337+
ClearBufferState();
343338

344-
ResetBufferCache();
339+
var offset = 0;
340+
_isLineCountDirty = true;
345341

346-
IList<ILogFileInfo> lostILogFileInfoList = [];
347-
IList<ILogFileInfo> readNewILogFileInfoList = [];
348-
IList<ILogFileInfo> newFileInfoList = [];
342+
lock (_monitor)
343+
{
344+
RolloverFilenameHandler rolloverHandler = new(_watchedILogFileInfo, _multiFileOptions);
345+
var fileNameList = rolloverHandler.GetNameList(_pluginRegistry);
349346

350-
var enumerator = _logFileInfoList.GetEnumerator();
347+
ResetBufferCache();
351348

352-
while (enumerator.MoveNext())
353-
{
354-
var logFileInfo = enumerator.Current;
355-
var fileName = logFileInfo.FullName;
356-
_logger.Debug(CultureInfo.InvariantCulture, "Testing file {0}", fileName);
349+
IList<ILogFileInfo> lostILogFileInfoList = [];
350+
IList<ILogFileInfo> readNewILogFileInfoList = [];
351+
IList<ILogFileInfo> newFileInfoList = [];
357352

358-
var node = fileNameList.Find(fileName);
353+
var enumerator = _logFileInfoList.GetEnumerator();
359354

360-
if (node == null)
355+
while (enumerator.MoveNext())
361356
{
362-
_logger.Warn(CultureInfo.InvariantCulture, "File {0} not found", fileName);
363-
continue;
364-
}
357+
var logFileInfo = enumerator.Current;
358+
var fileName = logFileInfo.FullName;
359+
_logger.Debug(CultureInfo.InvariantCulture, "Testing file {0}", fileName);
365360

366-
if (node.Previous != null)
367-
{
368-
fileName = node.Previous.Value;
369-
var newILogFileInfo = GetLogFileInfo(fileName);
370-
_logger.Debug(CultureInfo.InvariantCulture, "{0} exists\r\nOld size={1}, new size={2}", fileName, logFileInfo.OriginalLength, newILogFileInfo.Length);
371-
// is the new file the same as the old buffer info?
372-
if (newILogFileInfo.Length == logFileInfo.OriginalLength)
361+
var node = fileNameList.Find(fileName);
362+
363+
if (node == null)
373364
{
374-
ReplaceBufferInfos(logFileInfo, newILogFileInfo);
375-
newFileInfoList.Add(newILogFileInfo);
365+
_logger.Warn(CultureInfo.InvariantCulture, "File {0} not found", fileName);
366+
continue;
376367
}
377-
else
368+
369+
if (node.Previous != null)
378370
{
379-
_logger.Debug(CultureInfo.InvariantCulture, "Buffer for {0} must be re-read.", fileName);
380-
// not the same. so must read the rest of the list anew from the files
381-
readNewILogFileInfoList.Add(newILogFileInfo);
382-
while (enumerator.MoveNext())
371+
fileName = node.Previous.Value;
372+
var newILogFileInfo = GetLogFileInfo(fileName);
373+
_logger.Debug(CultureInfo.InvariantCulture, "{0} exists\r\nOld size={1}, new size={2}", fileName, logFileInfo.OriginalLength, newILogFileInfo.Length);
374+
// is the new file the same as the old buffer info?
375+
if (newILogFileInfo.Length == logFileInfo.OriginalLength)
383376
{
384-
fileName = enumerator.Current.FullName;
385-
node = fileNameList.Find(fileName);
386-
if (node == null)
387-
{
388-
_logger.Warn(CultureInfo.InvariantCulture, "File {0} not found", fileName);
389-
continue;
390-
}
391-
392-
if (node.Previous != null)
393-
{
394-
fileName = node.Previous.Value;
395-
_logger.Debug(CultureInfo.InvariantCulture, "New name is {0}", fileName);
396-
readNewILogFileInfoList.Add(GetLogFileInfo(fileName));
397-
}
398-
else
377+
ReplaceBufferInfos(logFileInfo, newILogFileInfo);
378+
newFileInfoList.Add(newILogFileInfo);
379+
}
380+
else
381+
{
382+
_logger.Debug(CultureInfo.InvariantCulture, "Buffer for {0} must be re-read.", fileName);
383+
// not the same. so must read the rest of the list anew from the files
384+
readNewILogFileInfoList.Add(newILogFileInfo);
385+
while (enumerator.MoveNext())
399386
{
400-
_logger.Warn(CultureInfo.InvariantCulture, "No previous file for {0} found", fileName);
387+
fileName = enumerator.Current.FullName;
388+
node = fileNameList.Find(fileName);
389+
if (node == null)
390+
{
391+
_logger.Warn(CultureInfo.InvariantCulture, "File {0} not found", fileName);
392+
continue;
393+
}
394+
395+
if (node.Previous != null)
396+
{
397+
fileName = node.Previous.Value;
398+
_logger.Debug(CultureInfo.InvariantCulture, "New name is {0}", fileName);
399+
readNewILogFileInfoList.Add(GetLogFileInfo(fileName));
400+
}
401+
else
402+
{
403+
_logger.Warn(CultureInfo.InvariantCulture, "No previous file for {0} found", fileName);
404+
}
401405
}
402406
}
403407
}
408+
else
409+
{
410+
_logger.Info(CultureInfo.InvariantCulture, "{0} does not exist", fileName);
411+
lostILogFileInfoList.Add(logFileInfo);
412+
}
404413
}
405-
else
414+
415+
if (lostILogFileInfoList.Count > 0)
406416
{
407-
_logger.Info(CultureInfo.InvariantCulture, "{0} does not exist", fileName);
408-
lostILogFileInfoList.Add(logFileInfo);
409-
}
410-
}
417+
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for lost files");
411418

412-
if (lostILogFileInfoList.Count > 0)
413-
{
414-
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for lost files");
419+
foreach (var logFileInfo in lostILogFileInfoList)
420+
{
421+
var lastDeletedBufferInfo = DeleteBuffersForInfo(logFileInfo, false);
422+
if (lastDeletedBufferInfo != null)
423+
{
424+
offset += lastDeletedBufferInfo.Value.StartLine + lastDeletedBufferInfo.Value.LineCount;
425+
}
426+
}
415427

416-
foreach (var logFileInfo in lostILogFileInfoList)
417-
{
418-
var lastDeletedBufferInfo = DeleteBuffersForInfo(logFileInfo, false);
419-
if (lastDeletedBufferInfo != null)
428+
_logger.Info(CultureInfo.InvariantCulture, "Adjusting StartLine values in {0} buffers by offset {1}", _bufferList.Count, offset);
429+
foreach (var buffer in _bufferList.Values.ToList())
420430
{
421-
offset += lastDeletedBufferInfo.Value.StartLine + lastDeletedBufferInfo.Value.LineCount;
431+
SetNewStartLineForBuffer(buffer, buffer.StartLine - offset);
422432
}
423-
}
424433

425-
_logger.Info(CultureInfo.InvariantCulture, "Adjusting StartLine values in {0} buffers by offset {1}", _bufferList.Count, offset);
426-
foreach (var buffer in _bufferList.Values)
427-
{
428-
SetNewStartLineForBuffer(buffer, buffer.StartLine - offset);
434+
#if DEBUG
435+
if (_bufferList.Values.Count > 0)
436+
{
437+
_logger.Debug(CultureInfo.InvariantCulture, "First buffer now has StartLine {0}", _bufferList.Values[0].StartLine);
438+
}
439+
#endif
429440
}
430441

431-
#if DEBUG
432-
if (_bufferList.Values.Count > 0)
442+
// Read anew all buffers following a buffer info that couldn't be matched with the corresponding existing file
443+
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for files that must be re-read");
444+
445+
foreach (var iLogFileInfo in readNewILogFileInfoList)
433446
{
434-
_logger.Debug(CultureInfo.InvariantCulture, "First buffer now has StartLine {0}", _bufferList.Values[0].StartLine);
447+
DeleteBuffersForInfo(iLogFileInfo, true);
435448
}
436-
#endif
437-
}
438449

439-
// Read anew all buffers following a buffer info that couldn't be matched with the corresponding existing file
440-
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for files that must be re-read");
450+
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for the watched file");
441451

442-
foreach (var iLogFileInfo in readNewILogFileInfoList)
443-
{
444-
DeleteBuffersForInfo(iLogFileInfo, true);
445-
}
452+
DeleteBuffersForInfo(_watchedILogFileInfo, true);
446453

447-
_logger.Info(CultureInfo.InvariantCulture, "Deleting buffers for the watched file");
454+
_logger.Info(CultureInfo.InvariantCulture, "Re-Reading files");
448455

449-
DeleteBuffersForInfo(_watchedILogFileInfo, true);
456+
foreach (var iLogFileInfo in readNewILogFileInfoList)
457+
{
458+
ReadToBufferList(iLogFileInfo, 0, LineCount);
459+
newFileInfoList.Add(iLogFileInfo);
460+
}
450461

451-
_logger.Info(CultureInfo.InvariantCulture, "Re-Reading files");
462+
_logFileInfoList = newFileInfoList;
463+
_watchedILogFileInfo = GetLogFileInfo(_watchedILogFileInfo.FullName);
464+
_logFileInfoList.Add(_watchedILogFileInfo);
465+
_logger.Info(CultureInfo.InvariantCulture, "Reading watched file");
452466

453-
foreach (var iLogFileInfo in readNewILogFileInfoList)
454-
{
455-
ReadToBufferList(iLogFileInfo, 0, LineCount);
456-
newFileInfoList.Add(iLogFileInfo);
467+
ReadToBufferList(_watchedILogFileInfo, 0, LineCount);
457468
}
458469

459-
_logFileInfoList = newFileInfoList;
460-
_watchedILogFileInfo = GetLogFileInfo(_watchedILogFileInfo.FullName);
461-
_logFileInfoList.Add(_watchedILogFileInfo);
462-
_logger.Info(CultureInfo.InvariantCulture, "Reading watched file");
470+
_logger.Info(CultureInfo.InvariantCulture, "ShiftBuffers() end. offset={0}", offset);
463471

464-
ReadToBufferList(_watchedILogFileInfo, 0, LineCount);
472+
return offset;
473+
}
474+
finally
475+
{
476+
ReleaseBufferListWriterLock();
465477
}
466-
467-
_logger.Info(CultureInfo.InvariantCulture, "ShiftBuffers() end. offset={0}", offset);
468-
469-
ReleaseBufferListWriterLock();
470-
471-
return offset;
472478
}
473479

474480
/// <summary>

src/LogExpert.Tests/BufferShiftTest.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public void Boot ()
2727
[Test]
2828
[TestCase(ReaderType.System)]
2929
//[TestCase(ReaderType.Legacy)] Legacy Reader does not Support this
30+
//TO Test real life scenario, use the LogRotator tool, in the src/Tools/LogRotator directory,
31+
//to create files and perform rollovers while watching the files in LogExpert with MultiFile enabled
32+
//(pattern: *$J(.))
3033
public void TestShiftBuffers1 (ReaderType readerType)
3134
{
3235
var linesPerFile = 10;
@@ -118,7 +121,6 @@ public void TestShiftBuffers1 (ReaderType readerType)
118121
_ = enumerator.MoveNext();
119122
}
120123

121-
_ = enumerator.MoveNext();
122124
// the last 2 files now contain the content of the previously watched file
123125
for (; i < logBuffers.Count; ++i)
124126
{

src/LogExpert.Tests/RolloverHandlerTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ protected static LinkedList<string> RolloverSimulation (LinkedList<string> files
6363
_ = enumerator.MoveNext();
6464
}
6565

66-
_ = CreateFile(null, nextEnumerator.Current);
66+
_ = CreateFile(null, enumerator.Current);
6767

6868
if (deleteLatestFile)
6969
{

src/tools/LogRotator/LogRotator.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
using System.Text;
2+
3+
const string baseDir = "logs";
4+
const string baseName = "engine.log";
5+
const int maxBackups = 6;
6+
const int linesPerFile = 50;
7+
8+
Directory.CreateDirectory(baseDir);
9+
10+
// Create initial set of files
11+
Console.WriteLine($"Creating initial files in '{Path.GetFullPath(baseDir)}'...");
12+
WriteLogFile(Path.Combine(baseDir, baseName), 0);
13+
14+
for (var i = 1; i <= maxBackups; i++)
15+
{
16+
WriteLogFile(Path.Combine(baseDir, $"{baseName}.{i}"), i);
17+
}
18+
19+
PrintFiles();
20+
Console.WriteLine();
21+
Console.WriteLine("Open the 'engine.log' file in LogExpert with MultiFile enabled (pattern: *$J(.))");
22+
Console.WriteLine("Press ENTER to perform a rotation (with oldest file deletion), or Q to quit.");
23+
24+
var rotationCount = 0;
25+
26+
while (true)
27+
{
28+
var key = Console.ReadKey(true);
29+
30+
if (key.Key == ConsoleKey.Q)
31+
{
32+
break;
33+
}
34+
35+
if (key.Key != ConsoleKey.Enter)
36+
{
37+
continue;
38+
}
39+
40+
rotationCount++;
41+
Console.WriteLine($"\n--- Rotation #{rotationCount} ---");
42+
43+
// Delete the oldest file (simulates maxBackups limit)
44+
var oldest = Path.Combine(baseDir, $"{baseName}.{maxBackups}");
45+
46+
if (File.Exists(oldest))
47+
{
48+
File.Delete(oldest);
49+
Console.WriteLine($" Deleted: {baseName}.{maxBackups}");
50+
}
51+
52+
// Shift all numbered files up by one
53+
for (var i = maxBackups - 1; i >= 1; i--)
54+
{
55+
var src = Path.Combine(baseDir, $"{baseName}.{i}");
56+
var dst = Path.Combine(baseDir, $"{baseName}.{i + 1}");
57+
58+
if (File.Exists(src))
59+
{
60+
File.Move(src, dst);
61+
Console.WriteLine($" Renamed: {baseName}.{i} -> {baseName}.{i + 1}");
62+
}
63+
}
64+
65+
// Rename current log to .1
66+
var current = Path.Combine(baseDir, baseName);
67+
var first = Path.Combine(baseDir, $"{baseName}.1");
68+
69+
if (File.Exists(current))
70+
{
71+
File.Move(current, first);
72+
Console.WriteLine($" Renamed: {baseName} -> {baseName}.1");
73+
}
74+
75+
// Create empty file first (like real log frameworks do), so LogExpert detects
76+
// newSize < oldSize and triggers ShiftBuffers()
77+
File.Create(current).Dispose();
78+
Console.WriteLine($" Created: {baseName} (empty - triggers rollover detection)");
79+
80+
PrintFiles();
81+
82+
// Wait for LogExpert's poll interval to detect the smaller file, then write content
83+
Console.WriteLine(" Waiting 2s for LogExpert to detect rollover...");
84+
Thread.Sleep(2000);
85+
86+
WriteLogFile(current, maxBackups + rotationCount);
87+
Console.WriteLine($" Wrote content to {baseName}");
88+
89+
PrintFiles();
90+
Console.WriteLine("\nPress ENTER for next rotation, Q to quit.");
91+
}
92+
93+
static void WriteLogFile(string path, int fileId)
94+
{
95+
using var writer = new StreamWriter(path, false, Encoding.UTF8);
96+
97+
for (var i = 1; i <= linesPerFile; i++)
98+
{
99+
writer.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [INFO] File#{fileId:D3} Line {i:D3} - {Path.GetFileName(path)} - Sample log message");
100+
}
101+
}
102+
103+
static void PrintFiles()
104+
{
105+
Console.WriteLine("\nCurrent files on disk:");
106+
107+
foreach (var f in Directory.GetFiles(baseDir, $"{baseName}*").OrderBy(f => f))
108+
{
109+
Console.WriteLine($" {Path.GetFileName(f)} ({new FileInfo(f).Length} bytes)");
110+
}
111+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net10.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
</PropertyGroup>
7+
</Project>

0 commit comments

Comments
 (0)