Skip to content

Commit 857d344

Browse files
Compress for cdb output files
1 parent 8fd7504 commit 857d344

File tree

3 files changed

+125
-2
lines changed

3 files changed

+125
-2
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
<PropertyGroup Label="Default Versioning">
4040
<!-- Default version for all projects (can be overridden per project) -->
4141
<VersionPrefix>1.1.2</VersionPrefix>
42-
<VersionSuffix>3</VersionSuffix>
42+
<VersionSuffix>4</VersionSuffix>
4343

4444
<!-- Calculated version -->
4545
<Version Condition="'$(Version)' == ''">$(VersionPrefix).$(VersionSuffix)</Version>

nexus_engine/nexus_engine/Internal/CdbSession.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics;
2+
using System.IO.Compression;
23
using System.Text;
34

45
using Nexus.Config;
@@ -233,6 +234,7 @@ public async Task DisposeAsync()
233234
{
234235
await SendQuitCommandAsync();
235236
await WaitForProcessExitAsync();
237+
await CompressCdbLogIfAvailableAsync();
236238
DisposeResources();
237239
}
238240
catch (Exception ex)
@@ -499,7 +501,7 @@ protected void CleanupOldCdbLogs(string sessionsDirectory, int retentionDays)
499501
try
500502
{
501503
var directoryInfo = m_FileSystem.GetDirectoryInfo(sessionsDirectory);
502-
var files = directoryInfo.GetFiles("cdb_*.log");
504+
var files = directoryInfo.GetFiles("cdb_*.log*");
503505

504506
if (files.Length == 0)
505507
{
@@ -542,6 +544,50 @@ protected void CleanupOldCdbLogs(string sessionsDirectory, int retentionDays)
542544
}
543545
}
544546

547+
/// <summary>
548+
/// Attempts to compress the current session's CDB log file using GZip in a best-effort manner.
549+
/// </summary>
550+
/// <returns>A task that represents the asynchronous compression operation.</returns>
551+
private async Task CompressCdbLogIfAvailableAsync()
552+
{
553+
try
554+
{
555+
if (string.IsNullOrWhiteSpace(SessionId))
556+
{
557+
return;
558+
}
559+
560+
var cdbLogFilePath = GetCdbSessionBasedLogPath(SessionId);
561+
562+
if (!File.Exists(cdbLogFilePath))
563+
{
564+
return;
565+
}
566+
567+
var compressedPath = $"{cdbLogFilePath}.gz";
568+
569+
if (File.Exists(compressedPath))
570+
{
571+
return;
572+
}
573+
574+
using (var sourceStream = new FileStream(cdbLogFilePath, FileMode.Open, FileAccess.Read, FileShare.Read))
575+
using (var destinationStream = new FileStream(compressedPath, FileMode.Create, FileAccess.Write, FileShare.None))
576+
using (var gzipStream = new GZipStream(destinationStream, CompressionLevel.Optimal))
577+
{
578+
await sourceStream.CopyToAsync(gzipStream).ConfigureAwait(false);
579+
}
580+
581+
File.Delete(cdbLogFilePath);
582+
583+
m_Logger.Info("Compressed CDB log for session {SessionId} to {CompressedPath}", SessionId, compressedPath);
584+
}
585+
catch (Exception ex)
586+
{
587+
m_Logger.Info(ex, "Failed to compress CDB log for session {SessionId}. Keeping uncompressed log file.", SessionId);
588+
}
589+
}
590+
545591
/// <summary>
546592
/// Builds the command line arguments for starting the CDB process.
547593
/// </summary>

unittests/nexus_engine/nexus_engine_unittests/Internal/CdbSessionTests.cs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
using Nexus.External.Apis.FileSystem;
1414
using Nexus.External.Apis.ProcessManagement;
1515

16+
using NLog;
17+
using NLog.Config;
18+
using NLog.Targets;
19+
1620
using Xunit;
1721

1822
namespace Nexus.Engine.Tests.Internal;
@@ -594,12 +598,18 @@ public void CleanupOldCdbLogs_DeletesFilesOlderThanRetentionDays()
594598

595599
var oldFilePath = Path.Combine(sessionsDirectory, "cdb_old.log");
596600
var recentFilePath = Path.Combine(sessionsDirectory, "cdb_recent.log");
601+
var oldCompressedFilePath = Path.Combine(sessionsDirectory, "cdb_old.log.gz");
602+
var recentCompressedFilePath = Path.Combine(sessionsDirectory, "cdb_recent.log.gz");
597603

