diff --git a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs
index 0bdfe5c425..b653f535fa 100644
--- a/src/Umbraco.Core/Hosting/IHostingEnvironment.cs
+++ b/src/Umbraco.Core/Hosting/IHostingEnvironment.cs
@@ -6,6 +6,10 @@ namespace Umbraco.Core.Hosting
{
string SiteName { get; }
string ApplicationId { get; }
+
+ ///
+ /// Will return the physical path to the root of the application
+ ///
string ApplicationPhysicalPath { get; }
string LocalTempPath { get; }
@@ -27,10 +31,22 @@ namespace Umbraco.Core.Hosting
bool IsHosted { get; }
Version IISVersion { get; }
+
+ // TODO: Should we change this name to MapPathWebRoot ? and also have a new MapPathContentRoot ?
+
+ ///
+ /// Maps a virtual path to a physical path to the application's web root
+ ///
+ ///
+ ///
+ ///
+ /// Depending on the runtime 'web root', this result can vary. For example in Net Framework the web root and the content root are the same, however
+ /// in netcore the web root is /www therefore this will Map to a physical path within www.
+ ///
string MapPath(string path);
///
- /// Maps a virtual path to the application's web root
+ /// Converts a virtual path to an absolute URL path based on the application's web root
///
/// The virtual path. Must start with either ~/ or / else an exception is thrown.
///
diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
index 2099778185..3ccc3b8a8d 100644
--- a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
+++ b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
@@ -11,10 +11,8 @@ namespace Umbraco.Core.Composing
/// Assigns a custom service provider factory to use Umbraco's container
///
///
+ ///
///
- public static IHostBuilder UseUmbraco(this IHostBuilder builder)
- => builder.UseUmbraco(new UmbracoServiceProviderFactory());
-
public static IHostBuilder UseUmbraco(this IHostBuilder builder, UmbracoServiceProviderFactory umbracoServiceProviderFactory)
=> builder.UseServiceProviderFactory(umbracoServiceProviderFactory);
}
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
index f4e8f85281..d810a08648 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
@@ -29,6 +29,8 @@ namespace Umbraco.Core.Logging.Serilog
{
global::Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg));
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
//Set this environment variable - so that it can be used in external config file
//add key="serilog:write-to:RollingFile.pathFormat" value="%BASEDIR%\logs\log.txt" />
Environment.SetEnvironmentVariable("BASEDIR", AppDomain.CurrentDomain.BaseDirectory, EnvironmentVariableTarget.Process);
@@ -57,6 +59,8 @@ namespace Umbraco.Core.Logging.Serilog
/// The number of days to keep log files. Default is set to null which means all logs are kept
public static LoggerConfiguration OutputDefaultTextFile(this LoggerConfiguration logConfig, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null)
{
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
//Main .txt logfile - in similar format to older Log4Net output
//Ends with ..txt as Date is inserted before file extension substring
logConfig.WriteTo.File($@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..txt",
@@ -85,6 +89,8 @@ namespace Umbraco.Core.Logging.Serilog
Encoding encoding = null
)
{
+ // TODO: Deal with this method call since it's obsolete, we need to change this
+
return configuration.Async(
asyncConfiguration => asyncConfiguration.Map(AppDomainId, (_,mapConfiguration) =>
mapConfiguration.File(
@@ -113,6 +119,8 @@ namespace Umbraco.Core.Logging.Serilog
/// The number of days to keep log files. Default is set to null which means all logs are kept
public static LoggerConfiguration OutputDefaultJsonFile(this LoggerConfiguration logConfig, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null)
{
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
//.clef format (Compact log event format, that can be imported into local SEQ & will make searching/filtering logs easier)
//Ends with ..txt as Date is inserted before file extension substring
logConfig.WriteTo.File(new CompactJsonFormatter(), $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\UmbracoTraceLog.{Environment.MachineName}..json",
@@ -131,6 +139,8 @@ namespace Umbraco.Core.Logging.Serilog
/// A Serilog LoggerConfiguration
public static LoggerConfiguration ReadFromConfigFile(this LoggerConfiguration logConfig)
{
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
//Read from main serilog.config file
logConfig.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.config");
@@ -144,6 +154,8 @@ namespace Umbraco.Core.Logging.Serilog
/// A Serilog LoggerConfiguration
public static LoggerConfiguration ReadFromUserConfigFile(this LoggerConfiguration logConfig)
{
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
//A nested logger - where any user configured sinks via config can not effect the main 'umbraco' logger above
logConfig.WriteTo.Logger(cfg =>
cfg.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.user.config"));
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
index bb77869e28..497de58bdd 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
@@ -32,6 +32,8 @@ namespace Umbraco.Core.Logging.Serilog
_ioHelper = ioHelper;
_marchal = marchal;
+ // TODO: we can't use AppDomain.CurrentDomain.BaseDirectory, need a consistent approach between netcore/netframework
+
Log.Logger = new LoggerConfiguration()
.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + logConfigFile)
.CreateLogger();
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs
new file mode 100644
index 0000000000..14f35361e6
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewerConfig.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+
+namespace Umbraco.Core.Logging.Viewer
+{
+ public interface ILogViewerConfig
+ {
+ IReadOnlyList GetSavedSearches();
+ IReadOnlyList AddSavedSearch(string name, string query);
+ IReadOnlyList DeleteSavedSearch(string name, string query);
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs
index aea1c8fae4..54dd58ec03 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs
@@ -5,6 +5,7 @@ using System.Linq;
using Newtonsoft.Json;
using Serilog.Events;
using Serilog.Formatting.Compact.Reader;
+using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
namespace Umbraco.Core.Logging.Viewer
@@ -13,14 +14,19 @@ namespace Umbraco.Core.Logging.Viewer
{
private readonly string _logsPath;
private readonly ILogger _logger;
+ private readonly IHostingEnvironment _hostingEnvironment;
- public JsonLogViewer(ILogger logger, IIOHelper ioHelper, string logsPath = "", string searchPath = "") : base(ioHelper, searchPath)
+ public JsonLogViewer(ILogger logger, ILogViewerConfig logViewerConfig, IHostingEnvironment hostingEnvironment) : base(logViewerConfig)
{
- if (string.IsNullOrEmpty(logsPath))
- logsPath = $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\";
-
- _logsPath = logsPath;
+ _hostingEnvironment = hostingEnvironment;
_logger = logger;
+
+ // TODO: this path is hard coded but it can actually be configured, but that is done via Serilog and we don't have a different abstraction/config
+ // for the logging path. We could make that, but then how would we get that abstraction into the Serilog config? I'm sure there is a way but
+ // don't have time right now to resolve that (since this was hard coded before). We could have a single/simple ILogConfig for umbraco that purely specifies
+ // the logging path and then we can have a special token that we replace in the serilog config that maps to that location? then at least we could inject
+ // that config in places where we are hard coding this path.
+ _logsPath = Path.Combine(_hostingEnvironment.ApplicationPhysicalPath, @"App_Data\Logs\");
}
private const int FileSizeCap = 100;
@@ -62,9 +68,6 @@ namespace Umbraco.Core.Logging.Viewer
{
var logs = new List();
- //Log Directory
- var logDirectory = $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\";
-
var count = 0;
//foreach full day in the range - see if we can find one or more filenames that end with
@@ -74,7 +77,7 @@ namespace Umbraco.Core.Logging.Viewer
//Filename ending to search for (As could be multiple)
var filesToFind = GetSearchPattern(day);
- var filesForCurrentDay = Directory.GetFiles(logDirectory, filesToFind);
+ var filesForCurrentDay = Directory.GetFiles(_logsPath, filesToFind);
//Foreach file we find - open it
foreach (var filePath in filesForCurrentDay)
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
index 79680a3d53..e4acde1265 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
@@ -9,7 +9,8 @@ namespace Umbraco.Core.Logging.Viewer
{
public void Compose(Composition composition)
{
- composition.SetLogViewer(factory => new JsonLogViewer(composition.Logger, factory.GetInstance()));
+ composition.RegisterUnique();
+ composition.SetLogViewer();
}
}
}
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs
new file mode 100644
index 0000000000..5511cd87c7
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerConfig.cs
@@ -0,0 +1,84 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json;
+using Umbraco.Core.Hosting;
+using Formatting = Newtonsoft.Json.Formatting;
+
+namespace Umbraco.Core.Logging.Viewer
+{
+ public class LogViewerConfig : ILogViewerConfig
+ {
+ private readonly IHostingEnvironment _hostingEnvironment;
+ private const string _pathToSearches = "~/Config/logviewer.searches.config.js";
+ private readonly FileInfo _searchesConfig;
+
+ public LogViewerConfig(IHostingEnvironment hostingEnvironment)
+ {
+ _hostingEnvironment = hostingEnvironment;
+ var trimmedPath = _pathToSearches.TrimStart('~', '/').Replace('/', Path.DirectorySeparatorChar);
+ var absolutePath = Path.Combine(_hostingEnvironment.ApplicationPhysicalPath, trimmedPath);
+ _searchesConfig = new FileInfo(absolutePath);
+ }
+
+ public IReadOnlyList GetSavedSearches()
+ {
+ //Our default implementation
+
+ //If file does not exist - lets create it with an empty array
+ EnsureFileExists();
+
+ var rawJson = System.IO.File.ReadAllText(_searchesConfig.FullName);
+ return JsonConvert.DeserializeObject(rawJson);
+ }
+
+ public IReadOnlyList AddSavedSearch(string name, string query)
+ {
+ //Get the existing items
+ var searches = GetSavedSearches().ToList();
+
+ //Add the new item to the bottom of the list
+ searches.Add(new SavedLogSearch { Name = name, Query = query });
+
+ //Serialize to JSON string
+ var rawJson = JsonConvert.SerializeObject(searches, Formatting.Indented);
+
+ //If file does not exist - lets create it with an empty array
+ EnsureFileExists();
+
+ //Write it back down to file
+ System.IO.File.WriteAllText(_searchesConfig.FullName, rawJson);
+
+ //Return the updated object - so we can instantly reset the entire array from the API response
+ //As opposed to push a new item into the array
+ return searches;
+ }
+
+ public IReadOnlyList DeleteSavedSearch(string name, string query)
+ {
+ //Get the existing items
+ var searches = GetSavedSearches().ToList();
+
+ //Removes the search
+ searches.RemoveAll(s => s.Name.Equals(name) && s.Query.Equals(query));
+
+ //Serialize to JSON string
+ var rawJson = JsonConvert.SerializeObject(searches, Formatting.Indented);
+
+ //Write it back down to file
+ System.IO.File.WriteAllText(_searchesConfig.FullName, rawJson);
+
+ //Return the updated object - so we can instantly reset the entire array from the API response
+ return searches;
+ }
+
+ private void EnsureFileExists()
+ {
+ if (_searchesConfig.Exists) return;
+ using (var writer = _searchesConfig.CreateText())
+ {
+ writer.Write("[]");
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs
index 4cc70eaf42..aae2976044 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs
@@ -1,31 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Xml;
-using Newtonsoft.Json;
using Serilog;
using Serilog.Events;
-using Umbraco.Core.Composing;
-using Umbraco.Core.IO;
using Umbraco.Core.Models;
-using Umbraco.Core.Persistence.DatabaseModelDefinitions;
-using Formatting = Newtonsoft.Json.Formatting;
namespace Umbraco.Core.Logging.Viewer
{
+
public abstract class LogViewerSourceBase : ILogViewer
{
- private readonly string _searchesConfigPath;
- private readonly IIOHelper _ioHelper;
+ private readonly ILogViewerConfig _logViewerConfig;
- protected LogViewerSourceBase(IIOHelper ioHelper, string pathToSearches = "")
- {
- if (string.IsNullOrEmpty(pathToSearches))
- // ReSharper disable once StringLiteralTypo
- pathToSearches = ioHelper.MapPath("~/Config/logviewer.searches.config.js");
-
- _searchesConfigPath = pathToSearches;
- _ioHelper = ioHelper;
+ protected LogViewerSourceBase(ILogViewerConfig logViewerConfig)
+ {
+ _logViewerConfig = logViewerConfig;
}
public abstract bool CanHandleLargeLogs { get; }
@@ -38,55 +27,13 @@ namespace Umbraco.Core.Logging.Viewer
public abstract bool CheckCanOpenLogs(LogTimePeriod logTimePeriod);
public virtual IReadOnlyList GetSavedSearches()
- {
- //Our default implementation
-
- //If file does not exist - lets create it with an empty array
- EnsureFileExists(_searchesConfigPath, "[]", _ioHelper);
-
- var rawJson = System.IO.File.ReadAllText(_searchesConfigPath);
- return JsonConvert.DeserializeObject(rawJson);
- }
+ => _logViewerConfig.GetSavedSearches();
public virtual IReadOnlyList AddSavedSearch(string name, string query)
- {
- //Get the existing items
- var searches = GetSavedSearches().ToList();
-
- //Add the new item to the bottom of the list
- searches.Add(new SavedLogSearch { Name = name, Query = query });
-
- //Serialize to JSON string
- var rawJson = JsonConvert.SerializeObject(searches, Formatting.Indented);
-
- //If file does not exist - lets create it with an empty array
- EnsureFileExists(_searchesConfigPath, "[]", _ioHelper);
-
- //Write it back down to file
- System.IO.File.WriteAllText(_searchesConfigPath, rawJson);
-
- //Return the updated object - so we can instantly reset the entire array from the API response
- //As opposed to push a new item into the array
- return searches;
- }
+ => _logViewerConfig.AddSavedSearch(name, query);
public virtual IReadOnlyList DeleteSavedSearch(string name, string query)
- {
- //Get the existing items
- var searches = GetSavedSearches().ToList();
-
- //Removes the search
- searches.RemoveAll(s => s.Name.Equals(name) && s.Query.Equals(query));
-
- //Serialize to JSON string
- var rawJson = JsonConvert.SerializeObject(searches, Formatting.Indented);
-
- //Write it back down to file
- System.IO.File.WriteAllText(_searchesConfigPath, rawJson);
-
- //Return the updated object - so we can instantly reset the entire array from the API response
- return searches;
- }
+ => _logViewerConfig.DeleteSavedSearch(name, query);
public int GetNumberOfErrors(LogTimePeriod logTimePeriod)
{
@@ -182,15 +129,6 @@ namespace Umbraco.Core.Logging.Viewer
};
}
- private static void EnsureFileExists(string path, string contents, IIOHelper ioHelper)
- {
- var absolutePath = ioHelper.MapPath(path);
- if (System.IO.File.Exists(absolutePath)) return;
-
- using (var writer = System.IO.File.CreateText(absolutePath))
- {
- writer.Write(contents);
- }
- }
+
}
}
diff --git a/src/Umbraco.Tests/Logging/LogviewerTests.cs b/src/Umbraco.Tests/Logging/LogviewerTests.cs
index 87cc19a2c6..75ff81a6d5 100644
--- a/src/Umbraco.Tests/Logging/LogviewerTests.cs
+++ b/src/Umbraco.Tests/Logging/LogviewerTests.cs
@@ -33,13 +33,14 @@ namespace Umbraco.Tests.Logging
//Create an example JSON log file to check results
//As a one time setup for all tets in this class/fixture
var ioHelper = TestHelper.IOHelper;
+ var hostingEnv = TestHelper.GetHostingEnvironment();
var exampleLogfilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"Logging\", _logfileName);
- _newLogfileDirPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"App_Data\Logs\");
+ _newLogfileDirPath = Path.Combine(hostingEnv.ApplicationPhysicalPath, @"App_Data\Logs\");
_newLogfilePath = Path.Combine(_newLogfileDirPath, _logfileName);
var exampleSearchfilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"Logging\", _searchfileName);
- _newSearchfileDirPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"Config\");
+ _newSearchfileDirPath = Path.Combine(hostingEnv.ApplicationPhysicalPath, @"Config\");
_newSearchfilePath = Path.Combine(_newSearchfileDirPath, _searchfileName);
//Create/ensure Directory exists
@@ -51,7 +52,8 @@ namespace Umbraco.Tests.Logging
File.Copy(exampleSearchfilePath, _newSearchfilePath, true);
var logger = Mock.Of();
- _logViewer = new JsonLogViewer(logger, ioHelper, logsPath: _newLogfileDirPath, searchPath: _newSearchfilePath);
+ var logViewerConfig = new LogViewerConfig(hostingEnv);
+ _logViewer = new JsonLogViewer(logger, logViewerConfig, hostingEnv);
}
[OneTimeTearDown]
diff --git a/src/Umbraco.Web.Common/Extensions/HostBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/HostBuilderExtensions.cs
new file mode 100644
index 0000000000..3cb0922837
--- /dev/null
+++ b/src/Umbraco.Web.Common/Extensions/HostBuilderExtensions.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.Hosting;
+using Serilog;
+using Umbraco.Core.Composing;
+
+namespace Umbraco.Web.Common.Extensions
+{
+ public static class HostBuilderExtensions
+ {
+ ///
+ /// Assigns a custom service provider factory to use Umbraco's container
+ ///
+ ///
+ ///
+ public static IHostBuilder UseUmbraco(this IHostBuilder builder)
+ {
+ return builder
+ .UseSerilog()
+ .UseUmbraco(new UmbracoServiceProviderFactory());
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
index 7203c4ba29..711659730f 100644
--- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
+++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
@@ -18,6 +18,7 @@
+
diff --git a/src/Umbraco.Web.UI.NetCore/Config/logviewer.searches.config.js b/src/Umbraco.Web.UI.NetCore/Config/logviewer.searches.config.js
new file mode 100644
index 0000000000..345fe23764
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Config/logviewer.searches.config.js
@@ -0,0 +1,42 @@
+[
+ {
+ "name": "Find all logs where the Level is NOT Verbose and NOT Debug",
+ "query": "Not(@Level='Verbose') and Not(@Level='Debug')"
+ },
+ {
+ "name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
+ "query": "Has(@Exception)"
+ },
+ {
+ "name": "Find all logs that have the property 'Duration'",
+ "query": "Has(Duration)"
+ },
+ {
+ "name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
+ "query": "Has(Duration) and Duration > 1000"
+ },
+ {
+ "name": "Find all logs that are from the namespace 'Umbraco.Core'",
+ "query": "StartsWith(SourceContext, 'Umbraco.Core')"
+ },
+ {
+ "name": "Find all logs that use a specific log message template",
+ "query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
+ },
+ {
+ "name": "Find logs where one of the items in the SortedComponentTypes property array is equal to",
+ "query": "SortedComponentTypes[?] = 'Umbraco.Web.Search.ExamineComponent'"
+ },
+ {
+ "name": "Find logs where one of the items in the SortedComponentTypes property array contains",
+ "query": "Contains(SortedComponentTypes[?], 'DatabaseServer')"
+ },
+ {
+ "name": "Find all logs that the message has localhost in it with SQL like",
+ "query": "@Message like '%localhost%'"
+ },
+ {
+ "name": "Find all logs that the message that starts with 'end' in it with SQL like",
+ "query": "@Message like 'end%'"
+ }
+]
diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs
index f0504d77e3..1fe66f6664 100644
--- a/src/Umbraco.Web.UI.NetCore/Program.cs
+++ b/src/Umbraco.Web.UI.NetCore/Program.cs
@@ -1,8 +1,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Serilog;
using Umbraco.Core.Composing;
+using Umbraco.Web.Common.Extensions;
namespace Umbraco.Web.UI.BackOffice
{
diff --git a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj
index 72f29f3c4b..2a3b90d5f1 100644
--- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj
+++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj
@@ -35,4 +35,12 @@
+
+
+
+
+
+
+
+