2020-12-20 08:36:11 +01:00
|
|
|
// Copyright (c) Umbraco.
|
|
|
|
|
// See LICENSE for more details.
|
|
|
|
|
|
2018-11-16 13:44:29 +00:00
|
|
|
using System;
|
2020-12-20 08:36:11 +01:00
|
|
|
using System.Collections.Generic;
|
2020-10-28 09:41:23 +01:00
|
|
|
using System.Diagnostics;
|
2018-11-16 13:44:29 +00:00
|
|
|
using System.IO;
|
2018-11-16 14:26:56 +00:00
|
|
|
using System.Linq;
|
2020-09-21 15:26:10 +02:00
|
|
|
using Microsoft.Extensions.Logging;
|
2020-12-20 08:36:11 +01:00
|
|
|
using Moq;
|
|
|
|
|
using NUnit.Framework;
|
|
|
|
|
using Serilog;
|
2021-02-09 10:22:42 +01:00
|
|
|
using Umbraco.Cms.Core;
|
|
|
|
|
using Umbraco.Cms.Core.Hosting;
|
|
|
|
|
using Umbraco.Cms.Core.IO;
|
|
|
|
|
using Umbraco.Cms.Core.Logging;
|
|
|
|
|
using Umbraco.Cms.Core.Models;
|
2021-02-10 14:58:22 +01:00
|
|
|
using Umbraco.Cms.Tests.UnitTests.TestHelpers;
|
2018-11-16 13:44:29 +00:00
|
|
|
using Umbraco.Core.Logging.Viewer;
|
2020-12-20 08:36:11 +01:00
|
|
|
using File = System.IO.File;
|
2018-11-16 13:44:29 +00:00
|
|
|
|
2021-02-10 14:58:22 +01:00
|
|
|
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Logging
|
2018-11-16 13:44:29 +00:00
|
|
|
{
|
|
|
|
|
[TestFixture]
|
|
|
|
|
public class LogviewerTests
|
|
|
|
|
{
|
|
|
|
|
private ILogViewer _logViewer;
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
private const string LogfileName = "UmbracoTraceLog.UNITTEST.20181112.json";
|
|
|
|
|
private const string SearchfileName = "logviewer.searches.config.js";
|
2018-11-16 13:44:29 +00:00
|
|
|
|
|
|
|
|
private string _newLogfilePath;
|
|
|
|
|
private string _newLogfileDirPath;
|
|
|
|
|
|
|
|
|
|
private string _newSearchfilePath;
|
|
|
|
|
private string _newSearchfileDirPath;
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
private readonly LogTimePeriod _logTimePeriod = new LogTimePeriod(
|
|
|
|
|
new DateTime(year: 2018, month: 11, day: 12, hour: 0, minute: 0, second: 0),
|
|
|
|
|
new DateTime(year: 2018, month: 11, day: 13, hour: 0, minute: 0, second: 0));
|
|
|
|
|
|
2018-11-16 13:44:29 +00:00
|
|
|
[OneTimeSetUp]
|
|
|
|
|
public void Setup()
|
|
|
|
|
{
|
2020-10-28 09:41:23 +01:00
|
|
|
var testRoot = TestContext.CurrentContext.TestDirectory.Split("bin")[0];
|
2018-11-16 13:44:29 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Create an example JSON log file to check results
|
|
|
|
|
// As a one time setup for all tets in this class/fixture
|
|
|
|
|
IIOHelper ioHelper = TestHelper.IOHelper;
|
|
|
|
|
IHostingEnvironment hostingEnv = TestHelper.GetHostingEnvironment();
|
|
|
|
|
|
|
|
|
|
ILoggingConfiguration loggingConfiguration = TestHelper.GetLoggingConfiguration(hostingEnv);
|
2020-04-22 14:23:56 +10:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
var exampleLogfilePath = Path.Combine(testRoot, "TestHelpers", "Assets", LogfileName);
|
2020-04-22 14:23:56 +10:00
|
|
|
_newLogfileDirPath = loggingConfiguration.LogDirectory;
|
2020-12-20 08:36:11 +01:00
|
|
|
_newLogfilePath = Path.Combine(_newLogfileDirPath, LogfileName);
|
2018-11-16 13:44:29 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
var exampleSearchfilePath = Path.Combine(testRoot, "TestHelpers", "Assets", SearchfileName);
|
2020-11-25 07:31:39 +01:00
|
|
|
_newSearchfileDirPath = Path.Combine(hostingEnv.ApplicationPhysicalPath, @"config");
|
2020-12-20 08:36:11 +01:00
|
|
|
_newSearchfilePath = Path.Combine(_newSearchfileDirPath, SearchfileName);
|
2018-11-16 13:44:29 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Create/ensure Directory exists
|
2019-11-13 21:00:54 +01:00
|
|
|
ioHelper.EnsurePathExists(_newLogfileDirPath);
|
|
|
|
|
ioHelper.EnsurePathExists(_newSearchfileDirPath);
|
2018-11-16 13:44:29 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Copy the sample files
|
2018-11-16 13:44:29 +00:00
|
|
|
File.Copy(exampleLogfilePath, _newLogfilePath, true);
|
|
|
|
|
File.Copy(exampleSearchfilePath, _newSearchfilePath, true);
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
ILogger<SerilogJsonLogViewer> logger = Mock.Of<ILogger<SerilogJsonLogViewer>>();
|
2020-04-21 13:07:20 +10:00
|
|
|
var logViewerConfig = new LogViewerConfig(hostingEnv);
|
2020-04-22 14:23:56 +10:00
|
|
|
_logViewer = new SerilogJsonLogViewer(logger, logViewerConfig, loggingConfiguration, Log.Logger);
|
2018-11-16 13:44:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[OneTimeTearDown]
|
|
|
|
|
public void TearDown()
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
// Cleanup & delete the example log & search files off disk
|
|
|
|
|
// Once all tests in this class/fixture have run
|
2018-11-16 13:44:29 +00:00
|
|
|
if (File.Exists(_newLogfilePath))
|
2020-12-20 08:36:11 +01:00
|
|
|
{
|
2018-11-16 13:44:29 +00:00
|
|
|
File.Delete(_newLogfilePath);
|
2020-12-20 08:36:11 +01:00
|
|
|
}
|
2018-11-16 13:44:29 +00:00
|
|
|
|
|
|
|
|
if (File.Exists(_newSearchfilePath))
|
2020-12-20 08:36:11 +01:00
|
|
|
{
|
2018-11-16 13:44:29 +00:00
|
|
|
File.Delete(_newSearchfilePath);
|
2020-12-20 08:36:11 +01:00
|
|
|
}
|
2018-11-16 13:44:29 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-16 13:45:34 +00:00
|
|
|
[Test]
|
|
|
|
|
public void Logs_Contain_Correct_Error_Count()
|
|
|
|
|
{
|
2019-05-03 14:56:44 +02:00
|
|
|
var numberOfErrors = _logViewer.GetNumberOfErrors(_logTimePeriod);
|
2018-11-16 13:45:34 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Our dummy log should contain 2 errors
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(1, numberOfErrors);
|
2018-11-16 13:45:34 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-16 14:26:56 +00:00
|
|
|
[Test]
|
|
|
|
|
public void Logs_Contain_Correct_Log_Level_Counts()
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
LogLevelCounts logCounts = _logViewer.GetLogLevelCounts(_logTimePeriod);
|
2019-05-03 14:56:44 +02:00
|
|
|
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(55, logCounts.Debug);
|
|
|
|
|
Assert.AreEqual(1, logCounts.Error);
|
2018-11-16 14:26:56 +00:00
|
|
|
Assert.AreEqual(0, logCounts.Fatal);
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(38, logCounts.Information);
|
|
|
|
|
Assert.AreEqual(6, logCounts.Warning);
|
2018-11-16 14:26:56 +00:00
|
|
|
}
|
|
|
|
|
|
2018-11-16 15:16:18 +00:00
|
|
|
[Test]
|
|
|
|
|
public void Logs_Contains_Correct_Message_Templates()
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
IEnumerable<LogTemplate> templates = _logViewer.GetMessageTemplates(_logTimePeriod);
|
2018-11-16 15:16:18 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Count no of templates
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(25, templates.Count());
|
2018-11-16 15:16:18 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Verify all templates & counts are unique
|
2018-11-16 15:16:18 +00:00
|
|
|
CollectionAssert.AllItemsAreUnique(templates);
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Ensure the collection contains LogTemplate objects
|
2018-11-16 15:16:18 +00:00
|
|
|
CollectionAssert.AllItemsAreInstancesOfType(templates, typeof(LogTemplate));
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Get first item & verify its template & count are what we expect
|
|
|
|
|
LogTemplate popularTemplate = templates.FirstOrDefault();
|
2018-11-16 15:16:18 +00:00
|
|
|
|
|
|
|
|
Assert.IsNotNull(popularTemplate);
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual("{EndMessage} ({Duration}ms) [Timing {TimingId}]", popularTemplate.MessageTemplate);
|
|
|
|
|
Assert.AreEqual(26, popularTemplate.Count);
|
2018-11-16 15:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void Logs_Can_Open_As_Small_File()
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
// We are just testing a return value (as we know the example file is less than 200MB)
|
|
|
|
|
// But this test method does not test/check that
|
2019-05-03 14:56:44 +02:00
|
|
|
var canOpenLogs = _logViewer.CheckCanOpenLogs(_logTimePeriod);
|
2018-11-16 15:16:18 +00:00
|
|
|
Assert.IsTrue(canOpenLogs);
|
|
|
|
|
}
|
2018-11-19 14:23:07 +00:00
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void Logs_Can_Be_Queried()
|
|
|
|
|
{
|
2020-10-28 09:41:23 +01:00
|
|
|
var sw = new Stopwatch();
|
|
|
|
|
sw.Start();
|
2020-12-20 08:36:11 +01:00
|
|
|
|
|
|
|
|
// Should get me the most 100 recent log entries & using default overloads for remaining params
|
|
|
|
|
PagedResult<LogMessage> allLogs = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1);
|
2018-11-19 14:23:07 +00:00
|
|
|
|
2020-10-28 09:41:23 +01:00
|
|
|
sw.Stop();
|
2020-12-20 08:36:11 +01:00
|
|
|
|
|
|
|
|
// Check we get 100 results back for a page & total items all good :)
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(100, allLogs.Items.Count());
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(102, allLogs.TotalItems);
|
|
|
|
|
Assert.AreEqual(2, allLogs.TotalPages);
|
2018-11-19 14:23:07 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check collection all contain same object type
|
2018-11-19 14:23:07 +00:00
|
|
|
CollectionAssert.AllItemsAreInstancesOfType(allLogs.Items, typeof(LogMessage));
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check first item is newest
|
|
|
|
|
LogMessage newestItem = allLogs.Items.First();
|
|
|
|
|
DateTimeOffset.TryParse("2018-11-12T08:39:18.1971147Z", out DateTimeOffset newDate);
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(newDate, newestItem.Timestamp);
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check we call method again with a smaller set of results & in ascending
|
|
|
|
|
PagedResult<LogMessage> smallQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, pageSize: 10, orderDirection: Direction.Ascending);
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(10, smallQuery.Items.Count());
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(11, smallQuery.TotalPages);
|
2018-11-19 14:23:07 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check first item is oldest
|
|
|
|
|
LogMessage oldestItem = smallQuery.Items.First();
|
|
|
|
|
DateTimeOffset.TryParse("2018-11-12T08:34:45.8371142Z", out DateTimeOffset oldDate);
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(oldDate, oldestItem.Timestamp);
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check invalid log levels
|
|
|
|
|
// Rather than expect 0 items - get all items back & ignore the invalid levels
|
2018-11-19 14:23:07 +00:00
|
|
|
string[] invalidLogLevels = { "Invalid", "NotALevel" };
|
2020-12-20 08:36:11 +01:00
|
|
|
PagedResult<LogMessage> queryWithInvalidLevels = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, logLevels: invalidLogLevels);
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(102, queryWithInvalidLevels.TotalItems);
|
2018-11-19 14:23:07 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check we can call method with an array of logLevel (error & warning)
|
|
|
|
|
string[] logLevels = { "Warning", "Error" };
|
|
|
|
|
PagedResult<LogMessage> queryWithLevels = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, logLevels: logLevels);
|
2020-10-28 09:41:23 +01:00
|
|
|
Assert.AreEqual(7, queryWithLevels.TotalItems);
|
2019-05-03 14:56:44 +02:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Query @Level='Warning' BUT we pass in array of LogLevels for Debug & Info (Expect to get 0 results)
|
2018-11-19 14:23:07 +00:00
|
|
|
string[] logLevelMismatch = { "Debug", "Information" };
|
2020-12-20 08:36:11 +01:00
|
|
|
PagedResult<LogMessage> filterLevelQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: "@Level='Warning'", logLevels: logLevelMismatch);
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(0, filterLevelQuery.TotalItems);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 09:41:23 +01:00
|
|
|
[TestCase("", 102)]
|
|
|
|
|
[TestCase("Has(@Exception)", 1)]
|
|
|
|
|
[TestCase("Has(Duration) and Duration > 1000", 2)]
|
|
|
|
|
[TestCase("Not(@Level = 'Verbose') and Not(@Level= 'Debug')", 45)]
|
|
|
|
|
[TestCase("StartsWith(SourceContext, 'Umbraco.Core')", 86)]
|
|
|
|
|
[TestCase("@MessageTemplate = '{EndMessage} ({Duration}ms) [Timing {TimingId}]'", 26)]
|
2018-11-19 14:23:07 +00:00
|
|
|
[TestCase("SortedComponentTypes[?] = 'Umbraco.Web.Search.ExamineComponent'", 1)]
|
|
|
|
|
[TestCase("Contains(SortedComponentTypes[?], 'DatabaseServer')", 1)]
|
|
|
|
|
[Test]
|
|
|
|
|
public void Logs_Can_Query_With_Expressions(string queryToVerify, int expectedCount)
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
PagedResult<LogMessage> testQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: queryToVerify);
|
2018-11-19 14:23:07 +00:00
|
|
|
Assert.AreEqual(expectedCount, testQuery.TotalItems);
|
|
|
|
|
}
|
2019-05-03 14:56:44 +02:00
|
|
|
|
2018-11-16 14:26:56 +00:00
|
|
|
[Test]
|
|
|
|
|
public void Log_Search_Can_Persist()
|
|
|
|
|
{
|
2020-12-20 08:36:11 +01:00
|
|
|
// Add a new search
|
2018-11-16 14:26:56 +00:00
|
|
|
_logViewer.AddSavedSearch("Unit Test Example", "Has(UnitTest)");
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
IReadOnlyList<SavedLogSearch> searches = _logViewer.GetSavedSearches();
|
2018-11-16 14:26:56 +00:00
|
|
|
|
|
|
|
|
var savedSearch = new SavedLogSearch
|
|
|
|
|
{
|
|
|
|
|
Name = "Unit Test Example",
|
|
|
|
|
Query = "Has(UnitTest)"
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Check if we can find the newly added item from the results we get back
|
|
|
|
|
IEnumerable<SavedLogSearch> findItem = searches.Where(x => x.Name == "Unit Test Example" && x.Query == "Has(UnitTest)");
|
2018-11-16 14:26:56 +00:00
|
|
|
|
|
|
|
|
Assert.IsNotNull(findItem, "We should have found the saved search, but get no results");
|
|
|
|
|
Assert.AreEqual(1, findItem.Count(), "Our list of searches should only contain one result");
|
|
|
|
|
|
2019-01-27 01:17:32 -05:00
|
|
|
// TODO: Need someone to help me find out why these don't work
|
2020-12-20 08:36:11 +01:00
|
|
|
// CollectionAssert.Contains(searches, savedSearch, "Can not find the new search that was saved");
|
|
|
|
|
// Assert.That(searches, Contains.Item(savedSearch));
|
2018-11-16 14:26:56 +00:00
|
|
|
|
2020-12-20 08:36:11 +01:00
|
|
|
// Remove the search from above & ensure it no longer exists
|
2018-11-16 14:26:56 +00:00
|
|
|
_logViewer.DeleteSavedSearch("Unit Test Example", "Has(UnitTest)");
|
|
|
|
|
|
|
|
|
|
searches = _logViewer.GetSavedSearches();
|
|
|
|
|
findItem = searches.Where(x => x.Name == "Unit Test Example" && x.Query == "Has(UnitTest)");
|
|
|
|
|
Assert.IsEmpty(findItem, "The search item should no longer exist");
|
|
|
|
|
}
|
2018-11-16 13:44:29 +00:00
|
|
|
}
|
|
|
|
|
}
|