598604
File.WriteAllText(oldFilePath, "old");
599605
File.WriteAllText(recentFilePath, "recent");
606+
File.WriteAllText(oldCompressedFilePath, "old_compressed");
607+
File.WriteAllText(recentCompressedFilePath, "recent_compressed");
600608

601609
File.SetCreationTime(oldFilePath, DateTime.Now.AddDays(-10));
610+
File.SetCreationTime(oldCompressedFilePath, DateTime.Now.AddDays(-10));
602611
File.SetCreationTime(recentFilePath, DateTime.Now);
612+
File.SetCreationTime(recentCompressedFilePath, DateTime.Now);
603613

604614
try
605615
{
@@ -615,8 +625,11 @@ public void CleanupOldCdbLogs_DeletesFilesOlderThanRetentionDays()
615625

616626
// Assert
617627
_ = File.Exists(oldFilePath).Should().BeFalse();
628+
_ = File.Exists(oldCompressedFilePath).Should().BeFalse();
618629
_ = File.Exists(recentFilePath).Should().BeTrue();
630+
_ = File.Exists(recentCompressedFilePath).Should().BeTrue();
619631
m_MockFileSystem.Verify(fs => fs.DeleteFile(oldFilePath), Times.Once);
632+
m_MockFileSystem.Verify(fs => fs.DeleteFile(oldCompressedFilePath), Times.Once);
620633
}
621634
finally
622635
{
@@ -1373,4 +1386,68 @@ public async Task InitializeAsync_WhenInitializationFails_DisposesSession()
13731386
// Assert - Session should be disposed after initialization failure
13741387
_ = session.IsInitialized.Should().BeFalse();
13751388
}
1389+
1390+
/// <summary>
1391+
/// Verifies that disposing the session compresses the CDB log file and deletes the original log.
1392+
/// </summary>
1393+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
1394+
[Fact]
1395+
public async Task DisposeAsync_CompressesCdbLog_AndDeletesOriginalLog()
1396+
{
1397+
// Arrange
1398+
var originalConfig = LogManager.Configuration;
1399+
var tempRoot = Path.Combine(Path.GetTempPath(), "MCPNexusCdbCompressionTests", Guid.NewGuid().ToString("N"));
1400+
var logsDirectory = Path.Combine(tempRoot, "Logs");
1401+
_ = Directory.CreateDirectory(logsDirectory);
1402+
1403+
try
1404+
{
1405+
var config = new LoggingConfiguration();
1406+
var mainLogPath = Path.Combine(logsDirectory, "main.log");
1407+
var fileTarget = new FileTarget("mainFile")
1408+
{
1409+
FileName = mainLogPath,
1410+
};
1411+
config.AddTarget(fileTarget);
1412+
config.AddRuleForAllLevels(fileTarget);
1413+
LogManager.Configuration = config;
1414+
1415+
var accessor = new CdbSessionTestAccessor(m_Settings.Object, m_MockFileSystem.Object, m_MockProcessManager.Object);
1416+
1417+
var sessionId = "session-compress";
1418+
var sessionIdProperty = typeof(CdbSession).GetProperty("SessionId", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
1419+
sessionIdProperty!.SetValue(accessor, sessionId);
1420+
1421+
var getLogPathMethod = typeof(CdbSession).GetMethod("GetCdbSessionBasedLogPath", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
1422+
var logPath = (string)getLogPathMethod!.Invoke(accessor, new object[] { sessionId })!;
1423+
1424+
var logDirectory = Path.GetDirectoryName(logPath);
1425+
if (!string.IsNullOrEmpty(logDirectory))
1426+
{
1427+
_ = Directory.CreateDirectory(logDirectory);
1428+
}
1429+
1430+
const string logContent = "cdb log content";
1431+
File.WriteAllText(logPath, logContent);
1432+
1433+
// Act
1434+
await accessor.DisposeAsync();
1435+
1436+
// Assert
1437+
var compressedPath = $"{logPath}.gz";
1438+
_ = File.Exists(logPath).Should().BeFalse();
1439+
_ = File.Exists(compressedPath).Should().BeTrue();
1440+
var compressedFileInfo = new FileInfo(compressedPath);
1441+
_ = compressedFileInfo.Length.Should().BeGreaterThan(0);
1442+
}
1443+
finally
1444+
{
1445+
LogManager.Configuration = originalConfig;
1446+
1447+
if (Directory.Exists(tempRoot))
1448+
{
1449+
Directory.Delete(tempRoot, true);
1450+
}
1451+
}
1452+
}
13761453
}

0 commit comments

Comments
 (0)