diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs index 6385e3431d..0ec66bbaaa 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.CoreServices.cs @@ -268,10 +268,12 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection public static IUmbracoBuilder AddLogViewer(this IUmbracoBuilder builder) { builder.Services.AddSingleton(); + builder.Services.AddSingleton(); builder.SetLogViewer(); builder.Services.AddSingleton(factory => new SerilogJsonLogViewer(factory.GetRequiredService>(), factory.GetRequiredService(), factory.GetRequiredService(), + factory.GetRequiredService(), Log.Logger)); return builder; diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs index 2fcb78f7a6..85ebcc3b68 100644 --- a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs +++ b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs @@ -31,7 +31,8 @@ namespace Umbraco.Extensions this LoggerConfiguration logConfig, IHostingEnvironment hostingEnvironment, ILoggingConfiguration loggingConfiguration, - IConfiguration configuration) + IConfiguration configuration, + out UmbracoFileConfiguration umbFileConfiguration) { global::Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg)); @@ -54,6 +55,8 @@ namespace Umbraco.Extensions //This is not optimal, but seems to be the only way if we do not make an Serilog.Sink.UmbracoFile sink all the way. var umbracoFileConfiguration = new UmbracoFileConfiguration(configuration); + umbFileConfiguration = umbracoFileConfiguration; + logConfig.WriteTo.UmbracoFile( path : umbracoFileConfiguration.GetPath(loggingConfiguration.LogDirectory), fileSizeLimitBytes: umbracoFileConfiguration.FileSizeLimitBytes, diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs index 054070240e..6c4df7e9fc 100644 --- a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs +++ b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.Configuration; using Serilog; using Serilog.Events; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Infrastructure.Logging.Serilog; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Logging.Serilog @@ -27,13 +28,14 @@ namespace Umbraco.Cms.Core.Logging.Serilog public static SerilogLogger CreateWithDefaultConfiguration( IHostingEnvironment hostingEnvironment, ILoggingConfiguration loggingConfiguration, - IConfiguration configuration) + IConfiguration configuration, + out UmbracoFileConfiguration umbracoFileConfig) { - var loggerConfig = new LoggerConfiguration() - .MinimalConfiguration(hostingEnvironment, loggingConfiguration, configuration) + var serilogConfig = new LoggerConfiguration() + .MinimalConfiguration(hostingEnvironment, loggingConfiguration, configuration, out umbracoFileConfig) .ReadFrom.Configuration(configuration); - return new SerilogLogger(loggerConfig); + return new SerilogLogger(serilogConfig); } /// diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogLevelLoader.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogLevelLoader.cs new file mode 100644 index 0000000000..705e283ed9 --- /dev/null +++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogLevelLoader.cs @@ -0,0 +1,18 @@ +using System.Collections.ObjectModel; +using Serilog.Events; + +namespace Umbraco.Cms.Core.Logging.Viewer +{ + public interface ILogLevelLoader + { + /// + /// Get the Serilog level values of the global minimum and the UmbracoFile one from the config file. + /// + ReadOnlyDictionary GetLogLevelsFromSinks(); + + /// + /// Get the Serilog minimum-level value from the config file. + /// + LogEventLevel GetGlobalMinLogLevel(); + } +} diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs index 61ca492ba2..c3c90ab9e9 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Serilog.Events; using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Logging.Viewer @@ -40,10 +43,16 @@ namespace Umbraco.Cms.Core.Logging.Viewer bool CheckCanOpenLogs(LogTimePeriod logTimePeriod); + /// + /// Get the Serilog minimum-level and UmbracoFile-level values from the config file. + /// + ReadOnlyDictionary GetLogLevels(); + /// /// Gets the current Serilog minimum log level /// /// + [Obsolete("Please use GetLogLevels() instead. Scheduled for removal in V11.")] string GetLogLevel(); /// diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogLevelLoader.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogLevelLoader.cs new file mode 100644 index 0000000000..b090ddb77c --- /dev/null +++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogLevelLoader.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using Serilog; +using Serilog.Events; +using Umbraco.Cms.Infrastructure.Logging.Serilog; + +namespace Umbraco.Cms.Core.Logging.Viewer +{ + public class LogLevelLoader : ILogLevelLoader + { + private readonly UmbracoFileConfiguration _umbracoFileConfig; + + public LogLevelLoader(UmbracoFileConfiguration umbracoFileConfig) => _umbracoFileConfig = umbracoFileConfig; + + /// + /// Get the Serilog level values of the global minimum and the UmbracoFile one from the config file. + /// + public ReadOnlyDictionary GetLogLevelsFromSinks() + { + var configuredLogLevels = new Dictionary + { + { "Global", GetGlobalMinLogLevel() }, + { "UmbracoFile", _umbracoFileConfig.RestrictedToMinimumLevel } + }; + + return new ReadOnlyDictionary(configuredLogLevels); + } + + /// + /// Get the Serilog minimum-level value from the config file. + /// + public LogEventLevel GetGlobalMinLogLevel() + { + var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast().Where(Log.IsEnabled).DefaultIfEmpty(LogEventLevel.Information)?.Min() ?? null; + return (LogEventLevel)logLevel; + } + } +} diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs index 7599ab0a16..cb45c7c738 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -6,7 +6,6 @@ using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Serilog.Events; using Serilog.Formatting.Compact.Reader; -using Umbraco.Cms.Core.Logging; namespace Umbraco.Cms.Core.Logging.Viewer { @@ -19,8 +18,9 @@ namespace Umbraco.Cms.Core.Logging.Viewer ILogger logger, ILogViewerConfig logViewerConfig, ILoggingConfiguration loggingConfiguration, + ILogLevelLoader logLevelLoader, global::Serilog.ILogger serilogLog) - : base(logViewerConfig, serilogLog) + : base(logViewerConfig, logLevelLoader, serilogLog) { _logger = logger; _logsPath = loggingConfiguration.LogDirectory; diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs index ce897de0cd..954e9402e2 100644 --- a/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs +++ b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs @@ -1,8 +1,11 @@ -using System; +using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; +using Microsoft.Extensions.DependencyInjection; using Serilog.Events; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Logging.Viewer @@ -10,11 +13,21 @@ namespace Umbraco.Cms.Core.Logging.Viewer public abstract class SerilogLogViewerSourceBase : ILogViewer { private readonly ILogViewerConfig _logViewerConfig; + private readonly ILogLevelLoader _logLevelLoader; private readonly global::Serilog.ILogger _serilogLog; + [Obsolete("Please use ctor with all params instead. Scheduled for removal in V11.")] protected SerilogLogViewerSourceBase(ILogViewerConfig logViewerConfig, global::Serilog.ILogger serilogLog) { _logViewerConfig = logViewerConfig; + _logLevelLoader = StaticServiceProvider.Instance.GetRequiredService(); + _serilogLog = serilogLog; + } + + protected SerilogLogViewerSourceBase(ILogViewerConfig logViewerConfig, ILogLevelLoader logLevelLoader, global::Serilog.ILogger serilogLog) + { + _logViewerConfig = logViewerConfig; + _logLevelLoader = logLevelLoader; _serilogLog = serilogLog; } @@ -43,14 +56,22 @@ namespace Umbraco.Cms.Core.Logging.Viewer return errorCounter.Count; } + /// + /// Get the Serilog minimum-level and UmbracoFile-level values from the config file. + /// + public ReadOnlyDictionary GetLogLevels() + { + return _logLevelLoader.GetLogLevelsFromSinks(); + } + /// /// Get the Serilog minimum-level value from the config file. /// - /// + [Obsolete("Please use LogLevelLoader.GetGlobalMinLogLevel() instead. Scheduled for removal in V11.")] public string GetLogLevel() { var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast().Where(_serilogLog.IsEnabled).DefaultIfEmpty(LogEventLevel.Information)?.Min() ?? null; - return logLevel?.ToString() ?? ""; + return logLevel?.ToString() ?? string.Empty; } public LogLevelCounts GetLogLevelCounts(LogTimePeriod logTimePeriod) @@ -129,7 +150,5 @@ namespace Umbraco.Cms.Core.Logging.Viewer Items = logMessages }; } - - } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs index 9fcf407581..766cf89ea1 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/LogViewerController.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Serilog.Events; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Logging.Viewer; using Umbraco.Cms.Core.Models; @@ -135,6 +137,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return _logViewer.DeleteSavedSearch(item.Name, item.Query); } + [HttpGet] + public ReadOnlyDictionary GetLogLevels() + { + return _logViewer.GetLogLevels(); + } + + [Obsolete("Please use GetLogLevels() instead. Scheduled for removal in V11.")] [HttpGet] public string GetLogLevel() { diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index df7ffd4328..4441ae6e42 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Reflection; using Microsoft.AspNetCore.Hosting; @@ -29,7 +30,8 @@ namespace Umbraco.Extensions IConfiguration configuration) { // Create a serilog logger - var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration, configuration); + var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration, configuration, out var umbracoFileConfig); + services.AddSingleton(umbracoFileConfig); // This is nessasary to pick up all the loggins to MS ILogger. Log.Logger = logger.SerilogLog; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/logviewer.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/logviewer.resource.js index 46ef8d2919..ea7f54e433 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/logviewer.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/logviewer.resource.js @@ -25,7 +25,10 @@ function logViewerResource($q, $http, umbRequestHelper) { getNumberOfErrors: (startDate, endDate) => request('GET', 'GetNumberOfErrors', '?startDate=' + startDate + '&endDate=' + endDate, 'Failed to retrieve number of errors in logs'), - + + getLogLevels: () => + request('GET', 'GetLogLevels', null, 'Failed to retrieve log levels'), + getLogLevel: () => request('GET', 'GetLogLevel', null, 'Failed to retrieve log level'), diff --git a/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.controller.js index d5b4a7ba24..bbcb30368f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.controller.js @@ -138,14 +138,21 @@ vm.commonLogMessages = data; }); - var logLevel = logViewerResource.getLogLevel().then(function(data) { - vm.logLevel = data; - const index = vm.logTypeLabels.findIndex(x => vm.logLevel.startsWith(x)); - vm.logLevelColor = index > -1 ? vm.logTypeColors[index] : '#000'; + var logLevels = logViewerResource.getLogLevels().then(function(data) { + vm.logLevels = {}; + vm.logLevelsCount = 0; + + for (let [key, value] of Object.entries(data)) { + const index = vm.logTypeLabels.findIndex(x => value.startsWith(x)); + if (index > -1) { + vm.logLevels[key] = index; + vm.logLevelsCount++; + } + } }); // Set loading indicator to false when these 3 queries complete - $q.all([savedSearches, numOfErrors, logCounts, commonMsgs, logLevel]).then(function () { + $q.all([savedSearches, numOfErrors, logCounts, commonMsgs, logLevels]).then(function () { vm.loading = false; }); diff --git a/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.html b/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.html index d3f2f86428..eb018db541 100644 --- a/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/logViewer/overview.html @@ -104,8 +104,13 @@ - - {{ vm.logLevel }} + +
+

{{ vm.logTypeLabels[logTypeIndex] }}

+

+ {{sink}}: {{ vm.logTypeLabels[logTypeIndex] }} +

+
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Logging/LogviewerTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Logging/LogviewerTests.cs index 9ba522fbc9..755e42b15d 100644 --- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Logging/LogviewerTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Logging/LogviewerTests.cs @@ -66,7 +66,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Logging ILogger logger = Mock.Of>(); var logViewerConfig = new LogViewerConfig(LogViewerQueryRepository, Mock.Of()); - _logViewer = new SerilogJsonLogViewer(logger, logViewerConfig, loggingConfiguration, Log.Logger); + var logLevelLoader = Mock.Of(); + _logViewer = new SerilogJsonLogViewer(logger, logViewerConfig, loggingConfiguration, logLevelLoader, Log.Logger); } [OneTimeTearDown]