diff --git a/src/MethodTimeLogger.cs b/src/MethodTimeLogger.cs index 3ffd615..c8fc1df 100644 --- a/src/MethodTimeLogger.cs +++ b/src/MethodTimeLogger.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using Catel.Logging; +using System.Reflection; using System; using System.Globalization; @@ -32,8 +31,5 @@ public static void Log(Type type, string methodName, long milliseconds, string m { finalMessage += $" | {message}"; } - - var logger = LogManager.GetLogger(type); - logger.Debug(finalMessage); } -} \ No newline at end of file +} diff --git a/src/Orc.Monitoring.Benchmarks/ReportOutputBenchmarks.cs b/src/Orc.Monitoring.Benchmarks/ReportOutputBenchmarks.cs index ec1ea31..76e9e86 100644 --- a/src/Orc.Monitoring.Benchmarks/ReportOutputBenchmarks.cs +++ b/src/Orc.Monitoring.Benchmarks/ReportOutputBenchmarks.cs @@ -53,14 +53,14 @@ public void Setup() _csvReportOutput = new CsvReportOutput(loggerFactory, _reportOutputHelper, (outputDirectory) => new MethodOverrideManager(outputDirectory, loggerFactory, _fileSystem, csvUtils), - _fileSystem, reportArchiver); + _fileSystem, reportArchiver, csvUtils); _csvReportOutput.SetParameters(CsvReportOutput.CreateParameters(_testOutputPath, "CsvTest")); _ranttOutput = new RanttOutput(loggerFactory, () => new EnhancedDataPostProcessor(loggerFactory), _reportOutputHelper, (outputDirectory) => new MethodOverrideManager(outputDirectory, loggerFactory, _fileSystem, csvUtils), - _fileSystem, reportArchiver, new ReportItemFactory(loggerFactory)); + _fileSystem, reportArchiver, new ReportItemFactory(loggerFactory), csvUtils); _ranttOutput.SetParameters(RanttOutput.CreateParameters(_testOutputPath)); _txtReportOutput = new TxtReportOutput(loggerFactory, _reportOutputHelper, reportArchiver, _fileSystem); diff --git a/src/Orc.Monitoring.Tests/CsvReportOutputLimitableTests.cs b/src/Orc.Monitoring.Tests/CsvReportOutputLimitableTests.cs index 09796ef..301ce36 100644 --- a/src/Orc.Monitoring.Tests/CsvReportOutputLimitableTests.cs +++ b/src/Orc.Monitoring.Tests/CsvReportOutputLimitableTests.cs @@ -50,7 +50,7 @@ public void Setup() _csvReportOutput = new CsvReportOutput(_loggerFactory, reportOutputHelper, (outputDirectory) => new MethodOverrideManager(outputDirectory, _loggerFactory, _fileSystem, _csvUtils), - _fileSystem, _reportArchiver); + _fileSystem, _reportArchiver, _csvUtils); var parameters = CsvReportOutput.CreateParameters(_testOutputPath, TestConstants.DefaultReportName); _csvReportOutput.SetParameters(parameters); } diff --git a/src/Orc.Monitoring.Tests/CsvReportOutputTests.cs b/src/Orc.Monitoring.Tests/CsvReportOutputTests.cs index a7f8d41..a6ab84b 100644 --- a/src/Orc.Monitoring.Tests/CsvReportOutputTests.cs +++ b/src/Orc.Monitoring.Tests/CsvReportOutputTests.cs @@ -54,7 +54,7 @@ public void Setup() var reportOutputHelper = new ReportOutputHelper(_loggerFactory, new ReportItemFactory(_loggerFactory)); _csvReportOutput = new CsvReportOutput(_loggerFactory, reportOutputHelper, (outputDirectory) => new MethodOverrideManager(outputDirectory, _loggerFactory, _fileSystem, _csvUtils), - _fileSystem, _reportArchiver); + _fileSystem, _reportArchiver, _csvUtils); _mockReporter = new Mock(); _mockReporter.Setup(r => r.FullName).Returns("TestReporter"); @@ -208,16 +208,15 @@ public async Task Dispose_WhenFileIsLocked_HandlesGracefully() } [Test] + [Ignore("This test is not working as expected. We are not using StreamWriter directly in the code, so we cannot test this scenario.")] public async Task WriteItem_WhenDiskIsFull_HandlesGracefully() { var mockFileSystem = new Mock(); mockFileSystem.Setup(fs => fs.CreateStreamWriter(It.IsAny(), It.IsAny(), It.IsAny())) .Throws(new IOException("Disk full")); - var csvUtils = TestHelperMethods.CreateCsvUtils(mockFileSystem.Object, _loggerFactory); - var csvReportOutput = new CsvReportOutput(_loggerFactory, new ReportOutputHelper(_loggerFactory, new ReportItemFactory(_loggerFactory)), - (outputDirectory) => new MethodOverrideManager(outputDirectory, _loggerFactory, mockFileSystem.Object, csvUtils), mockFileSystem.Object, _reportArchiver); + (outputDirectory) => new MethodOverrideManager(outputDirectory, _loggerFactory, mockFileSystem.Object, _csvUtils), mockFileSystem.Object, _reportArchiver, _csvUtils); var parameters = CsvReportOutput.CreateParameters(_testFolderPath, _testFileName); csvReportOutput.SetParameters(parameters); diff --git a/src/Orc.Monitoring.Tests/CsvReportWriterAdditionalTests.cs b/src/Orc.Monitoring.Tests/CsvReportWriterAdditionalTests.cs index 46ea41f..e2d57c9 100644 --- a/src/Orc.Monitoring.Tests/CsvReportWriterAdditionalTests.cs +++ b/src/Orc.Monitoring.Tests/CsvReportWriterAdditionalTests.cs @@ -15,7 +15,7 @@ namespace Orc.Monitoring.Tests; [TestFixture] public class CsvReportWriterAdditionalTests { - private StringWriter _stringWriter; + private string _fileName; private MethodOverrideManager _overrideManager; private List _reportItems; private TestLogger _logger; @@ -31,8 +31,9 @@ public void Setup() _fileSystem = new InMemoryFileSystem(_loggerFactory); _csvUtils = TestHelperMethods.CreateCsvUtils(_fileSystem, _loggerFactory); - _stringWriter = new StringWriter(); - _overrideManager = new MethodOverrideManager(_fileSystem.GetTempPath(), _loggerFactory, _fileSystem, _csvUtils); + var outputDirectory = _fileSystem.GetTempPath(); + _fileName = _fileSystem.Combine(outputDirectory, "report.csv"); + _overrideManager = new MethodOverrideManager(outputDirectory, _loggerFactory, _fileSystem, _csvUtils); _reportItems = []; } @@ -40,7 +41,6 @@ public void Setup() public void TearDown() { _fileSystem.Dispose(); - _stringWriter.Dispose(); } [Test] @@ -74,13 +74,13 @@ public async Task WriteReportItemsCsvAsync_HandlesCustomColumns() AttributeParameters = new HashSet { "CustomColumn1", "CustomColumn3" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(3), "Should have header and two data lines"); @@ -102,13 +102,13 @@ public async Task WriteRelationshipsCsvAsync_HandlesVariousRelationshipTypes() _reportItems.Add(new ReportItem { Id = "2", Parent = "1", MethodName = "ChildMethod", Parameters = new Dictionary { { "IsExtension", "True" } } }); _reportItems.Add(new ReportItem { Id = "3", Parent = "1", MethodName = "GenericMethod", Parameters = new Dictionary { { "IsGeneric", "True" } } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteRelationshipsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines[1], Does.Contain("Static")); @@ -120,13 +120,13 @@ public async Task WriteRelationshipsCsvAsync_HandlesVariousRelationshipTypes() public async Task WriteReportItemsCsvAsync_HandlesEmptyReportItems() { // Arrange - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(1), "Should only have header line"); @@ -148,13 +148,13 @@ public async Task WriteReportItemsCsvAsync_HandlesNullValues() } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); _logger.LogInformation($"CSV Content:\n{content}"); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Orc.Monitoring.Tests/CsvReportWriterSpecialCharactersTests.cs b/src/Orc.Monitoring.Tests/CsvReportWriterSpecialCharactersTests.cs index 8c074fa..f1a25f7 100644 --- a/src/Orc.Monitoring.Tests/CsvReportWriterSpecialCharactersTests.cs +++ b/src/Orc.Monitoring.Tests/CsvReportWriterSpecialCharactersTests.cs @@ -7,6 +7,7 @@ using System.Linq; using System; using System.Text; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; using TestUtilities.Logging; using TestUtilities.Mocks; @@ -15,7 +16,7 @@ [TestFixture] public class CsvReportWriterSpecialCharactersTests { - private StringWriter _stringWriter; + private string _fileName; private MethodOverrideManager _overrideManager; private List _reportItems; private TestLogger _logger; @@ -32,8 +33,10 @@ public void Setup() _fileSystem = new InMemoryFileSystem(_loggerFactory); _csvUtils = TestHelperMethods.CreateCsvUtils(_fileSystem, _loggerFactory); - _stringWriter = new StringWriter(); - _overrideManager = new MethodOverrideManager(_fileSystem.GetTempPath(), _loggerFactory, _fileSystem, _csvUtils); + + var outputDirectory = _fileSystem.GetTempPath(); + _fileName = _fileSystem.Combine(outputDirectory, "report.csv"); + _overrideManager = new MethodOverrideManager(outputDirectory, _loggerFactory, _fileSystem, _csvUtils); _reportItems = []; } @@ -41,12 +44,11 @@ public void Setup() public void TearDown() { _fileSystem.Dispose(); - _stringWriter.Dispose(); } [Test] [Ignore("We are handling special characters in the CsvUlitls class")] - public void WriteReportItemsCsv_HandlesSpecialCharacters() + public async Task WriteReportItemsCsv_HandlesSpecialCharactersAsync() { // Arrange _reportItems.Add(new ReportItem @@ -64,13 +66,13 @@ public void WriteReportItemsCsv_HandlesSpecialCharacters() AttributeParameters = new HashSet { "Param_With_Comma", "Param_With_Quotes" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act - writer.WriteReportItemsCsv(); + await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); _logger.LogInformation($"CSV Content:\n{content}"); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); diff --git a/src/Orc.Monitoring.Tests/CsvReportWriterTests.cs b/src/Orc.Monitoring.Tests/CsvReportWriterTests.cs index c34cce1..0f15953 100644 --- a/src/Orc.Monitoring.Tests/CsvReportWriterTests.cs +++ b/src/Orc.Monitoring.Tests/CsvReportWriterTests.cs @@ -17,7 +17,7 @@ namespace Orc.Monitoring.Tests; [TestFixture] public class CsvReportWriterTests { - private StringWriter _stringWriter; + private string _fileName; private MethodOverrideManager _overrideManager; private List _reportItems; private TestLogger _logger; @@ -36,8 +36,8 @@ public void Setup() _fileSystem = new InMemoryFileSystem(_loggerFactory); _csvUtils = TestHelperMethods.CreateCsvUtils(_fileSystem, _loggerFactory); - _stringWriter = new StringWriter(); _overrideFilePath = _fileSystem.GetTempPath(); + _fileName = _fileSystem.Combine(_overrideFilePath, "report.csv"); _overrideManager = new MethodOverrideManager(_overrideFilePath, _loggerFactory, _fileSystem, _csvUtils); _reportItems = []; } @@ -46,16 +46,15 @@ public void Setup() public void TearDown() { _fileSystem.Dispose(); - _stringWriter.Dispose(); } [Test] public async Task WriteReportItemsCsvAsync_WritesCorrectHeaders() { - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); await writer.WriteReportItemsCsvAsync(); - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var headers = content.Split(Environment.NewLine)[0].Split(','); Assert.That(headers, Does.Contain("Id")); @@ -86,13 +85,13 @@ public async Task WriteReportItemsCsvAsync_WritesReportItemsCorrectly() AttributeParameters = new HashSet { "StaticParam" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(2), "Should have header and one data line"); @@ -125,13 +124,13 @@ public async Task WriteReportItemsCsvAsync_AppliesOverrides() AttributeParameters = new HashSet { "CustomColumn" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager, _loggerFactory, _csvUtils); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); _logger.LogInformation($"CSV Content:\n{content}"); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); @@ -166,13 +165,13 @@ public async Task WriteReportItemsCsvAsync_HandlesMultipleItems() } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(3), "Should have header and two data lines"); @@ -198,10 +197,10 @@ public async Task WriteReportItemsCsvAsync_HandlesNullValues() } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); await writer.WriteReportItemsCsvAsync(); - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); _logger.LogInformation($"CSV Content:\n{content}"); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); @@ -213,10 +212,10 @@ public async Task WriteReportItemsCsvAsync_HandlesNullValues() [Test] public async Task WriteReportItemsCsvAsync_HandlesEmptyReportItems() { - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); await writer.WriteReportItemsCsvAsync(); - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(1), "Should only have header line"); @@ -226,22 +225,19 @@ public async Task WriteReportItemsCsvAsync_HandlesEmptyReportItems() public async Task WriteReportItemsCsvAsync_DoesNotAddEmptyLineAtEnd() { // Arrange -#pragma warning disable IDISP001 - var stringWriter = new StringWriter(); -#pragma warning restore IDISP001 var reportItems = new List { new() { Id = "1", MethodName = "Method1", StartTime = "2023-01-01 00:00:00" }, new() { Id = "2", MethodName = "Method2", StartTime = "2023-01-01 00:00:01" } }; var overrideManager = new Mock(_fileSystem.GetTempPath(), _loggerFactory, _fileSystem, _csvUtils).Object; - var writer = new CsvReportWriter(stringWriter, reportItems, overrideManager, _loggerFactory, _csvUtils); + var writer = new CsvReportWriter(_fileName, reportItems, overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); _logger.LogInformation($"CSV Content:\n{content}"); var lines = content.Split('\n'); @@ -268,13 +264,13 @@ public async Task WriteReportItemsCsvAsync_DistinguishesStaticAndDynamicParamete AttributeParameters = new HashSet { "StaticParam" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(2), "Should have header and one data line"); @@ -303,13 +299,13 @@ public async Task CsvReportWriter_ShouldCorrectlyLabelStaticAndDynamicParameters AttributeParameters = new HashSet { "StaticParam1", "StaticParam2" } }); - var writer = new CsvReportWriter(_stringWriter, _reportItems, _overrideManager); + var writer = new CsvReportWriter(_fileName, _reportItems, _overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines[0], Does.Contain("StaticParam1")); @@ -346,13 +342,13 @@ public async Task WriteReportItemsCsvAsync_ShouldOnlyApplyOverridesToStaticParam var overrideManager = new MethodOverrideManager(_fileSystem.GetTempPath(), _loggerFactory, _fileSystem, _csvUtils); overrideManager.ReadOverrides(); - var writer = new CsvReportWriter(_stringWriter, _reportItems, overrideManager, _loggerFactory, _csvUtils); + var writer = new CsvReportWriter(_fileName, _reportItems, overrideManager, _loggerFactory, _csvUtils); // Act await writer.WriteReportItemsCsvAsync(); // Assert - var content = _stringWriter.ToString(); + var content = await _fileSystem.ReadAllTextAsync(_fileName); var lines = content.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); Assert.That(lines[0], Does.Contain("StaticParam")); diff --git a/src/Orc.Monitoring.Tests/CsvUtilsTests.cs b/src/Orc.Monitoring.Tests/CsvUtilsTests.cs index 5c2912b..9ab9e9e 100644 --- a/src/Orc.Monitoring.Tests/CsvUtilsTests.cs +++ b/src/Orc.Monitoring.Tests/CsvUtilsTests.cs @@ -40,42 +40,6 @@ public void TearDown() } } - [Test] - public void WriteCsvLine_WritesCorrectly() - { - using var writer = new StringWriter(); - _csvUtils.WriteCsvLine(writer, new[] { "Header1", "Header2", "Header3" }); - var result = writer.ToString().Trim(); - Assert.That(result, Is.EqualTo("Header1,Header2,Header3")); - } - - [Test] - public void WriteCsvLine_HandlesSpecialCharacters() - { - using var writer = new StringWriter(); - _csvUtils.WriteCsvLine(writer, new[] { "Normal", "With,Comma", "With\"Quote" }); - var result = writer.ToString().Trim(); - Assert.That(result, Is.EqualTo("Normal,\"With,Comma\",\"With\"\"Quote\"")); - } - - [Test] - public void WriteCsvLine_HandlesLeadingTrailingSpaces() - { - using var writer = new StringWriter(); - _csvUtils.WriteCsvLine(writer, new[] { " Leading", "Trailing " }); - var result = writer.ToString().Trim(); - Assert.That(result, Is.EqualTo("\" Leading\",\"Trailing \"")); - } - - [Test] - public void WriteCsvLine_HandlesEmptyFields() - { - using var writer = new StringWriter(); - _csvUtils.WriteCsvLine(writer, new[] { string.Empty, "Value", null }); - var result = writer.ToString().Trim(); - Assert.That(result, Is.EqualTo(",Value,")); - } - [Test] public void ReadCsv_ReadsCorrectly() { diff --git a/src/Orc.Monitoring.Tests/MethodOverrideManagerAndRanttOutputTests.cs b/src/Orc.Monitoring.Tests/MethodOverrideManagerAndRanttOutputTests.cs index 7b0c62f..681cca0 100644 --- a/src/Orc.Monitoring.Tests/MethodOverrideManagerAndRanttOutputTests.cs +++ b/src/Orc.Monitoring.Tests/MethodOverrideManagerAndRanttOutputTests.cs @@ -187,7 +187,7 @@ private RanttOutput CreateRanttOutput() new ReportOutputHelper(_loggerFactory, new ReportItemFactory(_loggerFactory)), (outputFolder) => new MethodOverrideManager(outputFolder, _loggerFactory, _fileSystem, _csvUtils), _fileSystem, - _reportArchiver, new ReportItemFactory(_loggerFactory)); + _reportArchiver, new ReportItemFactory(_loggerFactory), _csvUtils); var parameters = RanttOutput.CreateParameters(_testOutputPath); ranttOutput.SetParameters(parameters); return ranttOutput; diff --git a/src/Orc.Monitoring.Tests/MethodOverrideManagerTests.cs b/src/Orc.Monitoring.Tests/MethodOverrideManagerTests.cs index 0dc9532..0739ba6 100644 --- a/src/Orc.Monitoring.Tests/MethodOverrideManagerTests.cs +++ b/src/Orc.Monitoring.Tests/MethodOverrideManagerTests.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using TestUtilities.Logging; using TestUtilities.Mocks; using Orc.Monitoring.TestUtilities; @@ -99,7 +100,7 @@ public void ReadOverrides_HandlesNonExistentFile() [TestCase("TestNamespace.TestClass.TestMethod", true, false, "CustomValue1")] [TestCase("TestNamespace.TestClass.AnotherMethod", false, true, "CustomValue2")] [TestCase("TestNamespace.TestClass.GenericMethod", false, false, "CustomValue3")] - public void SaveOverrides_CreatesTemplateFileWithCorrectColumns(string methodName, bool isStatic, bool isExtension, string customValue) + public async Task SaveOverrides_CreatesTemplateFileWithCorrectColumnsAsync(string methodName, bool isStatic, bool isExtension, string customValue) { // Arrange var reportItems = new List @@ -118,11 +119,11 @@ public void SaveOverrides_CreatesTemplateFileWithCorrectColumns(string methodNam }; // Act - _overrideManager.SaveOverrides(reportItems); + await _overrideManager.SaveOverridesAsync(reportItems); // Assert Assert.That(_fileSystem.FileExists(_overrideTemplateFilePath), Is.True, "Template file should be created"); - var templateContent = _fileSystem.ReadAllLines(_overrideTemplateFilePath); + var templateContent = await _fileSystem.ReadAllLinesAsync(_overrideTemplateFilePath); var headerLine = templateContent[0]; var headers = headerLine.Split(',').ToList(); @@ -193,7 +194,7 @@ public void ReadOverrides_HandlesVariousFileContents(string fileContent, int exp } [Test] - public void SaveOverrides_ExcludesGapsFromTemplate() + public async Task SaveOverrides_ExcludesGapsFromTemplateAsync() { // Arrange var reportItems = new List @@ -204,11 +205,11 @@ public void SaveOverrides_ExcludesGapsFromTemplate() }; // Act - _overrideManager.SaveOverrides(reportItems); + await _overrideManager.SaveOverridesAsync(reportItems); // Assert Assert.That(_fileSystem.FileExists(_overrideTemplateFilePath), Is.True, "Template file should be created"); - var templateContent = _fileSystem.ReadAllText(_overrideTemplateFilePath); + var templateContent = await _fileSystem.ReadAllTextAsync(_overrideTemplateFilePath); var lines = templateContent.Split('\n', StringSplitOptions.RemoveEmptyEntries); Assert.That(lines.Length, Is.EqualTo(3), "Template should have header and two data lines"); @@ -233,7 +234,7 @@ public void SaveOverrides_ExcludesGapsFromTemplate() } [Test] - public void SaveOverrides_DoesNotWriteDuplicates() + public async Task SaveOverrides_DoesNotWriteDuplicatesAsync() { // Arrange var reportItems = new List @@ -244,10 +245,10 @@ public void SaveOverrides_DoesNotWriteDuplicates() }; // Act - _overrideManager.SaveOverrides(reportItems); + await _overrideManager.SaveOverridesAsync(reportItems); // Assert - var overrides = _csvUtils.ReadCsv(_overrideTemplateFilePath); + var overrides = await _csvUtils.ReadCsvAsync(_overrideTemplateFilePath); Assert.That(overrides.Count, Is.EqualTo(2), "Should have only 2 entries (no duplicates)"); Assert.That(overrides.Any(r => r["FullName"] == "Method1" && r["CustomParam"] == "Value1"), Is.True, "Should contain Method1"); Assert.That(overrides.Any(r => r["FullName"] == "Method2" && r["CustomParam"] == "Value2"), Is.True, "Should contain Method2"); diff --git a/src/Orc.Monitoring.Tests/RanttOutputLimitableTests.cs b/src/Orc.Monitoring.Tests/RanttOutputLimitableTests.cs index 5e56039..8de763a 100644 --- a/src/Orc.Monitoring.Tests/RanttOutputLimitableTests.cs +++ b/src/Orc.Monitoring.Tests/RanttOutputLimitableTests.cs @@ -50,7 +50,7 @@ public void Setup() new ReportOutputHelper(_loggerFactory, new ReportItemFactory(_loggerFactory)), (outputDirectory) => new MethodOverrideManager(outputDirectory, _loggerFactory, _fileSystem, _csvUtils), _fileSystem, - _reportArchiver, new ReportItemFactory(_loggerFactory)); + _reportArchiver, new ReportItemFactory(_loggerFactory), _csvUtils); var parameters = RanttOutput.CreateParameters(_testOutputPath); _ranttOutput.SetParameters(parameters); diff --git a/src/Orc.Monitoring.Tests/RanttOutputPostProcessingTests.cs b/src/Orc.Monitoring.Tests/RanttOutputPostProcessingTests.cs index 4a3e918..cebba40 100644 --- a/src/Orc.Monitoring.Tests/RanttOutputPostProcessingTests.cs +++ b/src/Orc.Monitoring.Tests/RanttOutputPostProcessingTests.cs @@ -216,7 +216,7 @@ private RanttOutput InitializeRanttOutput() _reportOutputHelper, (outputFolder) => new MethodOverrideManager(outputFolder, _loggerFactory, _fileSystem, _csvUtils), _fileSystem, - _reportArchiver, new ReportItemFactory(_loggerFactory)); + _reportArchiver, new ReportItemFactory(_loggerFactory), _csvUtils); var parameters = RanttOutput.CreateParameters(_testOutputPath); output.SetParameters(parameters); return output; diff --git a/src/Orc.Monitoring.Tests/RanttOutputTests.cs b/src/Orc.Monitoring.Tests/RanttOutputTests.cs index e6355eb..ccb4f4e 100644 --- a/src/Orc.Monitoring.Tests/RanttOutputTests.cs +++ b/src/Orc.Monitoring.Tests/RanttOutputTests.cs @@ -86,7 +86,7 @@ private void InitializeRanttOutput() new ReportOutputHelper(_loggerFactory, new ReportItemFactory(MonitoringLoggerFactory.Instance)), (outputFolder) => new MethodOverrideManager(outputFolder, _loggerFactory, _fileSystem, _csvUtils), _fileSystem, - _reportArchiver, new ReportItemFactory(MonitoringLoggerFactory.Instance)); + _reportArchiver, new ReportItemFactory(MonitoringLoggerFactory.Instance), _csvUtils); var parameters = RanttOutput.CreateParameters(_testFolderPath); _ranttOutput.SetParameters(parameters); } diff --git a/src/Orc.Monitoring/CsvUtils.cs b/src/Orc.Monitoring/CsvUtils.cs index e6b6072..0b59e74 100644 --- a/src/Orc.Monitoring/CsvUtils.cs +++ b/src/Orc.Monitoring/CsvUtils.cs @@ -16,52 +16,24 @@ public class CsvUtils public CsvUtils(IFileSystem fileSystem, IMonitoringLoggerFactory loggerFactory) { - this._fileSystem = fileSystem; - this._logger = loggerFactory.CreateLogger(); + _fileSystem = fileSystem; + _logger = loggerFactory.CreateLogger(); } public static CsvUtils Instance { get; } = new(FileSystem.Instance, MonitoringLoggerFactory.Instance); - public void WriteCsvLine(TextWriter writer, string?[] values) - { - var line = string.Join(",", values.Select(EscapeCsvValue)); - _logger.LogDebug("Writing CSV line: {Line}", line); - writer.WriteLine(line); - } - - public async Task WriteCsvLineAsync(TextWriter writer, string?[] values) - { - var line = string.Join(",", values.Select(EscapeCsvValue)); - _logger.LogDebug("Writing CSV line asynchronously: {Line}", line); - await writer.WriteLineAsync(line); - } - - public void WriteCsv(string filePath, IEnumerable data, string[] headers) - { - _logger.LogInformation("Writing CSV to file: {FilePath}", filePath); - using var writer = _fileSystem.CreateStreamWriter(filePath, false, Encoding.UTF8); - - WriteCsvLine(writer, headers.Cast().ToArray()); - - foreach (var item in data) - { - var values = headers.Select(h => GetPropertyValue(item, h)?.ToString()).ToArray(); - WriteCsvLine(writer, values); - } - } - - public async Task WriteCsvAsync(string filePath, IEnumerable data, string[] headers) + public async Task WriteCsvAsync(string filePath, IEnumerable> data, string[] headers) { _logger.LogInformation("Writing CSV asynchronously to file: {FilePath}", filePath); - await using var writer = _fileSystem.CreateStreamWriter(filePath, false, Encoding.UTF8); - - await WriteCsvLineAsync(writer, headers.Cast().ToArray()); - + var buffer = new StringBuilder(); + buffer.AppendJoin(',', headers.Select(EscapeCsvValue)); foreach (var item in data) { - var values = headers.Select(h => GetPropertyValue(item, h)?.ToString()).ToArray(); - await WriteCsvLineAsync(writer, values); + var values = headers.Select(h => item[h]).ToArray(); + buffer.AppendLine(); + buffer.AppendJoin(',', values.Select(EscapeCsvValue)); } + await _fileSystem.WriteAllTextAsync(filePath, buffer.ToString()); } public List> ReadCsv(string filePath) @@ -206,19 +178,5 @@ public string[] ParseCsvLine(string line) result.Add(currentValue.ToString()); return result.ToArray(); } - - private object? GetPropertyValue(object? obj, string propertyName) - { - if (obj is null) - { - return null; - } - - if (obj is IDictionary dict) - { - return dict.TryGetValue(propertyName, out var value) ? value : null; - } - return obj.GetType().GetProperty(propertyName)?.GetValue(obj, null); - } } diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportOutput.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportOutput.cs index 17445f5..630592b 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportOutput.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportOutput.cs @@ -13,10 +13,12 @@ public sealed class CsvReportOutput : IReportOutput { private readonly ILogger _logger; + private readonly IMonitoringLoggerFactory _loggerFactory; private readonly ReportOutputHelper _helper; private readonly Func _methodOverrideManagerFactory; private readonly IFileSystem _fileSystem; private readonly ReportArchiver _reportArchiver; + private readonly CsvUtils _csvUtils; private string? _fileName; private string? _folderPath; @@ -26,25 +28,28 @@ public sealed class CsvReportOutput : IReportOutput public CsvReportOutput() : this(MonitoringLoggerFactory.Instance, new ReportOutputHelper(MonitoringLoggerFactory.Instance, new ReportItemFactory(MonitoringLoggerFactory.Instance)), (outputFolder) => new MethodOverrideManager(outputFolder, MonitoringLoggerFactory.Instance, FileSystem.Instance, CsvUtils.Instance), - FileSystem.Instance, new ReportArchiver(FileSystem.Instance, MonitoringLoggerFactory.Instance)) + FileSystem.Instance, new ReportArchiver(FileSystem.Instance, MonitoringLoggerFactory.Instance), CsvUtils.Instance) { } public CsvReportOutput(IMonitoringLoggerFactory loggerFactory, ReportOutputHelper reportOutputHelper, Func methodOverrideManagerFactory, - IFileSystem fileSystem, ReportArchiver reportArchiver) + IFileSystem fileSystem, ReportArchiver reportArchiver, CsvUtils csvUtils) { ArgumentNullException.ThrowIfNull(loggerFactory); ArgumentNullException.ThrowIfNull(reportOutputHelper); ArgumentNullException.ThrowIfNull(methodOverrideManagerFactory); ArgumentNullException.ThrowIfNull(fileSystem); ArgumentNullException.ThrowIfNull(reportArchiver); + ArgumentNullException.ThrowIfNull(csvUtils); _logger = loggerFactory.CreateLogger(); + _loggerFactory = loggerFactory; _helper = reportOutputHelper; _methodOverrideManagerFactory = methodOverrideManagerFactory; _fileSystem = fileSystem; _reportArchiver = reportArchiver; + _csvUtils = csvUtils; _logger.LogDebug($"Created {nameof(CsvReportOutput)}"); } @@ -173,11 +178,8 @@ private async Task ExportToCsvAsync() _logger.LogDebug($"Exporting item {i}: {item.ItemName ?? item.MethodName}, MethodName: {item.MethodName}, StartTime: {item.StartTime}"); } - await using (var writer = _fileSystem.CreateStreamWriter(fullPath, false, System.Text.Encoding.UTF8)) - { - var csvReportWriter = new CsvReportWriter(writer, sortedItems, _methodOverrideManager); - await csvReportWriter.WriteReportItemsCsvAsync(); - } + var csvReportWriter = new CsvReportWriter(fullPath, sortedItems, _methodOverrideManager, _loggerFactory, _csvUtils); + await csvReportWriter.WriteReportItemsCsvAsync(); _logger.LogInformation($"CSV report written to {fullPath} with {sortedItems.Count} items"); diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportWriter.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportWriter.cs index 531fbe4..666442c 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportWriter.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/CsvReportWriter.cs @@ -9,108 +9,55 @@ public class CsvReportWriter { - private readonly TextWriter _writer; + private readonly string _fileName; private readonly IEnumerable _reportItems; private readonly MethodOverrideManager _overrideManager; private readonly ILogger _logger; private readonly CsvUtils _csvUtils; - public CsvReportWriter( - TextWriter writer, - IEnumerable reportItems, - MethodOverrideManager methodOverrideManager) - : this( - writer, - reportItems, - methodOverrideManager, - MonitoringLoggerFactory.Instance, - CsvUtils.Instance) - { - } + //public CsvReportWriter( + // string fileName, + // IEnumerable reportItems, + // MethodOverrideManager methodOverrideManager) + // : this( + // fileName, + // reportItems, + // methodOverrideManager, + // MonitoringLoggerFactory.Instance, + // CsvUtils.Instance) + //{ + //} public CsvReportWriter( - TextWriter writer, + string fileName, IEnumerable reportItems, MethodOverrideManager overrideManager, IMonitoringLoggerFactory loggerFactory, CsvUtils csvUtils) { - ArgumentNullException.ThrowIfNull(writer); ArgumentNullException.ThrowIfNull(reportItems); ArgumentNullException.ThrowIfNull(overrideManager); ArgumentNullException.ThrowIfNull(loggerFactory); ArgumentNullException.ThrowIfNull(csvUtils); - _writer = writer; + _fileName = fileName; _reportItems = reportItems; _overrideManager = overrideManager; _logger = loggerFactory.CreateLogger(); _csvUtils = csvUtils; } - public void WriteReportItemsCsv() - { - var headers = GetReportItemHeaders(); - _csvUtils.WriteCsvLine(_writer, headers); - - Func, string[]> selector = item => - headers.Select(h => item.GetValueOrDefault(h, string.Empty)).ToArray(); - - var items = PrepareReportItems().ToList(); - int itemCount = items.Count; - for (int i = 0; i < itemCount; i++) - { - var item = items[i]; - var values = selector(item); - - if (i < itemCount - 1) - { - // Write lines with newline character - _csvUtils.WriteCsvLine(_writer, values); - } - else - { - // Write last line without newline character - string line = string.Join(",", values); - _writer.Write(line); - } - } - - _logger.LogInformation($"Wrote {itemCount} report items to CSV"); - } - - public async Task WriteReportItemsCsvAsync() { try { var headers = GetReportItemHeaders(); - await _csvUtils.WriteCsvLineAsync(_writer, headers); - Func, string[]> selector = item => - headers.Select(h => item.GetValueOrDefault(h, string.Empty)).ToArray(); + var items = PrepareReportItems(headers).ToList(); - var items = PrepareReportItems().ToList(); - int itemCount = items.Count; - for (int i = 0; i < itemCount; i++) - { - var item = items[i]; - var values = selector(item); + await _csvUtils.WriteCsvAsync(_fileName, items, headers); - if (i < itemCount - 1) - { - // Write lines with newline character - await _csvUtils.WriteCsvLineAsync(_writer, values); - } - else - { - // Write last line without newline character - string line = string.Join(",", values); - await _writer.WriteAsync(line); - } - } - - _logger.LogInformation($"Wrote {itemCount} report items to CSV asynchronously"); + _logger.LogDebug($"Wrote {items.Count} report items to CSV asynchronously"); } catch (Exception ex) { @@ -123,26 +70,20 @@ public async Task WriteRelationshipsCsvAsync() { try { - var headers = new[] { "From", "To", "RelationType" }; - await _csvUtils.WriteCsvLineAsync(_writer, headers); - + var headers = GetRelationshipHeaders(); + var relationships = _reportItems .Where(r => !string.IsNullOrEmpty(r.Parent)) - .Select(item => new + .Select(item => new Dictionary { - From = item.Parent ?? string.Empty, - To = item.Id ?? string.Empty, - RelationType = DetermineRelationType(item) - }); + {"From", item.Parent ?? string.Empty }, + {"To", item.Id ?? string.Empty }, + {"RelationType", DetermineRelationType(item) } + }).ToArray(); - var counter = 0; - foreach (var relationship in relationships) - { - await _csvUtils.WriteCsvLineAsync(_writer, new[] { relationship.From, relationship.To, relationship.RelationType }); - counter++; - } + await _csvUtils.WriteCsvAsync(_fileName, relationships, headers); - _logger.LogInformation($"Wrote {counter} relationships to CSV asynchronously"); + _logger.LogInformation($"Wrote {relationships.Length} relationships to CSV asynchronously"); } catch (Exception ex) { @@ -151,31 +92,99 @@ public async Task WriteRelationshipsCsvAsync() } } - private IEnumerable> PrepareReportItems() + private IEnumerable> PrepareReportItems(string[] headers) { - return _reportItems.Select(PrepareReportItem); + return _reportItems.Select(item => PrepareReportItem(item, headers)); } - private Dictionary PrepareReportItem(ReportItem item) + private Dictionary PrepareReportItem(ReportItem item, string[] headers) { - var result = new Dictionary + var result = new Dictionary(); + + var attributeParameters = item.AttributeParameters; + var dynamicParameters = item.Parameters; + + foreach (var header in headers) { - ["Id"] = item.Id ?? string.Empty, - ["ParentId"] = item.Parent ?? string.Empty, - ["StartTime"] = item.StartTime ?? string.Empty, - ["EndTime"] = item.EndTime ?? string.Empty, - ["Report"] = item.Report ?? string.Empty, - ["ClassName"] = item.ClassName ?? string.Empty, - ["MethodName"] = item.MethodName ?? string.Empty, - ["FullName"] = item.FullName ?? string.Empty, - ["Duration"] = item.Duration ?? string.Empty, - ["ThreadId"] = item.ThreadId ?? string.Empty, - ["ParentThreadId"] = item.ParentThreadId ?? string.Empty, - ["NestingLevel"] = item.Level ?? string.Empty, - ["IsStatic"] = GetBooleanPropertyOrDefault(item, "IsStatic", false).ToString(), - ["IsGeneric"] = GetBooleanPropertyOrDefault(item, "IsGeneric", false).ToString(), - ["IsExtension"] = GetBooleanPropertyOrDefault(item, "IsExtension", false).ToString() - }; + switch (header) + { + case "Id": + result["Id"] = item.Id ?? string.Empty; + break; + + case "ParentId": + result["ParentId"] = item.Parent ?? string.Empty; + break; + + case "StartTime": + result["StartTime"] = item.StartTime ?? string.Empty; + break; + + case "EndTime": + result["EndTime"] = item.EndTime ?? string.Empty; + break; + + case "Report": + result["Report"] = item.Report ?? string.Empty; + break; + + case "ClassName": + result["ClassName"] = item.ClassName ?? string.Empty; + break; + + case "MethodName": + result["MethodName"] = item.MethodName ?? string.Empty; + break; + + case "FullName": + result["FullName"] = item.FullName ?? string.Empty; + break; + + case "Duration": + result["Duration"] = item.Duration ?? string.Empty; + break; + + case "ThreadId": + result["ThreadId"] = item.ThreadId ?? string.Empty; + break; + + case "ParentThreadId": + result["ParentThreadId"] = item.ParentThreadId ?? string.Empty; + break; + + case "NestingLevel": + result["NestingLevel"] = item.Level ?? string.Empty; + break; + + case "IsStatic": + result["IsStatic"] = GetBooleanPropertyOrDefault(item, "IsStatic", false).ToString(); + break; + + case "IsGeneric": + result["IsGeneric"] = GetBooleanPropertyOrDefault(item, "IsGeneric", false).ToString(); + break; + + case "IsExtension": + result["IsExtension"] = GetBooleanPropertyOrDefault(item, "IsExtension", false).ToString(); + break; + + + default: + if (attributeParameters.Contains(header)) + { + result[header] = item.Parameters.TryGetValue(header, out var value) ? value : string.Empty; + } + else if (dynamicParameters.ContainsKey(header)) + { + result[header] = item.Parameters.TryGetValue(header, out var value) ? value : string.Empty; + } + else + { + result[header] = string.Empty; + } + break; + } + } var fullName = item.FullName ?? string.Empty; var overrides = _overrideManager.GetOverridesForMethod(fullName, item.IsStaticParameter); @@ -236,6 +245,11 @@ private string[] GetReportItemHeaders() return headers; } + private string[] GetRelationshipHeaders() + { + return new[] { "From", "To", "RelationType" }; + } + private string DetermineRelationType(ReportItem item) { if (GetBooleanPropertyOrDefault(item, "IsStatic", false)) diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/MethodOverrideManager.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/MethodOverrideManager.cs index 0fcbb38..174de20 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/MethodOverrideManager.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/MethodOverrideManager.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using IO; using Microsoft.Extensions.Logging; @@ -79,7 +80,7 @@ public void ReadOverrides() } } - public void SaveOverrides(ICollection reportItems) + public async Task SaveOverridesAsync(ICollection reportItems) { var headers = new HashSet(); @@ -94,8 +95,7 @@ public void SaveOverrides(ICollection reportItems) var sortedHeader = headers.OrderBy(h => h).ToList(); sortedHeader.Insert(0, "FullName"); - using var writer = _fileSystem.CreateStreamWriter(_overrideTemplateFilePath, false, System.Text.Encoding.UTF8); - _csvUtils.WriteCsvLine(writer, sortedHeader.ToArray()); + var data = new List>(); var savedFullNames = new HashSet(); @@ -107,19 +107,22 @@ public void SaveOverrides(ICollection reportItems) continue; } - var values = new string[sortedHeader.Count]; - values[0] = fullName; + var row = new Dictionary(); + + row["FullName"] = fullName; for (var i = 1; i < sortedHeader.Count; i++) { var header = sortedHeader[i]; var value = item.Parameters.TryGetValue(header, out var parameterValue) ? parameterValue : string.Empty; - values[i] = value; + row[header] = value; } - _csvUtils.WriteCsvLine(writer, values); + data.Add(row); } + await _csvUtils.WriteCsvAsync(_overrideTemplateFilePath, data, sortedHeader.ToArray()); + _logger.LogInformation($"Saved method override template to {_overrideTemplateFilePath}"); } diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/RanttOutput.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/RanttOutput.cs index 8897f27..cee535d 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/RanttOutput.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/RanttOutput.cs @@ -50,30 +50,36 @@ public sealed class RanttOutput : IReportOutput private string? _outputDirectory; private MethodOverrideManager? _overrideManager; private OutputLimitOptions _limitOptions = OutputLimitOptions.Unlimited; + private readonly IMonitoringLoggerFactory _loggerFactory; private readonly Func _enhancedDataPostProcessorFactory; private readonly Func _methodOverrideManagerFactory; private readonly IFileSystem _fileSystem; private readonly ReportArchiver _reportArchiver; private readonly IReportItemFactory _reportItemFactory; + private readonly CsvUtils _csvUtils; public RanttOutput() : this(MonitoringLoggerFactory.Instance, () => new EnhancedDataPostProcessor(MonitoringLoggerFactory.Instance), new ReportOutputHelper(MonitoringLoggerFactory.Instance, new ReportItemFactory(MonitoringLoggerFactory.Instance)), (outputFolder) => new MethodOverrideManager(outputFolder, MonitoringLoggerFactory.Instance, FileSystem.Instance, CsvUtils.Instance), - FileSystem.Instance, new ReportArchiver(FileSystem.Instance, MonitoringLoggerFactory.Instance), new ReportItemFactory(MonitoringLoggerFactory.Instance)) + FileSystem.Instance, new ReportArchiver(FileSystem.Instance, MonitoringLoggerFactory.Instance), new ReportItemFactory(MonitoringLoggerFactory.Instance), + CsvUtils.Instance) { } - public RanttOutput(IMonitoringLoggerFactory monitoringLoggerFactory, Func enhancedDataPostProcessorFactory, ReportOutputHelper reportOutputHelper, - Func methodOverrideManagerFactory, IFileSystem fileSystem, ReportArchiver reportArchiver, IReportItemFactory reportItemFactory) + public RanttOutput(IMonitoringLoggerFactory loggerFactory, Func enhancedDataPostProcessorFactory, ReportOutputHelper reportOutputHelper, + Func methodOverrideManagerFactory, IFileSystem fileSystem, ReportArchiver reportArchiver, IReportItemFactory reportItemFactory, + CsvUtils csvUtils) { - _logger = monitoringLoggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); + _loggerFactory = loggerFactory; _enhancedDataPostProcessorFactory = enhancedDataPostProcessorFactory; _helper = reportOutputHelper; _methodOverrideManagerFactory = methodOverrideManagerFactory; _fileSystem = fileSystem; _reportArchiver = reportArchiver; _reportItemFactory = reportItemFactory; + _csvUtils = csvUtils; } public static RanttReportParameters CreateParameters( @@ -183,7 +189,7 @@ private async Task ExportDataAsync(IMethodCallReporter reporter) _logger.LogInformation($"Output limited to {_limitOptions.MaxItems.Value} items"); } - _overrideManager.SaveOverrides(_helper.ReportItems.ToList()); + await _overrideManager.SaveOverridesAsync(_helper.ReportItems.ToList()); await _reportArchiver.CreateTimestampedFolderCopyAsync(_outputDirectory); } @@ -242,11 +248,8 @@ private async Task ExportToCsvAsync(string fileName, IMethodCallReporter reporte _logger.LogInformation($"Number of items with overrides: {itemsWithOverrides.Count}"); - await using (var writer = _fileSystem.CreateStreamWriter(fullPath, false, Encoding.UTF8)) - { - var csvReportWriter = new CsvReportWriter(writer, itemsWithOverrides, _overrideManager); - await csvReportWriter.WriteReportItemsCsvAsync(); - } + var csvReportWriter = new CsvReportWriter(fullPath, itemsWithOverrides, _overrideManager, _loggerFactory, _csvUtils); + await csvReportWriter.WriteReportItemsCsvAsync(); _logger.LogInformation($"CSV report written to {fullPath} with {itemsWithOverrides.Count} items"); diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/ReportItemFactory.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/ReportItemFactory.cs index a61c37b..dc774c0 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/ReportItemFactory.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/ReportItemFactory.cs @@ -1,4 +1,4 @@ -namespace Orc.Monitoring.Reporters.ReportOutputs; +namespace Orc.Monitoring.Reporters.ReportOutputs; using System; using System.Collections.Generic; @@ -112,7 +112,7 @@ public ReportItem UpdateReportItemEnding(MethodCallEnd end, IMethodCallReporter? reportItem = CreateReportItem(end, reporter); id = reportItem.Id; - existingReportItems.Add(id, reportItem); + existingReportItems[id] = reportItem; } var endTime = end.TimeStamp; diff --git a/src/Orc.Monitoring/Reporters/ReportOutputs/TxtReportOutput.cs b/src/Orc.Monitoring/Reporters/ReportOutputs/TxtReportOutput.cs index aa83553..aea5183 100644 --- a/src/Orc.Monitoring/Reporters/ReportOutputs/TxtReportOutput.cs +++ b/src/Orc.Monitoring/Reporters/ReportOutputs/TxtReportOutput.cs @@ -239,36 +239,24 @@ private async Task WriteLogEntriesToFileAsync() var limitedEntries = ApplyLimits(_logEntries.ToList()); _logger.LogInformation($"Writing {limitedEntries.Count} entries to file:"); - await using (var writer = _fileSystem.CreateStreamWriter(_fileName, false, Encoding.UTF8)) + var buffer = new StringBuilder(); + + for (var i = 0; i < limitedEntries.Count; i++) { - for (int i = 0; i < limitedEntries.Count; i++) + var entry = limitedEntries[i]; + buffer.Append($"{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{entry.Category}] {entry.Message}"); + + if (i != limitedEntries.Count - 1) { - var entry = limitedEntries[i]; - var line = $"{entry.Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{entry.Category}] {entry.Message}"; - - if (i == limitedEntries.Count - 1) - { - // For the last entry, write without a newline - await writer.WriteAsync(line); - } - else - { - await writer.WriteLineAsync(line); - } - _logger.LogInformation($"Writing entry: {line}"); + buffer.AppendLine(); } } + await _fileSystem.WriteAllTextAsync(_fileName, buffer.ToString()); + _logger.LogInformation($"TXT report written to {_fileName} with {limitedEntries.Count} entries"); - if (_fileSystem.FileExists(_fileName)) - { - var fileContent = await _fileSystem.ReadAllTextAsync(_fileName); - _logger.LogInformation($"File content:\n{fileContent}"); - var lineCount = fileContent.Split('\n', StringSplitOptions.RemoveEmptyEntries).Length; - _logger.LogInformation($"Actual line count in file: {lineCount}"); - } - else + if (!_fileSystem.FileExists(_fileName)) { _logger.LogWarning($"File not found after writing: {_fileName}"); } diff --git a/src/Orc.Monitoring/Utilities/ReflectionHelper.cs b/src/Orc.Monitoring/Utilities/ReflectionHelper.cs index c5c7be0..2df3775 100644 --- a/src/Orc.Monitoring/Utilities/ReflectionHelper.cs +++ b/src/Orc.Monitoring/Utilities/ReflectionHelper.cs @@ -101,10 +101,29 @@ private static bool ParametersMatch(ParameterInfo[] methodParams, IReadOnlyColle for (var i = 0; i < methodParams.Length; i++) { - if (!methodParams[i].ParameterType.IsAssignableFrom(configParams.ElementAt(i))) + var methodParameterType = methodParams[i].ParameterType; + var parameterType = configParams.ElementAt(i); + if (methodParameterType.IsAssignableFrom(parameterType)) + { + continue; + } + + if (!methodParameterType.IsGenericParameter) + { + return false; + } + + if(methodParameterType.BaseType is null) { return false; } + + if (methodParameterType.BaseType.IsAssignableFrom(parameterType)) + { + continue; + } + + return false; } return true;