diff --git a/.gitignore b/.gitignore
index 5f2432313f..d8c3f27d5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -176,3 +176,4 @@ build/temp/
/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/lib/*
/src/Umbraco.Web.UI.NetCore/wwwroot/Umbraco/views/*
/src/Umbraco.Web.UI.NetCore/wwwroot/App_Data/TEMP/*
+/src/Umbraco.Web.UI.NetCore/App_Data/Logs/*
diff --git a/src/Umbraco.Core/Cache/GenericDictionaryRequestAppCache.cs b/src/Umbraco.Core/Cache/GenericDictionaryRequestAppCache.cs
new file mode 100644
index 0000000000..193235ca7e
--- /dev/null
+++ b/src/Umbraco.Core/Cache/GenericDictionaryRequestAppCache.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using Umbraco.Core.Composing;
+
+namespace Umbraco.Core.Cache
+{
+ ///
+ /// Implements a fast on top of HttpContext.Items.
+ ///
+ ///
+ /// If no current HttpContext items can be found (no current HttpContext,
+ /// or no Items...) then this cache acts as a pass-through and does not cache
+ /// anything.
+ ///
+ public class GenericDictionaryRequestAppCache : FastDictionaryAppCacheBase, IRequestCache
+ {
+ ///
+ /// Initializes a new instance of the class with a context, for unit tests!
+ ///
+ public GenericDictionaryRequestAppCache(Func> requestItems) : base()
+ {
+ ContextItems = requestItems;
+ }
+
+ private Func> ContextItems { get; }
+
+ public bool IsAvailable => TryGetContextItems(out _);
+
+ private bool TryGetContextItems(out IDictionary items)
+ {
+ items = ContextItems?.Invoke();
+ return items != null;
+ }
+
+ ///
+ public override object Get(string key, Func factory)
+ {
+ //no place to cache so just return the callback result
+ if (!TryGetContextItems(out var items)) return factory();
+
+ key = GetCacheKey(key);
+
+ Lazy result;
+
+ try
+ {
+ EnterWriteLock();
+ result = items[key] as Lazy; // null if key not found
+
+ // cannot create value within the lock, so if result.IsValueCreated is false, just
+ // do nothing here - means that if creation throws, a race condition could cause
+ // more than one thread to reach the return statement below and throw - accepted.
+
+ if (result == null || SafeLazy.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
+ {
+ result = SafeLazy.GetSafeLazy(factory);
+ items[key] = result;
+ }
+ }
+ finally
+ {
+ ExitWriteLock();
+ }
+
+ // using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
+ // exceptions (but try again and again) and silently eat them - however at
+ // some point we have to report them - so need to re-throw here
+
+ // this does not throw anymore
+ //return result.Value;
+
+ var value = result.Value; // will not throw (safe lazy)
+ if (value is SafeLazy.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
+ return value;
+ }
+
+ public bool Set(string key, object value)
+ {
+ //no place to cache so just return the callback result
+ if (!TryGetContextItems(out var items)) return false;
+ key = GetCacheKey(key);
+ try
+ {
+
+ EnterWriteLock();
+ items[key] = SafeLazy.GetSafeLazy(() => value);
+ }
+ finally
+ {
+ ExitWriteLock();
+ }
+ return true;
+ }
+
+ public bool Remove(string key)
+ {
+ //no place to cache so just return the callback result
+ if (!TryGetContextItems(out var items)) return false;
+ key = GetCacheKey(key);
+ try
+ {
+
+ EnterWriteLock();
+ items.Remove(key);
+ }
+ finally
+ {
+ ExitWriteLock();
+ }
+ return true;
+ }
+
+ #region Entries
+
+ protected override IEnumerable GetDictionaryEntries()
+ {
+ const string prefix = CacheItemPrefix + "-";
+
+ if (!TryGetContextItems(out var items)) return Enumerable.Empty();
+
+ return items.Cast()
+ .Where(x => x.Key is string s && s.StartsWith(prefix));
+ }
+
+ protected override void RemoveEntry(string key)
+ {
+ if (!TryGetContextItems(out var items)) return;
+
+ items.Remove(key);
+ }
+
+ protected override object GetEntry(string key)
+ {
+ return !TryGetContextItems(out var items) ? null : items[key];
+ }
+
+ #endregion
+
+ #region Lock
+
+ private const string ContextItemsLockKey = "Umbraco.Core.Cache.HttpRequestCache::LockEntered";
+
+ protected override void EnterReadLock() => EnterWriteLock();
+
+ protected override void EnterWriteLock()
+ {
+ if (!TryGetContextItems(out var items)) return;
+
+ // note: cannot keep 'entered' as a class variable here,
+ // since there is one per request - so storing it within
+ // ContextItems - which is locked, so this should be safe
+
+ var entered = false;
+ Monitor.Enter(items, ref entered);
+ items[ContextItemsLockKey] = entered;
+ }
+
+ protected override void ExitReadLock() => ExitWriteLock();
+
+ protected override void ExitWriteLock()
+ {
+ if (!TryGetContextItems(out var items)) return;
+
+ var entered = (bool?)items[ContextItemsLockKey] ?? false;
+ if (entered)
+ Monitor.Exit(items);
+ items.Remove(ContextItemsLockKey);
+ }
+
+ #endregion
+
+ public IEnumerator> GetEnumerator()
+ {
+ if (!TryGetContextItems(out var items))
+ {
+ yield break;
+ }
+
+ foreach (var item in items)
+ {
+ yield return new KeyValuePair(item.Key.ToString(), item.Value);
+ }
+ }
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+ }
+}
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.Core/Logging/ILoggingConfiguration.cs b/src/Umbraco.Core/Logging/ILoggingConfiguration.cs
new file mode 100644
index 0000000000..47e2d8fa7c
--- /dev/null
+++ b/src/Umbraco.Core/Logging/ILoggingConfiguration.cs
@@ -0,0 +1,13 @@
+namespace Umbraco.Core.Logging
+{
+
+ public interface ILoggingConfiguration
+ {
+ ///
+ /// The physical path where logs are stored
+ ///
+ string LogDirectory { get; }
+ string LogConfigurationFile { get; }
+ string UserLogConfigurationFile { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Logging/IProfiler.cs b/src/Umbraco.Core/Logging/IProfiler.cs
index 1327651197..d855612c95 100644
--- a/src/Umbraco.Core/Logging/IProfiler.cs
+++ b/src/Umbraco.Core/Logging/IProfiler.cs
@@ -2,18 +2,12 @@
namespace Umbraco.Core.Logging
{
+
///
/// Defines the profiling service.
///
public interface IProfiler
{
- ///
- /// Renders the profiling results.
- ///
- /// The profiling results.
- /// Generally used for HTML rendering.
- string Render();
-
///
/// Gets an that will time the code between its creation and disposal.
///
diff --git a/src/Umbraco.Core/Logging/IProfilerHtml.cs b/src/Umbraco.Core/Logging/IProfilerHtml.cs
new file mode 100644
index 0000000000..4f9ee62e0b
--- /dev/null
+++ b/src/Umbraco.Core/Logging/IProfilerHtml.cs
@@ -0,0 +1,15 @@
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// Used to render a profiler in a web page
+ ///
+ public interface IProfilerHtml
+ {
+ ///
+ /// Renders the profiling results.
+ ///
+ /// The profiling results.
+ /// Generally used for HTML rendering.
+ string Render();
+ }
+}
diff --git a/src/Umbraco.Core/Logging/LogProfiler.cs b/src/Umbraco.Core/Logging/LogProfiler.cs
index 294f92dad3..a1d2a2e61f 100644
--- a/src/Umbraco.Core/Logging/LogProfiler.cs
+++ b/src/Umbraco.Core/Logging/LogProfiler.cs
@@ -15,12 +15,6 @@ namespace Umbraco.Core.Logging
_logger = logger;
}
- ///
- public string Render()
- {
- return string.Empty;
- }
-
///
public IDisposable Step(string name)
{
diff --git a/src/Umbraco.Core/Logging/LoggingConfiguration.cs b/src/Umbraco.Core/Logging/LoggingConfiguration.cs
new file mode 100644
index 0000000000..c657c9d430
--- /dev/null
+++ b/src/Umbraco.Core/Logging/LoggingConfiguration.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace Umbraco.Core.Logging
+{
+ public class LoggingConfiguration : ILoggingConfiguration
+ {
+ public LoggingConfiguration(string logDirectory, string logConfigurationFile, string userLogConfigurationFile)
+ {
+ LogDirectory = logDirectory ?? throw new ArgumentNullException(nameof(logDirectory));
+ LogConfigurationFile = logConfigurationFile ?? throw new ArgumentNullException(nameof(logConfigurationFile));
+ UserLogConfigurationFile = userLogConfigurationFile ?? throw new ArgumentNullException(nameof(userLogConfigurationFile));
+ }
+
+ public string LogDirectory { get; }
+
+ public string LogConfigurationFile { get; }
+
+ public string UserLogConfigurationFile { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Logging/VoidProfiler.cs b/src/Umbraco.Core/Logging/VoidProfiler.cs
index 51bec521a3..d771fd7630 100644
--- a/src/Umbraco.Core/Logging/VoidProfiler.cs
+++ b/src/Umbraco.Core/Logging/VoidProfiler.cs
@@ -6,11 +6,6 @@ namespace Umbraco.Core.Logging
{
private readonly VoidDisposable _disposable = new VoidDisposable();
- public string Render()
- {
- return string.Empty;
- }
-
public IDisposable Step(string name)
{
return _disposable;
diff --git a/src/Umbraco.Core/Net/NullSessionIdResolver.cs b/src/Umbraco.Core/Net/NullSessionIdResolver.cs
new file mode 100644
index 0000000000..6bfa578268
--- /dev/null
+++ b/src/Umbraco.Core/Net/NullSessionIdResolver.cs
@@ -0,0 +1,7 @@
+namespace Umbraco.Net
+{
+ public class NullSessionIdResolver : ISessionIdResolver
+ {
+ public string SessionId => null;
+ }
+}
diff --git a/src/Umbraco.Core/Web/IRequestAccessor.cs b/src/Umbraco.Core/Web/IRequestAccessor.cs
index 8a92b6ee22..7dc77d3c29 100644
--- a/src/Umbraco.Core/Web/IRequestAccessor.cs
+++ b/src/Umbraco.Core/Web/IRequestAccessor.cs
@@ -9,5 +9,6 @@ namespace Umbraco.Web
string GetQueryStringValue(string name);
event EventHandler EndRequest;
event EventHandler RouteAttempt;
+ Uri GetRequestUrl();
}
}
diff --git a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
index 2099778185..ab758d42af 100644
--- a/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
+++ b/src/Umbraco.Infrastructure/Composing/HostBuilderExtensions.cs
@@ -13,8 +13,17 @@ namespace Umbraco.Core.Composing
///
///
public static IHostBuilder UseUmbraco(this IHostBuilder builder)
- => builder.UseUmbraco(new UmbracoServiceProviderFactory());
+ {
+ return builder
+ .UseUmbraco(new UmbracoServiceProviderFactory());
+ }
+ ///
+ /// Assigns a custom service provider factory to use Umbraco's container
+ ///
+ ///
+ ///
+ ///
public static IHostBuilder UseUmbraco(this IHostBuilder builder, UmbracoServiceProviderFactory umbracoServiceProviderFactory)
=> builder.UseServiceProviderFactory(umbracoServiceProviderFactory);
}
diff --git a/src/Umbraco.Infrastructure/Diagnostics/MiniDump.cs b/src/Umbraco.Infrastructure/Diagnostics/MiniDump.cs
index 9bc0b1c3fb..57e9b5204b 100644
--- a/src/Umbraco.Infrastructure/Diagnostics/MiniDump.cs
+++ b/src/Umbraco.Infrastructure/Diagnostics/MiniDump.cs
@@ -3,6 +3,7 @@ using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Umbraco.Core.Composing;
+using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
namespace Umbraco.Core.Diagnostics
@@ -100,7 +101,7 @@ namespace Umbraco.Core.Diagnostics
return bRet;
}
- public static bool Dump(IMarchal marchal, IIOHelper ioHelper, Option options = Option.WithFullMemory, bool withException = false)
+ public static bool Dump(IMarchal marchal, IHostingEnvironment hostingEnvironment, Option options = Option.WithFullMemory, bool withException = false)
{
lock (LockO)
{
@@ -110,7 +111,7 @@ namespace Umbraco.Core.Diagnostics
// filter everywhere in our code = not!
var stacktrace = withException ? Environment.StackTrace : string.Empty;
- var filepath = ioHelper.MapPath("~/App_Data/MiniDump");
+ var filepath = Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "App_Data/MiniDump");
if (Directory.Exists(filepath) == false)
Directory.CreateDirectory(filepath);
@@ -122,11 +123,11 @@ namespace Umbraco.Core.Diagnostics
}
}
- public static bool OkToDump(IIOHelper ioHelper)
+ public static bool OkToDump(IHostingEnvironment hostingEnvironment)
{
lock (LockO)
{
- var filepath = ioHelper.MapPath("~/App_Data/MiniDump");
+ var filepath = Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "App_Data/MiniDump");
if (Directory.Exists(filepath) == false) return true;
var count = Directory.GetFiles(filepath, "*.dmp").Length;
return count < 8;
diff --git a/src/Umbraco.Infrastructure/Logging/MessageTemplates.cs b/src/Umbraco.Infrastructure/Logging/MessageTemplates.cs
index 4640007e1a..712ff85e16 100644
--- a/src/Umbraco.Infrastructure/Logging/MessageTemplates.cs
+++ b/src/Umbraco.Infrastructure/Logging/MessageTemplates.cs
@@ -16,14 +16,14 @@ namespace Umbraco.Core.Logging
// but it only has a pre-release NuGet package. So, we've got to use Serilog's code, which
// means we cannot get rid of Serilog entirely. We may want to revisit this at some point.
+ // TODO: Do we still need this, is there a non-pre release package shipped?
+
private static readonly Lazy MinimalLogger = new Lazy(() => new LoggerConfiguration().CreateLogger());
public string Render(string messageTemplate, params object[] args)
{
- // by default, unless initialized otherwise, Log.Logger is SilentLogger which cannot bind message
- // templates. Log.Logger is set to a true Logger when initializing Umbraco's logger, but in case
- // that has not been done already - use a temp minimal logger (eg for tests).
- var logger = Log.Logger as global::Serilog.Core.Logger ?? MinimalLogger.Value;
+ // resolve a minimal logger instance which is used to bind message templates
+ var logger = MinimalLogger.Value;
var bound = logger.BindMessageTemplate(messageTemplate, args, out var parsedTemplate, out var boundProperties);
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs
index 45468ace9f..704e80d302 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestIdEnricher.cs
@@ -11,13 +11,13 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
/// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpRequestIdEnricher.cs
/// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want
///
- internal class HttpRequestIdEnricher : ILogEventEnricher
+ public class HttpRequestIdEnricher : ILogEventEnricher
{
- private readonly Func _requestCacheGetter;
+ private readonly IRequestCache _requestCache;
- public HttpRequestIdEnricher(Func requestCacheGetter)
+ public HttpRequestIdEnricher(IRequestCache requestCache)
{
- _requestCacheGetter = requestCacheGetter;
+ _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
}
///
@@ -34,11 +34,8 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
- var requestCache = _requestCacheGetter();
- if(requestCache is null) return;
-
Guid requestId;
- if (!LogHttpRequest.TryGetCurrentHttpRequestId(out requestId, requestCache))
+ if (!LogHttpRequest.TryGetCurrentHttpRequestId(out requestId, _requestCache))
return;
var requestIdProperty = new LogEventProperty(HttpRequestIdPropertyName, new ScalarValue(requestId));
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs
index 08eb6b93f0..20643ff539 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpRequestNumberEnricher.cs
@@ -13,9 +13,9 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
/// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpRequestNumberEnricher.cs
/// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want
///
- internal class HttpRequestNumberEnricher : ILogEventEnricher
+ public class HttpRequestNumberEnricher : ILogEventEnricher
{
- private readonly Func _requestCacheGetter;
+ private readonly IRequestCache _requestCache;
private static int _lastRequestNumber;
private static readonly string _requestNumberItemName = typeof(HttpRequestNumberEnricher).Name + "+RequestNumber";
@@ -25,9 +25,9 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
private const string _httpRequestNumberPropertyName = "HttpRequestNumber";
- public HttpRequestNumberEnricher(Func requestCacheGetter)
+ public HttpRequestNumberEnricher(IRequestCache requestCache)
{
- _requestCacheGetter = requestCacheGetter;
+ _requestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
}
///
@@ -39,10 +39,7 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
- var requestCache = _requestCacheGetter();
- if (requestCache is null) return;
-
- var requestNumber = requestCache.Get(_requestNumberItemName,
+ var requestNumber = _requestCache.Get(_requestNumberItemName,
() => Interlocked.Increment(ref _lastRequestNumber));
var requestNumberProperty = new LogEventProperty(_httpRequestNumberPropertyName, new ScalarValue(requestNumber));
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs
index 1558cdcf21..19572b5b42 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/HttpSessionIdEnricher.cs
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Logging.Serilog.Enrichers
/// Original source - https://github.com/serilog-web/classic/blob/master/src/SerilogWeb.Classic/Classic/Enrichers/HttpSessionIdEnricher.cs
/// Nupkg: 'Serilog.Web.Classic' contains handlers & extra bits we do not want
///
- internal class HttpSessionIdEnricher : ILogEventEnricher
+ public class HttpSessionIdEnricher : ILogEventEnricher
{
private readonly ISessionIdResolver _sessionIdResolver;
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs
new file mode 100644
index 0000000000..1f495d3a50
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/Enrichers/ThreadAbortExceptionEnricher.cs
@@ -0,0 +1,96 @@
+using System;
+using System.Reflection;
+using System.Threading;
+using Serilog.Core;
+using Serilog.Events;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Diagnostics;
+using Umbraco.Core.Hosting;
+
+namespace Umbraco.Infrastructure.Logging.Serilog.Enrichers
+{
+ ///
+ /// Enriches the log if there are ThreadAbort exceptions and will automatically create a minidump if it can
+ ///
+ public class ThreadAbortExceptionEnricher : ILogEventEnricher
+ {
+ private readonly ICoreDebugSettings _coreDebugSettings;
+ private readonly IHostingEnvironment _hostingEnvironment;
+ private readonly IMarchal _marchal;
+
+ public ThreadAbortExceptionEnricher(ICoreDebugSettings coreDebugSettings, IHostingEnvironment hostingEnvironment, IMarchal marchal)
+ {
+ _coreDebugSettings = coreDebugSettings;
+ _hostingEnvironment = hostingEnvironment;
+ _marchal = marchal;
+ }
+
+ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ switch (logEvent.Level)
+ {
+ case LogEventLevel.Error:
+ case LogEventLevel.Fatal:
+ DumpThreadAborts(logEvent, propertyFactory);
+ break;
+ }
+ }
+
+ private void DumpThreadAborts(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ {
+ if (!IsTimeoutThreadAbortException(logEvent.Exception)) return;
+
+ var message = "The thread has been aborted, because the request has timed out.";
+
+ // dump if configured, or if stacktrace contains Monitor.ReliableEnter
+ var dump = _coreDebugSettings.DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(logEvent.Exception);
+
+ // dump if it is ok to dump (might have a cap on number of dump...)
+ dump &= MiniDump.OkToDump(_hostingEnvironment);
+
+ if (!dump)
+ {
+ message += ". No minidump was created.";
+ logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadAbortExceptionInfo", message));
+ }
+ else
+ try
+ {
+ var dumped = MiniDump.Dump(_marchal, _hostingEnvironment, withException: true);
+ message += dumped
+ ? ". A minidump was created in App_Data/MiniDump."
+ : ". Failed to create a minidump.";
+ logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadAbortExceptionInfo", message));
+ }
+ catch (Exception ex)
+ {
+ message = "Failed to create a minidump. " + ex;
+ logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("ThreadAbortExceptionInfo", message));
+ }
+ }
+
+ private static bool IsTimeoutThreadAbortException(Exception exception)
+ {
+ if (!(exception is ThreadAbortException abort)) return false;
+ if (abort.ExceptionState == null) return false;
+
+ var stateType = abort.ExceptionState.GetType();
+ if (stateType.FullName != "System.Web.HttpApplication+CancelModuleException") return false;
+
+ var timeoutField = stateType.GetField("_timeout", BindingFlags.Instance | BindingFlags.NonPublic);
+ if (timeoutField == null) return false;
+
+ return (bool)timeoutField.GetValue(abort.ExceptionState);
+ }
+
+ private static bool IsMonitorEnterThreadAbortException(Exception exception)
+ {
+ if (!(exception is ThreadAbortException abort)) return false;
+
+ var stacktrace = abort.StackTrace;
+ return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
+ }
+
+
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
index f4e8f85281..dfcc401ea3 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/LoggerConfigExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.IO;
using System.Text;
using Serilog;
using Serilog.Configuration;
@@ -6,11 +7,8 @@ using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Compact;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Composing;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging.Serilog.Enrichers;
-using Umbraco.Net;
namespace Umbraco.Core.Logging.Serilog
{
@@ -24,27 +22,30 @@ namespace Umbraco.Core.Logging.Serilog
/// It is highly recommended that you keep/use this default in your own logging config customizations
///
/// A Serilog LoggerConfiguration
- ///
- public static LoggerConfiguration MinimalConfiguration(this LoggerConfiguration logConfig, IHostingEnvironment hostingEnvironment, ISessionIdResolver sessionIdResolver, Func requestCacheGetter)
+ ///
+ ///
+ public static LoggerConfiguration MinimalConfiguration(
+ this LoggerConfiguration logConfig,
+ IHostingEnvironment hostingEnvironment,
+ ILoggingConfiguration loggingConfiguration)
{
global::Serilog.Debugging.SelfLog.Enable(msg => System.Diagnostics.Debug.WriteLine(msg));
//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);
- Environment.SetEnvironmentVariable("MACHINENAME", Environment.MachineName, EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("UMBLOGDIR", loggingConfiguration.LogDirectory, EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("BASEDIR", hostingEnvironment.ApplicationPhysicalPath, EnvironmentVariableTarget.Process);
+ Environment.SetEnvironmentVariable("MACHINENAME", Environment.MachineName, EnvironmentVariableTarget.Process);
logConfig.MinimumLevel.Verbose() //Set to highest level of logging (as any sinks may want to restrict it to Errors only)
.Enrich.WithProcessId()
.Enrich.WithProcessName()
.Enrich.WithThreadId()
- .Enrich.WithProperty(AppDomainId, AppDomain.CurrentDomain.Id)
+ .Enrich.WithProperty(AppDomainId, AppDomain.CurrentDomain.Id)
.Enrich.WithProperty("AppDomainAppId", hostingEnvironment.ApplicationId.ReplaceNonAlphanumericChars(string.Empty))
.Enrich.WithProperty("MachineName", Environment.MachineName)
.Enrich.With()
- .Enrich.With(new HttpSessionIdEnricher(sessionIdResolver))
- .Enrich.With(new HttpRequestNumberEnricher(requestCacheGetter))
- .Enrich.With(new HttpRequestIdEnricher(requestCacheGetter));
+ .Enrich.FromLogContext(); // allows us to dynamically enrich
return logConfig;
}
@@ -53,13 +54,14 @@ namespace Umbraco.Core.Logging.Serilog
/// Outputs a .txt format log at /App_Data/Logs/
///
/// A Serilog LoggerConfiguration
+ ///
/// The log level you wish the JSON file to collect - default is Verbose (highest)
/// 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)
+ public static LoggerConfiguration OutputDefaultTextFile(this LoggerConfiguration logConfig, ILoggingConfiguration loggingConfiguration, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null)
{
//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",
+ logConfig.WriteTo.File(Path.Combine(loggingConfiguration.LogDirectory, $@"UmbracoTraceLog.{Environment.MachineName}..txt"),
shared: true,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: minimumLevel,
@@ -99,7 +101,8 @@ namespace Umbraco.Core.Logging.Serilog
rollingInterval,
rollOnFileSizeLimit,
retainedFileCountLimit,
- encoding),
+ encoding,
+ null),
sinkMapCountLimit:0)
);
}
@@ -109,13 +112,14 @@ namespace Umbraco.Core.Logging.Serilog
/// Outputs a CLEF format JSON log at /App_Data/Logs/
///
/// A Serilog LoggerConfiguration
+ ///
/// The log level you wish the JSON file to collect - default is Verbose (highest)
/// 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)
+ public static LoggerConfiguration OutputDefaultJsonFile(this LoggerConfiguration logConfig, ILoggingConfiguration loggingConfiguration, LogEventLevel minimumLevel = LogEventLevel.Verbose, int? retainedFileCount = null)
{
//.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",
+ logConfig.WriteTo.File(new CompactJsonFormatter(), Path.Combine(loggingConfiguration.LogDirectory, $@"UmbracoTraceLog.{Environment.MachineName}..json"),
shared: true,
rollingInterval: RollingInterval.Day, //Create a new JSON file every day
retainedFileCountLimit: retainedFileCount, //Setting to null means we keep all files - default is 31 days
@@ -129,10 +133,11 @@ namespace Umbraco.Core.Logging.Serilog
/// That allows the main logging pipeline to be configured
///
/// A Serilog LoggerConfiguration
- public static LoggerConfiguration ReadFromConfigFile(this LoggerConfiguration logConfig)
+ ///
+ public static LoggerConfiguration ReadFromConfigFile(this LoggerConfiguration logConfig, ILoggingConfiguration loggingConfiguration)
{
//Read from main serilog.config file
- logConfig.ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + @"\config\serilog.config");
+ logConfig.ReadFrom.AppSettings(filePath: loggingConfiguration.LogConfigurationFile);
return logConfig;
}
@@ -142,13 +147,15 @@ namespace Umbraco.Core.Logging.Serilog
/// That allows a separate logging pipeline to be configured that will not affect the main Umbraco log
///
/// A Serilog LoggerConfiguration
- public static LoggerConfiguration ReadFromUserConfigFile(this LoggerConfiguration logConfig)
+ ///
+ public static LoggerConfiguration ReadFromUserConfigFile(this LoggerConfiguration logConfig, ILoggingConfiguration loggingConfiguration)
{
//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"));
+ cfg.ReadFrom.AppSettings(filePath: loggingConfiguration.UserLogConfigurationFile));
return logConfig;
}
+
}
}
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogComposer.cs b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogComposer.cs
new file mode 100644
index 0000000000..18b417d428
--- /dev/null
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogComposer.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Umbraco.Core;
+using Umbraco.Core.Composing;
+using Umbraco.Core.Logging.Serilog.Enrichers;
+using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
+
+namespace Umbraco.Infrastructure.Logging.Serilog
+{
+ public class SerilogComposer : ICoreComposer
+ {
+ public void Compose(Composition composition)
+ {
+ composition.RegisterUnique();
+ composition.RegisterUnique();
+ composition.RegisterUnique();
+ composition.RegisterUnique();
+ }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
index bb77869e28..38af9554ab 100644
--- a/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
+++ b/src/Umbraco.Infrastructure/Logging/Serilog/SerilogLogger.cs
@@ -1,72 +1,56 @@
using System;
using System.IO;
-using System.Reflection;
-using System.Threading;
using Serilog;
using Serilog.Events;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Diagnostics;
using Umbraco.Core.Hosting;
-using Umbraco.Core.IO;
-using Umbraco.Net;
namespace Umbraco.Core.Logging.Serilog
{
+
///
/// Implements on top of Serilog.
///
public class SerilogLogger : ILogger, IDisposable
{
- private readonly ICoreDebugSettings _coreDebugSettings;
- private readonly IIOHelper _ioHelper;
- private readonly IMarchal _marchal;
+ public global::Serilog.ILogger SerilogLog { get; }
///
/// Initialize a new instance of the class with a configuration file.
///
///
- public SerilogLogger(ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal, FileInfo logConfigFile)
+ public SerilogLogger(FileInfo logConfigFile)
{
- _coreDebugSettings = coreDebugSettings;
- _ioHelper = ioHelper;
- _marchal = marchal;
-
- Log.Logger = new LoggerConfiguration()
- .ReadFrom.AppSettings(filePath: AppDomain.CurrentDomain.BaseDirectory + logConfigFile)
+ SerilogLog = new LoggerConfiguration()
+ .ReadFrom.AppSettings(filePath: logConfigFile.FullName)
.CreateLogger();
}
- public SerilogLogger(ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal, LoggerConfiguration logConfig)
+ public SerilogLogger(LoggerConfiguration logConfig)
{
- _coreDebugSettings = coreDebugSettings;
- _ioHelper = ioHelper;
- _marchal = marchal;
-
//Configure Serilog static global logger with config passed in
- Log.Logger = logConfig.CreateLogger();
+ SerilogLog = logConfig.CreateLogger();
}
///
/// Creates a logger with some pre-defined configuration and remainder from config file
///
/// Used by UmbracoApplicationBase to get its logger.
- public static SerilogLogger CreateWithDefaultConfiguration(IHostingEnvironment hostingEnvironment, ISessionIdResolver sessionIdResolver, Func requestCacheGetter, ICoreDebugSettings coreDebugSettings, IIOHelper ioHelper, IMarchal marchal)
+ public static SerilogLogger CreateWithDefaultConfiguration(IHostingEnvironment hostingEnvironment, ILoggingConfiguration loggingConfiguration)
{
var loggerConfig = new LoggerConfiguration();
loggerConfig
- .MinimalConfiguration(hostingEnvironment, sessionIdResolver, requestCacheGetter)
- .ReadFromConfigFile()
- .ReadFromUserConfigFile();
+ .MinimalConfiguration(hostingEnvironment, loggingConfiguration)
+ .ReadFromConfigFile(loggingConfiguration)
+ .ReadFromUserConfigFile(loggingConfiguration);
- return new SerilogLogger(coreDebugSettings, ioHelper, marchal, loggerConfig);
+ return new SerilogLogger(loggerConfig);
}
///
/// Gets a contextualized logger.
///
private global::Serilog.ILogger LoggerFor(Type reporting)
- => Log.Logger.ForContext(reporting);
+ => SerilogLog.ForContext(reporting);
///
/// Maps Umbraco's log level to Serilog's.
@@ -99,8 +83,7 @@ namespace Umbraco.Core.Logging.Serilog
///
public void Fatal(Type reporting, Exception exception, string message)
{
- var logger = LoggerFor(reporting);
- DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref message);
+ var logger = LoggerFor(reporting);
logger.Fatal(exception, message);
}
@@ -108,8 +91,7 @@ namespace Umbraco.Core.Logging.Serilog
public void Fatal(Type reporting, Exception exception)
{
var logger = LoggerFor(reporting);
- var message = "Exception.";
- DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref message);
+ var message = "Exception.";
logger.Fatal(exception, message);
}
@@ -128,16 +110,14 @@ namespace Umbraco.Core.Logging.Serilog
///
public void Fatal(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues)
{
- var logger = LoggerFor(reporting);
- DumpThreadAborts(logger, LogEventLevel.Fatal, exception, ref messageTemplate);
+ var logger = LoggerFor(reporting);
logger.Fatal(exception, messageTemplate, propertyValues);
}
///
public void Error(Type reporting, Exception exception, string message)
{
- var logger = LoggerFor(reporting);
- DumpThreadAborts(logger, LogEventLevel.Error, exception, ref message);
+ var logger = LoggerFor(reporting);
logger.Error(exception, message);
}
@@ -146,7 +126,6 @@ namespace Umbraco.Core.Logging.Serilog
{
var logger = LoggerFor(reporting);
var message = "Exception";
- DumpThreadAborts(logger, LogEventLevel.Error, exception, ref message);
logger.Error(exception, message);
}
@@ -166,67 +145,9 @@ namespace Umbraco.Core.Logging.Serilog
public void Error(Type reporting, Exception exception, string messageTemplate, params object[] propertyValues)
{
var logger = LoggerFor(reporting);
- DumpThreadAborts(logger, LogEventLevel.Error, exception, ref messageTemplate);
logger.Error(exception, messageTemplate, propertyValues);
}
- private void DumpThreadAborts(global::Serilog.ILogger logger, LogEventLevel level, Exception exception, ref string messageTemplate)
- {
- var dump = false;
-
- if (IsTimeoutThreadAbortException(exception))
- {
- messageTemplate += "\r\nThe thread has been aborted, because the request has timed out.";
-
- // dump if configured, or if stacktrace contains Monitor.ReliableEnter
- dump = _coreDebugSettings.DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
-
- // dump if it is ok to dump (might have a cap on number of dump...)
- dump &= MiniDump.OkToDump(_ioHelper);
- }
-
- if (dump)
- {
- try
- {
- var dumped = MiniDump.Dump(_marchal, _ioHelper, withException: true);
- messageTemplate += dumped
- ? "\r\nA minidump was created in App_Data/MiniDump"
- : "\r\nFailed to create a minidump";
- }
- catch (Exception ex)
- {
- messageTemplate += "\r\nFailed to create a minidump";
-
- //Log a new entry (as opposed to appending to same log entry)
- logger.Write(level, ex, "Failed to create a minidump ({ExType}: {ExMessage})",
- new object[]{ ex.GetType().FullName, ex.Message });
- }
- }
- }
-
- private static bool IsMonitorEnterThreadAbortException(Exception exception)
- {
- if (!(exception is ThreadAbortException abort)) return false;
-
- var stacktrace = abort.StackTrace;
- return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
- }
-
- private static bool IsTimeoutThreadAbortException(Exception exception)
- {
- if (!(exception is ThreadAbortException abort)) return false;
- if (abort.ExceptionState == null) return false;
-
- var stateType = abort.ExceptionState.GetType();
- if (stateType.FullName != "System.Web.HttpApplication+CancelModuleException") return false;
-
- var timeoutField = stateType.GetField("_timeout", BindingFlags.Instance | BindingFlags.NonPublic);
- if (timeoutField == null) return false;
-
- return (bool) timeoutField.GetValue(abort.ExceptionState);
- }
-
///
public void Warn(Type reporting, string message)
{
@@ -289,7 +210,7 @@ namespace Umbraco.Core.Logging.Serilog
public void Dispose()
{
- Log.CloseAndFlush();
+ SerilogLog.DisposeIfDisposable();
}
}
}
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs
index dbdd7842ba..6763b0ebbb 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/ILogViewer.cs
@@ -1,7 +1,5 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using Umbraco.Core.Models;
-using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Logging.Viewer
{
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/LogViewerComposer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerComposer.cs
index 79680a3d53..ee115be325 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/JsonLogViewer.cs b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs
similarity index 87%
rename from src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs
rename to src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs
index aea1c8fae4..366a0fb9de 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/JsonLogViewer.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogJsonLogViewer.cs
@@ -5,22 +5,25 @@ 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
{
- internal class JsonLogViewer : LogViewerSourceBase
+ internal class SerilogJsonLogViewer : SerilogLogViewerSourceBase
{
private readonly string _logsPath;
private readonly ILogger _logger;
- public JsonLogViewer(ILogger logger, IIOHelper ioHelper, string logsPath = "", string searchPath = "") : base(ioHelper, searchPath)
+ public SerilogJsonLogViewer(
+ ILogger logger,
+ ILogViewerConfig logViewerConfig,
+ ILoggingConfiguration loggingConfiguration,
+ global::Serilog.ILogger serilogLog)
+ : base(logViewerConfig, serilogLog)
{
- if (string.IsNullOrEmpty(logsPath))
- logsPath = $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\";
-
- _logsPath = logsPath;
_logger = logger;
+ _logsPath = loggingConfiguration.LogDirectory;
}
private const int FileSizeCap = 100;
@@ -62,9 +65,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 +74,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)
@@ -130,7 +130,7 @@ namespace Umbraco.Core.Logging.Viewer
{
// As we are reading/streaming one line at a time in the JSON file
// Thus we can not report the line number, as it will always be 1
- _logger.Error(ex, "Unable to parse a line in the JSON log file");
+ _logger.Error(ex, "Unable to parse a line in the JSON log file");
evt = null;
return true;
diff --git a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs
similarity index 60%
rename from src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs
rename to src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs
index 4cc70eaf42..7c8503a37e 100644
--- a/src/Umbraco.Infrastructure/Logging/Viewer/LogViewerSourceBase.cs
+++ b/src/Umbraco.Infrastructure/Logging/Viewer/SerilogLogViewerSourceBase.cs
@@ -1,31 +1,22 @@
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
+
+ public abstract class SerilogLogViewerSourceBase : ILogViewer
{
- private readonly string _searchesConfigPath;
- private readonly IIOHelper _ioHelper;
+ private readonly ILogViewerConfig _logViewerConfig;
+ private readonly global::Serilog.ILogger _serilogLog;
- 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 SerilogLogViewerSourceBase(ILogViewerConfig logViewerConfig, global::Serilog.ILogger serilogLog)
+ {
+ _logViewerConfig = logViewerConfig;
+ _serilogLog = serilogLog;
}
public abstract bool CanHandleLargeLogs { get; }
@@ -38,55 +29,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)
{
@@ -101,7 +50,7 @@ namespace Umbraco.Core.Logging.Viewer
///
public string GetLogLevel()
{
- var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast().Where(Log.Logger.IsEnabled)?.Min() ?? null;
+ var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast().Where(_serilogLog.IsEnabled)?.Min() ?? null;
return logLevel?.ToString() ?? "";
}
@@ -182,15 +131,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.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
index cfd67a5a02..dbf36da8f7 100644
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/UmbracoPlan.cs
@@ -64,6 +64,7 @@ namespace Umbraco.Core.Migrations.Upgrade
{
get
{
+
// no state in database yet - assume we have something in web.config that makes some sense
if (!SemVersion.TryParse(_globalSettings.ConfigurationStatus, out var currentVersion))
throw new InvalidOperationException($"Could not get current version from web.config {Constants.AppSettings.ConfigurationStatus} appSetting.");
diff --git a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
index c60aa57e16..38204d9452 100644
--- a/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
+++ b/src/Umbraco.Infrastructure/Runtime/CoreInitialComposer.cs
@@ -30,6 +30,7 @@ using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Examine;
using Umbraco.Infrastructure.Media;
+using Umbraco.Net;
using Umbraco.Web;
using Umbraco.Web.Actions;
using Umbraco.Web.Cache;
diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
index 92e47771a6..ebd91f52a2 100644
--- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
+++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs
@@ -25,6 +25,7 @@ namespace Umbraco.Core.Runtime
private IFactory _factory;
private readonly RuntimeState _state;
private readonly IUmbracoBootPermissionChecker _umbracoBootPermissionChecker;
+ private readonly IRequestCache _requestCache;
private readonly IGlobalSettings _globalSettings;
private readonly IConnectionStrings _connectionStrings;
@@ -39,7 +40,8 @@ namespace Umbraco.Core.Runtime
IBackOfficeInfo backOfficeInfo,
IDbProviderFactoryCreator dbProviderFactoryCreator,
IMainDom mainDom,
- ITypeFinder typeFinder)
+ ITypeFinder typeFinder,
+ IRequestCache requestCache)
{
IOHelper = ioHelper;
Configs = configs;
@@ -50,6 +52,7 @@ namespace Umbraco.Core.Runtime
DbProviderFactoryCreator = dbProviderFactoryCreator;
_umbracoBootPermissionChecker = umbracoBootPermissionChecker;
+ _requestCache = requestCache;
Logger = logger;
MainDom = mainDom;
@@ -110,6 +113,7 @@ namespace Umbraco.Core.Runtime
{
if (register is null) throw new ArgumentNullException(nameof(register));
+
// create and register the essential services
// ie the bare minimum required to boot
@@ -129,12 +133,19 @@ namespace Umbraco.Core.Runtime
"Booted.",
"Boot failed."))
{
- Logger.Info("Booting Core");
+
+ Logger.Info("Booting site '{HostingSiteName}', app '{HostingApplicationId}', path '{HostingPhysicalPath}', server '{MachineName}'.",
+ HostingEnvironment?.SiteName,
+ HostingEnvironment?.ApplicationId,
+ HostingEnvironment?.ApplicationPhysicalPath,
+ NetworkHelper.MachineName);
Logger.Debug("Runtime: {Runtime}", GetType().FullName);
// application environment
ConfigureUnhandledException();
- return _factory = Configure(register, timer);
+ _factory = Configure(register, timer);
+
+ return _factory;
}
}
@@ -151,7 +162,7 @@ namespace Umbraco.Core.Runtime
try
{
-
+
// run handlers
RuntimeOptions.DoRuntimeBoot(ProfilingLogger);
@@ -244,6 +255,13 @@ namespace Umbraco.Core.Runtime
// create & initialize the components
_components = _factory.GetInstance();
_components.Initialize();
+
+
+ // now (and only now) is the time to switch over to perWebRequest scopes.
+ // up until that point we may not have a request, and scoped services would
+ // fail to resolve - but we run Initialize within a factory scope - and then,
+ // here, we switch the factory to bind scopes to requests
+ _factory.EnablePerWebRequestScope();
}
protected virtual void ConfigureUnhandledException()
@@ -350,7 +368,7 @@ namespace Umbraco.Core.Runtime
return new AppCaches(
new DeepCloneAppCache(new ObjectCacheAppCache()),
- NoAppCache.Instance,
+ _requestCache,
new IsolatedCaches(type => new DeepCloneAppCache(new ObjectCacheAppCache())));
}
diff --git a/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs b/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs
deleted file mode 100644
index fc2a019023..0000000000
--- a/src/Umbraco.Infrastructure/Runtime/WebRuntime.cs
+++ /dev/null
@@ -1,90 +0,0 @@
-using Umbraco.Core;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Composing;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Hosting;
-using Umbraco.Core.IO;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Persistence;
-using Umbraco.Core.Runtime;
-
-namespace Umbraco.Web.Runtime
-{
- ///
- /// Represents the Web Umbraco runtime.
- ///
- /// On top of CoreRuntime, handles all of the web-related runtime aspects of Umbraco.
- public class WebRuntime : CoreRuntime
- {
- private readonly IRequestCache _requestCache;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public WebRuntime(
- Configs configs,
- IUmbracoVersion umbracoVersion,
- IIOHelper ioHelper,
- ILogger logger,
- IProfiler profiler,
- IHostingEnvironment hostingEnvironment,
- IBackOfficeInfo backOfficeInfo,
- IDbProviderFactoryCreator dbProviderFactoryCreator,
- IMainDom mainDom,
- ITypeFinder typeFinder,
- IRequestCache requestCache,
- IUmbracoBootPermissionChecker umbracoBootPermissionChecker):
- base(configs, umbracoVersion, ioHelper, logger, profiler ,umbracoBootPermissionChecker, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder)
- {
- _requestCache = requestCache;
- }
-
- ///
- public override IFactory Configure(IRegister register)
- {
-
- var profilingLogger = new ProfilingLogger(Logger, Profiler);
- var umbracoVersion = new UmbracoVersion();
- using (var timer = profilingLogger.TraceDuration(
- $"Booting Umbraco {umbracoVersion.SemanticVersion.ToSemanticString()}.",
- "Booted.",
- "Boot failed."))
- {
- Logger.Info("Booting site '{HostingSiteName}', app '{HostingApplicationId}', path '{HostingPhysicalPath}', server '{MachineName}'.",
- HostingEnvironment.SiteName,
- HostingEnvironment.ApplicationId,
- HostingEnvironment.ApplicationPhysicalPath,
- NetworkHelper.MachineName);
- Logger.Debug("Runtime: {Runtime}", GetType().FullName);
-
- var factory = base.Configure(register);
-
- // now (and only now) is the time to switch over to perWebRequest scopes.
- // up until that point we may not have a request, and scoped services would
- // fail to resolve - but we run Initialize within a factory scope - and then,
- // here, we switch the factory to bind scopes to requests
- factory.EnablePerWebRequestScope();
-
- return factory;
- }
-
-
- }
-
- #region Getters
-
- protected override AppCaches GetAppCaches() => new AppCaches(
- // we need to have the dep clone runtime cache provider to ensure
- // all entities are cached properly (cloned in and cloned out)
- new DeepCloneAppCache(new ObjectCacheAppCache()),
- // we need request based cache when running in web-based context
- _requestCache,
- new IsolatedCaches(type =>
- // we need to have the dep clone runtime cache provider to ensure
- // all entities are cached properly (cloned in and cloned out)
- new DeepCloneAppCache(new ObjectCacheAppCache())));
-
- #endregion
- }
-}
-
diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs
index 42b1e6c0dd..21b1f66395 100644
--- a/src/Umbraco.Tests.Common/TestHelperBase.cs
+++ b/src/Umbraco.Tests.Common/TestHelperBase.cs
@@ -152,5 +152,14 @@ namespace Umbraco.Tests.Common
return mock.Object;
}
+
+ public ILoggingConfiguration GetLoggingConfiguration(IHostingEnvironment hostingEnv = null)
+ {
+ hostingEnv = hostingEnv ?? GetHostingEnvironment();
+ return new LoggingConfiguration(
+ Path.Combine(hostingEnv.ApplicationPhysicalPath, "App_Data\\Logs"),
+ Path.Combine(hostingEnv.ApplicationPhysicalPath, "config\\serilog.config"),
+ Path.Combine(hostingEnv.ApplicationPhysicalPath, "config\\serilog.user.config"));
+ }
}
}
diff --git a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs
index 6430291bc2..9f29b14858 100644
--- a/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs
+++ b/src/Umbraco.Tests.Integration/Implementations/TestHostingEnvironment.cs
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
using Umbraco.Web.Common.AspNetCore;
namespace Umbraco.Tests.Integration.Implementations
diff --git a/src/Umbraco.Tests.Integration/RuntimeTests.cs b/src/Umbraco.Tests.Integration/RuntimeTests.cs
index 52c29d2037..ad040c8ef1 100644
--- a/src/Umbraco.Tests.Integration/RuntimeTests.cs
+++ b/src/Umbraco.Tests.Integration/RuntimeTests.cs
@@ -4,8 +4,10 @@ using Moq;
using NUnit.Framework;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
using Smidge;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Runtime;
@@ -57,7 +59,7 @@ namespace Umbraco.Tests.Integration
var coreRuntime = new CoreRuntime(testHelper.GetConfigs(), testHelper.GetUmbracoVersion(),
testHelper.IOHelper, testHelper.Logger, testHelper.Profiler, testHelper.UmbracoBootPermissionChecker,
testHelper.GetHostingEnvironment(), testHelper.GetBackOfficeInfo(), testHelper.DbProviderFactoryCreator,
- testHelper.MainDom, testHelper.GetTypeFinder());
+ testHelper.MainDom, testHelper.GetTypeFinder(), NoAppCache.Instance);
// boot it!
var factory = coreRuntime.Configure(umbracoContainer);
@@ -99,7 +101,7 @@ namespace Umbraco.Tests.Integration
// Add it!
services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
+ services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
});
var host = await hostBuilder.StartAsync();
@@ -138,7 +140,7 @@ namespace Umbraco.Tests.Integration
// Add it!
services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
+ services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
});
var host = await hostBuilder.StartAsync();
diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
index 101feb79a4..0137850408 100644
--- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
+++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs
@@ -108,7 +108,7 @@ namespace Umbraco.Tests.Integration.Testing
// Add it!
services.AddUmbracoConfiguration(hostContext.Configuration);
- services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, out _);
+ services.AddUmbracoCore(webHostEnvironment, umbracoContainer, GetType().Assembly, NoAppCache.Instance, testHelper.GetLoggingConfiguration(), out _);
});
var host = await hostBuilder.StartAsync();
diff --git a/src/Umbraco.Tests/Logging/LogviewerTests.cs b/src/Umbraco.Tests/Logging/LogviewerTests.cs
index 87cc19a2c6..0a193b4446 100644
--- a/src/Umbraco.Tests/Logging/LogviewerTests.cs
+++ b/src/Umbraco.Tests/Logging/LogviewerTests.cs
@@ -1,9 +1,11 @@
using Moq;
using NUnit.Framework;
+using Serilog;
using System;
using System.IO;
using System.Linq;
using Umbraco.Core;
+using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Tests.TestHelpers;
@@ -33,13 +35,16 @@ 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 loggingConfiguration = TestHelper.GetLoggingConfiguration(hostingEnv);
var exampleLogfilePath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"Logging\", _logfileName);
- _newLogfileDirPath = Path.Combine(TestContext.CurrentContext.TestDirectory, @"App_Data\Logs\");
+ _newLogfileDirPath = loggingConfiguration.LogDirectory;
_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 +56,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 SerilogJsonLogViewer(logger, logViewerConfig, loggingConfiguration, Log.Logger);
}
[OneTimeTearDown]
diff --git a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
index b900453a5e..960d355c0d 100644
--- a/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
+++ b/src/Umbraco.Tests/Routing/RenderRouteHandlerTests.cs
@@ -16,10 +16,10 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Core.Strings;
using Umbraco.Core.Configuration;
-using Umbraco.Core.Dictionary;
using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Core.Runtime;
using Umbraco.Core.Services;
using Umbraco.Tests.PublishedContent;
using Umbraco.Tests.Testing;
@@ -47,10 +47,10 @@ namespace Umbraco.Tests.Routing
HostingEnvironment);
}
- public class TestRuntime : WebRuntime
+ public class TestRuntime : CoreRuntime
{
public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo)
- : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder(), TestHelper.GetRequestCache(), new AspNetUmbracoBootPermissionChecker())
+ : base(configs, umbracoVersion, ioHelper, Mock.Of(), Mock.Of(), new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder(), NoAppCache.Instance)
{
}
diff --git a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
index 488a4f6dad..f63c56b64e 100644
--- a/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
+++ b/src/Umbraco.Tests/Runtimes/CoreRuntimeTests.cs
@@ -5,6 +5,7 @@ using Examine;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
@@ -16,6 +17,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Runtime;
using Umbraco.Core.Scoping;
+using Umbraco.Net;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Web;
@@ -116,7 +118,7 @@ namespace Umbraco.Tests.Runtimes
public class TestRuntime : CoreRuntime
{
public TestRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo)
- :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder())
+ :base(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, TestHelper.GetTypeFinder(), NoAppCache.Instance)
{
}
@@ -134,6 +136,7 @@ namespace Umbraco.Tests.Runtimes
public override IFactory Configure(IRegister container)
{
container.Register(Lifetime.Singleton);
+ container.Register(Lifetime.Singleton);
var factory = base.Configure(container);
return factory;
diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs
index 2a87fc7a43..67dc24e041 100644
--- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs
+++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs
@@ -84,7 +84,7 @@ namespace Umbraco.Tests.Runtimes
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, hostingEnvironment, backOfficeInfo);
// create the core runtime and have it compose itself
- var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder);
+ var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder, NoAppCache.Instance);
// determine actual runtime level
runtimeState.DetermineRuntimeLevel(databaseFactory, logger);
@@ -278,7 +278,7 @@ namespace Umbraco.Tests.Runtimes
composition.RegisterEssentials(logger, profiler, profilingLogger, mainDom, appCaches, databaseFactory, typeLoader, runtimeState, typeFinder, ioHelper, umbracoVersion, TestHelper.DbProviderFactoryCreator, hostingEnvironment, backOfficeInfo);
// create the core runtime and have it compose itself
- var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder);
+ var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetUmbracoBootPermissionChecker(), hostingEnvironment, backOfficeInfo, TestHelper.DbProviderFactoryCreator, TestHelper.MainDom, typeFinder, NoAppCache.Instance);
// get the components
// all of them?
diff --git a/src/Umbraco.Tests/TestHelpers/Stubs/TestProfiler.cs b/src/Umbraco.Tests/TestHelpers/Stubs/TestProfiler.cs
index 39cac6e24f..ea0f9cc44f 100644
--- a/src/Umbraco.Tests/TestHelpers/Stubs/TestProfiler.cs
+++ b/src/Umbraco.Tests/TestHelpers/Stubs/TestProfiler.cs
@@ -19,11 +19,6 @@ namespace Umbraco.Tests.TestHelpers.Stubs
private static bool _enabled;
- public string Render()
- {
- return string.Empty;
- }
-
public IDisposable Step(string name)
{
return _enabled ? MiniProfiler.Current.Step(name) : null;
diff --git a/src/Umbraco.Tests/TestHelpers/TestHelper.cs b/src/Umbraco.Tests/TestHelpers/TestHelper.cs
index 7eca49183d..6fcba1ba1a 100644
--- a/src/Umbraco.Tests/TestHelpers/TestHelper.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestHelper.cs
@@ -324,6 +324,8 @@ namespace Umbraco.Tests.TestHelpers
public static IHostingEnvironment GetHostingEnvironment() => _testHelperInternal.GetHostingEnvironment();
+ public static ILoggingConfiguration GetLoggingConfiguration(IHostingEnvironment hostingEnv) => _testHelperInternal.GetLoggingConfiguration(hostingEnv);
+
public static IApplicationShutdownRegistry GetHostingEnvironmentLifetime() => _testHelperInternal.GetHostingEnvironmentLifetime();
public static IIpResolver GetIpResolver() => _testHelperInternal.GetIpResolver();
diff --git a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
index 901192c609..80f6ab9c9e 100644
--- a/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
+++ b/src/Umbraco.Tests/Testing/UmbracoTestBase.cs
@@ -264,7 +264,7 @@ namespace Umbraco.Tests.Testing
profiler = Mock.Of();
break;
case UmbracoTestOptions.Logger.Serilog:
- logger = new SerilogLogger(TestHelper.CoreDebugSettings, IOHelper, TestHelper.Marchal, new FileInfo(TestHelper.MapPathForTestFiles("~/unit-test.config")));
+ logger = new SerilogLogger(new FileInfo(TestHelper.MapPathForTestFiles("~/unit-test.config")));
profiler = new LogProfiler(logger);
break;
case UmbracoTestOptions.Logger.Console:
diff --git a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs
index a6c544062e..0d55fd99d7 100644
--- a/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs
+++ b/src/Umbraco.Tests/UmbracoExamine/ExamineBaseTest.cs
@@ -18,7 +18,7 @@ namespace Umbraco.Tests.UmbracoExamine
public void InitializeFixture()
{
- var logger = new SerilogLogger(TestHelper.CoreDebugSettings, IOHelper, TestHelper.Marchal, new FileInfo(TestHelper.MapPathForTestFiles("~/unit-test.config")));
+ var logger = new SerilogLogger(new FileInfo(TestHelper.MapPathForTestFiles("~/unit-test.config")));
_profilingLogger = new ProfilingLogger(logger, new LogProfiler(logger));
}
diff --git a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs
index ddf06e6532..a27113e881 100644
--- a/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs
+++ b/src/Umbraco.Web.BackOffice/AspNetCore/UmbracoApplicationBuilderExtensions.cs
@@ -1,9 +1,13 @@
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
+using Serilog.Context;
using Smidge;
using Umbraco.Core;
+using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
+using Umbraco.Infrastructure.Logging.Serilog.Enrichers;
+using Umbraco.Web.Common.Middleware;
namespace Umbraco.Web.BackOffice.AspNetCore
{
@@ -31,6 +35,10 @@ namespace Umbraco.Web.BackOffice.AspNetCore
var runtimeShutdown = new CoreRuntimeShutdown(runtime, hostLifetime);
hostLifetime.RegisterObject(runtimeShutdown);
+ // Register our global threadabort enricher for logging
+ var threadAbortEnricher = app.ApplicationServices.GetRequiredService();
+ LogContext.Push(threadAbortEnricher); // NOTE: We are not in a using clause because we are not removing it, it is on the global context
+
// Start the runtime!
runtime.Start();
@@ -64,6 +72,15 @@ namespace Umbraco.Web.BackOffice.AspNetCore
}
}
+ public static IApplicationBuilder UseUmbracoRequestLogging(this IApplicationBuilder app)
+ {
+ if (app == null) throw new ArgumentNullException(nameof(app));
+
+ app.UseMiddleware();
+
+ return app;
+ }
+
public static IApplicationBuilder UseUmbracoRuntimeMinification(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
diff --git a/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs b/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs
index 0ed0fd658a..65c2be051f 100644
--- a/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs
+++ b/src/Umbraco.Web.BackOffice/Filters/MinifyJavaScriptResultAttribute.cs
@@ -2,9 +2,7 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Umbraco.Core.Hosting;
using Microsoft.Extensions.DependencyInjection;
-using Umbraco.Core.Runtime;
using Umbraco.Core.WebAssets;
-using Umbraco.Web.BackOffice.Controllers;
using Umbraco.Web.Common.ActionResults;
namespace Umbraco.Web.BackOffice.Filters
@@ -14,10 +12,11 @@ namespace Umbraco.Web.BackOffice.Filters
public override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
{
// logic before action goes here
- var hostingEnvironment = context.HttpContext.RequestServices.GetService();
+ var serviceProvider = context.HttpContext.RequestServices;
+ var hostingEnvironment = serviceProvider.GetService();
if (!hostingEnvironment.IsDebugMode)
{
- var runtimeMinifier = context.HttpContext.RequestServices.GetService();
+ var runtimeMinifier = serviceProvider.GetService();
if (context.Result is JavaScriptResult jsResult)
{
diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs
index decfcfa660..b3ec11c241 100644
--- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs
+++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreHostingEnvironment.cs
@@ -5,13 +5,12 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Umbraco.Core;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Logging;
namespace Umbraco.Web.Common.AspNetCore
{
public class AspNetCoreHostingEnvironment : Umbraco.Core.Hosting.IHostingEnvironment
{
-
-
private readonly IHostingSettings _hostingSettings;
private readonly IWebHostEnvironment _webHostEnvironment;
@@ -28,6 +27,7 @@ namespace Umbraco.Web.Common.AspNetCore
ApplicationVirtualPath = "/"; //TODO how to find this, This is a server thing, not application thing.
IISVersion = new Version(0, 0); // TODO not necessary IIS
+
}
public bool IsHosted { get; } = true;
diff --git a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
index 9ba0f06f64..f2a15e1fc7 100644
--- a/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
+++ b/src/Umbraco.Web.Common/AspNetCore/AspNetCoreRequestAccessor.cs
@@ -1,6 +1,8 @@
using System;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Extensions;
using Umbraco.Web.Common.Extensions;
+using Umbraco.Web.Common.Lifetime;
using Umbraco.Web.Routing;
namespace Umbraco.Web.Common.AspNetCore
@@ -8,20 +10,39 @@ namespace Umbraco.Web.Common.AspNetCore
public class AspNetCoreRequestAccessor : IRequestAccessor
{
private readonly IHttpContextAccessor _httpContextAccessor;
- public AspNetCoreRequestAccessor(IHttpContextAccessor httpContextAccessor)
+ private readonly IUmbracoContextAccessor _umbracoContextAccessor;
+
+ public AspNetCoreRequestAccessor(IHttpContextAccessor httpContextAccessor, IUmbracoRequestLifetime umbracoRequestLifetime, IUmbracoContextAccessor umbracoContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
+ _umbracoContextAccessor = umbracoContextAccessor;
+
+ umbracoRequestLifetime.RequestStart += RequestStart;
+ umbracoRequestLifetime.RequestEnd += RequestEnd;
}
+ private void RequestEnd(object sender, HttpContext e)
+ {
+ EndRequest?.Invoke(sender, new UmbracoRequestEventArgs(_umbracoContextAccessor.UmbracoContext));
+ }
+
+ private void RequestStart(object sender, HttpContext e)
+ {
+ var reason = EnsureRoutableOutcome.IsRoutable;
+ RouteAttempt?.Invoke(sender, new RoutableAttemptEventArgs(reason, _umbracoContextAccessor.UmbracoContext));
+ }
+
+
+
public string GetRequestValue(string name) => GetFormValue(name) ?? GetQueryStringValue(name);
public string GetFormValue(string name) => _httpContextAccessor.GetRequiredHttpContext().Request.Form[name];
public string GetQueryStringValue(string name) => _httpContextAccessor.GetRequiredHttpContext().Request.Query[name];
- //TODO implement
public event EventHandler EndRequest;
//TODO implement
public event EventHandler RouteAttempt;
+ public Uri GetRequestUrl() => _httpContextAccessor.HttpContext != null ? new Uri(_httpContextAccessor.HttpContext.Request.GetEncodedUrl()) : null;
}
}
diff --git a/src/Umbraco.Web.Common/Constants/ViewConstants.cs b/src/Umbraco.Web.Common/Constants/ViewConstants.cs
new file mode 100644
index 0000000000..5da1a74f55
--- /dev/null
+++ b/src/Umbraco.Web.Common/Constants/ViewConstants.cs
@@ -0,0 +1,12 @@
+namespace Umbraco.Web.Common.Constants
+{
+ ///
+ /// constants
+ ///
+ internal static class ViewConstants
+ {
+ internal const string ViewLocation = "~/Views";
+
+ internal const string DataTokenCurrentViewContext = "umbraco-current-view-context";
+ }
+}
diff --git a/src/Umbraco.Web.Common/Controllers/RenderController.cs b/src/Umbraco.Web.Common/Controllers/RenderController.cs
new file mode 100644
index 0000000000..43058616de
--- /dev/null
+++ b/src/Umbraco.Web.Common/Controllers/RenderController.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace Umbraco.Web.Common.Controllers
+{
+ public abstract class RenderController : Controller
+ {
+
+ }
+}
diff --git a/src/Umbraco.Web.Common/Events/ActionExecutedEventArgs.cs b/src/Umbraco.Web.Common/Events/ActionExecutedEventArgs.cs
new file mode 100644
index 0000000000..b33cbc7d8a
--- /dev/null
+++ b/src/Umbraco.Web.Common/Events/ActionExecutedEventArgs.cs
@@ -0,0 +1,17 @@
+using System;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Umbraco.Web.Common.Events
+{
+ public class ActionExecutedEventArgs : EventArgs
+ {
+ public Controller Controller { get; set; }
+ public object Model { get; set; }
+
+ public ActionExecutedEventArgs(Controller controller, object model)
+ {
+ Controller = controller;
+ Model = model;
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs
index 843620d571..3facf1b77f 100644
--- a/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs
+++ b/src/Umbraco.Web.Common/Extensions/UmbracoCoreServiceCollectionExtensions.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Reflection;
@@ -6,6 +8,10 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Serilog;
+using Serilog.Extensions.Hosting;
+using Serilog.Extensions.Logging;
using Smidge;
using Smidge.Nuglify;
using Umbraco.Composing;
@@ -71,7 +77,22 @@ namespace Umbraco.Web.Common.Extensions
var umbContainer = UmbracoServiceProviderFactory.UmbracoContainer;
- services.AddUmbracoCore(webHostEnvironment, umbContainer, Assembly.GetEntryAssembly(), out factory);
+ var loggingConfig = new LoggingConfiguration(
+ Path.Combine(webHostEnvironment.ContentRootPath, "App_Data\\Logs"),
+ Path.Combine(webHostEnvironment.ContentRootPath, "config\\serilog.config"),
+ Path.Combine(webHostEnvironment.ContentRootPath, "config\\serilog.user.config"));
+
+ IHttpContextAccessor httpContextAccessor = new HttpContextAccessor();
+ services.AddSingleton(httpContextAccessor);
+
+ var requestCache = new GenericDictionaryRequestAppCache(() => httpContextAccessor.HttpContext.Items);
+
+ services.AddUmbracoCore(webHostEnvironment,
+ umbContainer,
+ Assembly.GetEntryAssembly(),
+ requestCache,
+ loggingConfig,
+ out factory);
return services;
}
@@ -83,20 +104,34 @@ namespace Umbraco.Web.Common.Extensions
///
///
///
+ ///
+ ///
+ ///
///
///
- public static IServiceCollection AddUmbracoCore(this IServiceCollection services, IWebHostEnvironment webHostEnvironment, IRegister umbContainer, Assembly entryAssembly, out IFactory factory)
+ public static IServiceCollection AddUmbracoCore(
+ this IServiceCollection services,
+ IWebHostEnvironment webHostEnvironment,
+ IRegister umbContainer,
+ Assembly entryAssembly,
+ IRequestCache requestCache,
+ ILoggingConfiguration loggingConfiguration,
+ out IFactory factory)
{
if (services is null) throw new ArgumentNullException(nameof(services));
var container = umbContainer;
if (container is null) throw new ArgumentNullException(nameof(container));
if (entryAssembly is null) throw new ArgumentNullException(nameof(entryAssembly));
- // Special case! The generic host adds a few default services but we need to manually add this one here NOW because
- // we resolve it before the host finishes configuring in the call to CreateCompositionRoot
- services.AddSingleton();
+ var serviceProvider = services.BuildServiceProvider();
+ var configs = serviceProvider.GetService();
- CreateCompositionRoot(services, webHostEnvironment, out var logger, out var configs, out var ioHelper, out var hostingEnvironment, out var backOfficeInfo, out var profiler);
+
+ CreateCompositionRoot(services,
+ configs,
+ webHostEnvironment,
+ loggingConfiguration,
+ out var logger, out var ioHelper, out var hostingEnvironment, out var backOfficeInfo, out var profiler);
var globalSettings = configs.Global();
var umbracoVersion = new UmbracoVersion(globalSettings);
@@ -109,14 +144,15 @@ namespace Umbraco.Web.Common.Extensions
profiler,
hostingEnvironment,
backOfficeInfo,
- CreateTypeFinder(logger, profiler, webHostEnvironment, entryAssembly));
+ CreateTypeFinder(logger, profiler, webHostEnvironment, entryAssembly),
+ requestCache);
factory = coreRuntime.Configure(container);
return services;
}
- private static ITypeFinder CreateTypeFinder(ILogger logger, IProfiler profiler, IWebHostEnvironment webHostEnvironment, Assembly entryAssembly)
+ private static ITypeFinder CreateTypeFinder(Core.Logging.ILogger logger, IProfiler profiler, IWebHostEnvironment webHostEnvironment, Assembly entryAssembly)
{
// TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however
// this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now.
@@ -126,11 +162,12 @@ namespace Umbraco.Web.Common.Extensions
return new TypeFinder(logger, new DefaultUmbracoAssemblyProvider(entryAssembly), runtimeHash);
}
- private static IRuntime GetCoreRuntime(Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, ILogger logger,
+ private static IRuntime GetCoreRuntime(
+ Configs configs, IUmbracoVersion umbracoVersion, IIOHelper ioHelper, Core.Logging.ILogger logger,
IProfiler profiler, Core.Hosting.IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo,
- ITypeFinder typeFinder)
+ ITypeFinder typeFinder, IRequestCache requestCache)
{
- var connectionStringConfig = configs.ConnectionStrings()[Constants.System.UmbracoConnectionName];
+ var connectionStringConfig = configs.ConnectionStrings()[Core.Constants.System.UmbracoConnectionName];
var dbProviderFactoryCreator = new SqlServerDbProviderFactoryCreator(
connectionStringConfig?.ProviderName,
DbProviderFactories.GetFactory);
@@ -145,53 +182,92 @@ namespace Umbraco.Web.Common.Extensions
var mainDom = new MainDom(logger, mainDomLock);
- var coreRuntime = new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, new AspNetCoreBootPermissionsChecker(),
- hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom, typeFinder);
+ var coreRuntime = new CoreRuntime(
+ configs,
+ umbracoVersion,
+ ioHelper,
+ logger,
+ profiler,
+ new AspNetCoreBootPermissionsChecker(),
+ hostingEnvironment,
+ backOfficeInfo,
+ dbProviderFactoryCreator,
+ mainDom,
+ typeFinder,
+ requestCache);
return coreRuntime;
}
- private static IServiceCollection CreateCompositionRoot(IServiceCollection services, IWebHostEnvironment webHostEnvironment,
- out ILogger logger, out Configs configs, out IIOHelper ioHelper, out Core.Hosting.IHostingEnvironment hostingEnvironment,
- out IBackOfficeInfo backOfficeInfo, out IProfiler profiler)
+ private static IServiceCollection CreateCompositionRoot(
+ IServiceCollection services,
+ Configs configs,
+ IWebHostEnvironment webHostEnvironment,
+ ILoggingConfiguration loggingConfiguration,
+ out Core.Logging.ILogger logger,
+ out IIOHelper ioHelper,
+ out Core.Hosting.IHostingEnvironment hostingEnvironment,
+ out IBackOfficeInfo backOfficeInfo,
+ out IProfiler profiler)
{
- // TODO: We need to avoid this, surely there's a way? See ContainerTests.BuildServiceProvider_Before_Host_Is_Configured
- var serviceProvider = services.BuildServiceProvider();
-
- var httpContextAccessor = serviceProvider.GetRequiredService();
-
- configs = serviceProvider.GetService();
if (configs == null)
throw new InvalidOperationException($"Could not resolve type {typeof(Configs)} from the container, ensure {nameof(AddUmbracoConfiguration)} is called before calling {nameof(AddUmbracoCore)}");
var hostingSettings = configs.Hosting();
- var coreDebug = configs.CoreDebug();
var globalSettings = configs.Global();
hostingEnvironment = new AspNetCoreHostingEnvironment(hostingSettings, webHostEnvironment);
ioHelper = new IOHelper(hostingEnvironment, globalSettings);
- logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment,
- new AspNetCoreSessionManager(httpContextAccessor),
- // TODO: We need to avoid this, surely there's a way? See ContainerTests.BuildServiceProvider_Before_Host_Is_Configured
- () => services.BuildServiceProvider().GetService(), coreDebug, ioHelper,
- new AspNetCoreMarchal());
+ logger = AddLogger(services, hostingEnvironment, loggingConfiguration);
backOfficeInfo = new AspNetCoreBackOfficeInfo(globalSettings);
- profiler = GetWebProfiler(hostingEnvironment, httpContextAccessor);
+ profiler = GetWebProfiler(hostingEnvironment);
return services;
}
+ ///
+ /// Create and configure the logger
+ ///
+ ///
+ private static Core.Logging.ILogger AddLogger(IServiceCollection services, Core.Hosting.IHostingEnvironment hostingEnvironment, ILoggingConfiguration loggingConfiguration)
+ {
+ // Create a serilog logger
+ var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration);
+
+ // Wire up all the bits that serilog needs. We need to use our own code since the Serilog ext methods don't cater to our needs since
+ // we don't want to use the global serilog `Log` object and we don't have our own ILogger implementation before the HostBuilder runs which
+ // is the only other option that these ext methods allow.
+ // I have created a PR to make this nicer https://github.com/serilog/serilog-extensions-hosting/pull/19 but we'll need to wait for that.
+ // Also see : https://github.com/serilog/serilog-extensions-hosting/blob/dev/src/Serilog.Extensions.Hosting/SerilogHostBuilderExtensions.cs
+
+ services.AddSingleton(services => new SerilogLoggerFactory(logger.SerilogLog, false));
+
+ // This won't (and shouldn't) take ownership of the logger.
+ services.AddSingleton(logger.SerilogLog);
+
+ // Registered to provide two services...
+ var diagnosticContext = new DiagnosticContext(logger.SerilogLog);
+
+ // Consumed by e.g. middleware
+ services.AddSingleton(diagnosticContext);
+
+ // Consumed by user code
+ services.AddSingleton(diagnosticContext);
+
+ return logger;
+ }
+
public static IServiceCollection AddUmbracoRuntimeMinifier(this IServiceCollection services,
IConfiguration configuration)
{
- services.AddSmidge(configuration.GetSection(Constants.Configuration.ConfigRuntimeMinification));
+ services.AddSmidge(configuration.GetSection(Core.Constants.Configuration.ConfigRuntimeMinification));
services.AddSmidgeNuglify();
return services;
}
- private static IProfiler GetWebProfiler(Umbraco.Core.Hosting.IHostingEnvironment hostingEnvironment, IHttpContextAccessor httpContextAccessor)
+ private static IProfiler GetWebProfiler(Umbraco.Core.Hosting.IHostingEnvironment hostingEnvironment)
{
// create and start asap to profile boot
if (!hostingEnvironment.IsDebugMode)
@@ -201,7 +277,7 @@ namespace Umbraco.Web.Common.Extensions
return new VoidProfiler();
}
- var webProfiler = new WebProfiler(httpContextAccessor);
+ var webProfiler = new WebProfiler();
webProfiler.StartBoot();
return webProfiler;
@@ -216,4 +292,5 @@ namespace Umbraco.Web.Common.Extensions
}
+
}
diff --git a/src/Umbraco.Web.Common/Filters/DisableBrowserCacheAttribute.cs b/src/Umbraco.Web.Common/Filters/DisableBrowserCacheAttribute.cs
new file mode 100644
index 0000000000..0fe251bac4
--- /dev/null
+++ b/src/Umbraco.Web.Common/Filters/DisableBrowserCacheAttribute.cs
@@ -0,0 +1,35 @@
+using System;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Net.Http.Headers;
+
+namespace Umbraco.Web.Common.Filters
+{
+ ///
+ /// Ensures that the request is not cached by the browser
+ ///
+ public class DisableBrowserCacheAttribute : ActionFilterAttribute
+ {
+ public override void OnResultExecuting(ResultExecutingContext context)
+ {
+ base.OnResultExecuting(context);
+
+ var httpResponse = context.HttpContext.Response;
+
+ if (httpResponse.StatusCode != 200) return;
+
+ httpResponse.GetTypedHeaders().CacheControl =
+ new CacheControlHeaderValue()
+ {
+ NoCache = true,
+ MaxAge = TimeSpan.Zero,
+ MustRevalidate = true,
+ NoStore = true
+ };
+
+ httpResponse.Headers[HeaderNames.LastModified] = DateTime.Now.ToString("R"); // Format RFC1123
+ httpResponse.Headers[HeaderNames.Pragma] = "no-cache";
+ httpResponse.Headers[HeaderNames.Expires] = new DateTime(1990, 1, 1, 0, 0, 0).ToString("R");
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Filters/EnsurePartialViewMacroViewContextFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/EnsurePartialViewMacroViewContextFilterAttribute.cs
new file mode 100644
index 0000000000..269e437d0d
--- /dev/null
+++ b/src/Umbraco.Web.Common/Filters/EnsurePartialViewMacroViewContextFilterAttribute.cs
@@ -0,0 +1,88 @@
+using System.IO;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.AspNetCore.Mvc.Rendering;
+using Microsoft.AspNetCore.Mvc.ViewEngines;
+using Microsoft.AspNetCore.Mvc.ViewFeatures;
+using Umbraco.Web.Common.Constants;
+using Umbraco.Web.Common.Controllers;
+
+namespace Umbraco.Web.Common.Filters
+{
+ ///
+ /// This is a special filter which is required for the RTE to be able to render Partial View Macros that
+ /// contain forms when the RTE value is resolved outside of an MVC view being rendered
+ ///
+ ///
+ /// The entire way that we support partial view macros that contain forms isn't really great, these forms
+ /// need to be executed as ChildActions so that the ModelState,ViewData,TempData get merged into that action
+ /// so the form can show errors, viewdata, etc...
+ /// Under normal circumstances, macros will be rendered after a ViewContext is created but in some cases
+ /// developers will resolve the RTE value in the controller, in this case the Form won't be rendered correctly
+ /// with merged ModelState from the controller because the special DataToken hasn't been set yet (which is
+ /// normally done in the UmbracoViewPageOfModel when a real ViewContext is available.
+ /// So we need to detect if the currently rendering controller is IRenderController and if so we'll ensure that
+ /// this DataToken exists before the action executes in case the developer resolves an RTE value that contains
+ /// a partial view macro form.
+ ///
+ public class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute
+ {
+
+ ///
+ /// Ensures the custom ViewContext datatoken is set before the RenderController action is invoked,
+ /// this ensures that any calls to GetPropertyValue with regards to RTE or Grid editors can still
+ /// render any PartialViewMacro with a form and maintain ModelState
+ ///
+ ///
+ public override void OnActionExecuting(ActionExecutingContext context)
+ {
+ if (!(context.Controller is Controller controller)) return;
+
+ //ignore anything that is not IRenderController
+ if (!(controller is RenderController)) return;
+
+ SetViewContext(context, controller);
+ }
+
+ ///
+ /// Ensures that the custom ViewContext datatoken is set after the RenderController action is invoked,
+ /// this ensures that any custom ModelState that may have been added in the RenderController itself is
+ /// passed onwards in case it is required when rendering a PartialViewMacro with a form
+ ///
+ /// The filter context.
+ public override void OnResultExecuting(ResultExecutingContext context)
+ {
+ if (!(context.Controller is Controller controller)) return;
+
+ //ignore anything that is not IRenderController
+ if (!(controller is RenderController)) return;
+
+ SetViewContext(context, controller);
+ }
+
+ private void SetViewContext(ActionContext context, Controller controller)
+ {
+ var viewCtx = new ViewContext(
+ context,
+ new DummyView(),
+ controller.ViewData,
+ controller.TempData,
+ new StringWriter(),
+ new HtmlHelperOptions());
+
+ //set the special data token
+ context.RouteData.DataTokens[ViewConstants.DataTokenCurrentViewContext] = viewCtx;
+ }
+
+ private class DummyView : IView
+ {
+ public Task RenderAsync(ViewContext context)
+ {
+ return Task.CompletedTask;
+ }
+
+ public string Path { get; }
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Filters/PreRenderViewActionFilterAttribute.cs b/src/Umbraco.Web.Common/Filters/PreRenderViewActionFilterAttribute.cs
new file mode 100644
index 0000000000..2ba58a8fd7
--- /dev/null
+++ b/src/Umbraco.Web.Common/Filters/PreRenderViewActionFilterAttribute.cs
@@ -0,0 +1,43 @@
+using System;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Umbraco.Web.Common.Events;
+
+namespace Umbraco.Web.Common.Filters
+{
+ public class PreRenderViewActionFilterAttribute : ActionFilterAttribute
+ {
+ public override void OnActionExecuted(ActionExecutedContext context)
+ {
+ if (!(context.Controller is Controller umbController) || !(context.Result is ViewResult result))
+ {
+ return;
+ }
+
+ var model = result.Model;
+ if (model == null)
+ {
+ return;
+ }
+
+ var args = new ActionExecutedEventArgs(umbController, model);
+ OnActionExecuted(args);
+
+ if (args.Model != model)
+ {
+ result.ViewData.Model = args.Model;
+ }
+
+ base.OnActionExecuted(context);
+ }
+
+
+ public static event EventHandler ActionExecuted;
+
+ private static void OnActionExecuted(ActionExecutedEventArgs e)
+ {
+ var handler = ActionExecuted;
+ handler?.Invoke(null, e);
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Filters/StatusCodeResultAttribute.cs b/src/Umbraco.Web.Common/Filters/StatusCodeResultAttribute.cs
new file mode 100644
index 0000000000..8f3fcf3a95
--- /dev/null
+++ b/src/Umbraco.Web.Common/Filters/StatusCodeResultAttribute.cs
@@ -0,0 +1,39 @@
+using System.Net;
+using Microsoft.AspNetCore.Diagnostics;
+using Microsoft.AspNetCore.Mvc.Filters;
+using Microsoft.Extensions.DependencyInjection;
+using Umbraco.Core.Configuration.UmbracoSettings;
+
+namespace Umbraco.Web.Common.Filters
+{
+ ///
+ /// Forces the response to have a specific http status code
+ ///
+ public class StatusCodeResultAttribute : ActionFilterAttribute
+ {
+ private readonly HttpStatusCode _statusCode;
+
+ public StatusCodeResultAttribute(HttpStatusCode statusCode)
+ {
+ _statusCode = statusCode;
+ }
+
+ public override void OnActionExecuted(ActionExecutedContext context)
+ {
+ base.OnActionExecuted(context);
+
+ var httpContext = context.HttpContext;
+
+ httpContext.Response.StatusCode = (int)_statusCode;
+
+ var disableIisCustomErrors = httpContext.RequestServices.GetService().TrySkipIisCustomErrors;
+ var statusCodePagesFeature = httpContext.Features.Get();
+
+ if (statusCodePagesFeature != null)
+ {
+ // if IIS Custom Errors are disabled, we won't enable the Status Code Pages
+ statusCodePagesFeature.Enabled = !disableIisCustomErrors;
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestLoggingMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestLoggingMiddleware.cs
new file mode 100644
index 0000000000..0e2158c939
--- /dev/null
+++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestLoggingMiddleware.cs
@@ -0,0 +1,41 @@
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Http;
+using Serilog.Context;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Logging.Serilog.Enrichers;
+using Umbraco.Net;
+
+namespace Umbraco.Web.Common.Middleware
+{
+ public class UmbracoRequestLoggingMiddleware
+ {
+ readonly RequestDelegate _next;
+ private readonly HttpSessionIdEnricher _sessionIdEnricher;
+ private readonly HttpRequestNumberEnricher _requestNumberEnricher;
+ private readonly HttpRequestIdEnricher _requestIdEnricher;
+
+ public UmbracoRequestLoggingMiddleware(RequestDelegate next,
+ HttpSessionIdEnricher sessionIdEnricher,
+ HttpRequestNumberEnricher requestNumberEnricher,
+ HttpRequestIdEnricher requestIdEnricher)
+ {
+ _next = next;
+ _sessionIdEnricher = sessionIdEnricher;
+ _requestNumberEnricher = requestNumberEnricher;
+ _requestIdEnricher = requestIdEnricher;
+ }
+
+ public async Task Invoke(HttpContext httpContext)
+ {
+ // TODO: Need to decide if we want this stuff still, there's new request logging in serilog:
+ // https://github.com/serilog/serilog-aspnetcore#request-logging which i think would suffice and replace all of this?
+
+ using (LogContext.Push(_sessionIdEnricher))
+ using (LogContext.Push(_requestNumberEnricher))
+ using (LogContext.Push(_requestIdEnricher))
+ {
+ await _next(httpContext);
+ }
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs
index 93461fc1d5..e8695f3c9c 100644
--- a/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs
+++ b/src/Umbraco.Web.Common/Middleware/UmbracoRequestMiddleware.cs
@@ -22,5 +22,4 @@ namespace Umbraco.Web.Common.Middleware
_umbracoRequestLifetimeManager.EndRequest(context);
}
}
-
}
diff --git a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
index f194826626..332a405580 100644
--- a/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
+++ b/src/Umbraco.Web.Common/Runtime/AspNetCoreComposer.cs
@@ -11,6 +11,9 @@ using Umbraco.Web.Common.Lifetime;
using Umbraco.Web.Common.Macros;
using Umbraco.Web.Composing.CompositionExtensions;
using Umbraco.Web.Macros;
+using Umbraco.Core.Diagnostics;
+using Umbraco.Web.Common.Runtime.Profiler;
+using Umbraco.Core.Logging;
namespace Umbraco.Web.Common.Runtime
{
@@ -47,6 +50,10 @@ namespace Umbraco.Web.Common.Runtime
composition.RegisterMultipleUnique();
+ composition.RegisterUnique();
+
+ composition.RegisterUnique();
+
composition.RegisterUnique();
composition.RegisterUnique();
diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs
index bdbc6f164d..958e134bab 100644
--- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs
+++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfiler.cs
@@ -1,61 +1,21 @@
using System;
-using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using StackExchange.Profiling;
-using StackExchange.Profiling.Internal;
using Umbraco.Core;
using Umbraco.Core.Logging;
+// TODO: This namespace is strange, not sure why i has "Runtime" in the name?
namespace Umbraco.Web.Common.Runtime.Profiler
{
+
public class WebProfiler : IProfiler
{
private MiniProfiler _startupProfiler;
-
- private readonly IHttpContextAccessor _httpContextAccessor;
private int _first;
- public WebProfiler(IHttpContextAccessor httpContextAccessor)
- {
- // create our own provider, which can provide a profiler even during boot
- _httpContextAccessor = httpContextAccessor;
- }
-
- ///
- ///
- ///
- ///
- /// Normally we would call MiniProfiler.Current.RenderIncludes(...), but because the requeststate is not set, this method does not work.
- /// We fake the requestIds from the RequestState here.
- ///
- public string Render()
- {
-
- var profiler = MiniProfiler.Current;
- if (profiler == null) return string.Empty;
-
- var context = _httpContextAccessor.HttpContext;
-
- var path = (profiler.Options as MiniProfilerOptions)?.RouteBasePath.Value.EnsureTrailingSlash();
-
- var result = StackExchange.Profiling.Internal.Render.Includes(
- profiler,
- path: context.Request.PathBase + path,
- isAuthorized: true,
- requestIDs: new List{ profiler.Id },
- position: RenderPosition.Right,
- showTrivial: profiler.Options.PopupShowTrivial,
- showTimeWithChildren: profiler.Options.PopupShowTimeWithChildren,
- maxTracesToShow: profiler.Options.PopupMaxTracesToShow,
- showControls:profiler.Options.ShowControls,
- startHidden: profiler.Options.PopupStartHidden);
-
- return result;
- }
-
public IDisposable Step(string name)
{
return MiniProfiler.Current?.Step(name);
diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs
index 688a3e5c28..523faf2da5 100644
--- a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs
+++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerComposer.cs
@@ -1,8 +1,15 @@
-using Umbraco.Core.Composing;
+using Umbraco.Core;
+using Umbraco.Core.Composing;
namespace Umbraco.Web.Common.Runtime.Profiler
{
internal class WebProfilerComposer : ComponentComposer, ICoreComposer
{
+ public override void Compose(Composition composition)
+ {
+ base.Compose(composition);
+
+ composition.RegisterUnique();
+ }
}
}
diff --git a/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerHtml.cs b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerHtml.cs
new file mode 100644
index 0000000000..9e989d6b5c
--- /dev/null
+++ b/src/Umbraco.Web.Common/Runtime/Profiler/WebProfilerHtml.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Http;
+using StackExchange.Profiling;
+using StackExchange.Profiling.Internal;
+using Umbraco.Core.Logging;
+
+// TODO: This namespace is strange, not sure why i has "Runtime" in the name?
+namespace Umbraco.Web.Common.Runtime.Profiler
+{
+ public class WebProfilerHtml : IProfilerHtml
+ {
+ private readonly IHttpContextAccessor _httpContextAccessor;
+
+ public WebProfilerHtml(IHttpContextAccessor httpContextAccessor)
+ {
+ // create our own provider, which can provide a profiler even during boot
+ _httpContextAccessor = httpContextAccessor;
+ }
+
+ ///
+ ///
+ /// Normally we would call MiniProfiler.Current.RenderIncludes(...), but because the requeststate is not set, this method does not work.
+ /// We fake the requestIds from the RequestState here.
+ ///
+ public string Render()
+ {
+
+ var profiler = MiniProfiler.Current;
+ if (profiler == null) return string.Empty;
+
+ var context = _httpContextAccessor.HttpContext;
+
+ var path = (profiler.Options as MiniProfilerOptions)?.RouteBasePath.Value.EnsureTrailingSlash();
+
+ var result = StackExchange.Profiling.Internal.Render.Includes(
+ profiler,
+ path: context.Request.PathBase + path,
+ isAuthorized: true,
+ requestIDs: new List { profiler.Id },
+ position: RenderPosition.Right,
+ showTrivial: profiler.Options.PopupShowTrivial,
+ showTimeWithChildren: profiler.Options.PopupShowTimeWithChildren,
+ maxTracesToShow: profiler.Options.PopupMaxTracesToShow,
+ showControls: profiler.Options.ShowControls,
+ startHidden: profiler.Options.PopupStartHidden);
+
+ return result;
+ }
+ }
+}
diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs
index ba3c6c289f..c4b9522099 100644
--- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs
+++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs
@@ -112,7 +112,7 @@ namespace Umbraco.Web.Common.RuntimeMinification
public void Reset()
{
var version = DateTime.UtcNow.Ticks.ToString();
- _configManipulator.SaveConfigValue(Constants.Configuration.ConfigRuntimeMinificationVersion, version.ToString());
+ _configManipulator.SaveConfigValue(Core.Constants.Configuration.ConfigRuntimeMinificationVersion, version.ToString());
}
diff --git a/src/Umbraco.Web.Common/Security/WebSecurity.cs b/src/Umbraco.Web.Common/Security/WebSecurity.cs
new file mode 100644
index 0000000000..7e0d5ac7f7
--- /dev/null
+++ b/src/Umbraco.Web.Common/Security/WebSecurity.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Security;
+using Umbraco.Core;
+using Umbraco.Core.Services;
+using Umbraco.Core.Models.Membership;
+using Microsoft.AspNetCore.Http;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Hosting;
+using Umbraco.Core.Models;
+using Umbraco.Web.Common.Extensions;
+using Umbraco.Web.Security;
+
+namespace Umbraco.Web.Common.Security
+{
+
+ ///
+ /// A utility class used for dealing with USER security in Umbraco
+ ///
+ public class WebSecurity : IWebSecurity
+ {
+ private IUser _currentUser;
+
+
+ public IUser CurrentUser
+ {
+ get => _currentUser;
+ set => _currentUser = value;
+ }
+
+ public double PerformLogin(int userId)
+ {
+ return 15;
+ }
+
+ public void ClearCurrentLogin()
+ {
+
+ }
+
+ public Attempt GetUserId()
+ {
+ return new Attempt();
+ }
+
+ public bool ValidateCurrentUser()
+ {
+ return false;
+ }
+
+ public ValidateRequestAttempt ValidateCurrentUser(bool throwExceptions, bool requiresApproval = true) => throw new NotImplementedException();
+
+ public ValidateRequestAttempt AuthorizeRequest(bool throwExceptions = false) => throw new NotImplementedException();
+
+ public bool UserHasSectionAccess(string section, IUser user) => false;
+
+ public bool IsAuthenticated() => false;
+ }
+}
diff --git a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
index d62cd838a2..eed88bc491 100644
--- a/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
+++ b/src/Umbraco.Web.Common/Umbraco.Web.Common.csproj
@@ -20,6 +20,7 @@
+
diff --git a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
index dd7ce36cb5..9c86c85b5c 100644
--- a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
+++ b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContext.cs
@@ -1,13 +1,9 @@
using System;
-using System.Web;
-using Microsoft.AspNetCore.Http;
-using Microsoft.AspNetCore.Http.Extensions;
using Umbraco.Composing;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Models.PublishedContent;
-using Umbraco.Web.Composing;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
@@ -19,7 +15,6 @@ namespace Umbraco.Web
///
public class UmbracoContext : DisposableObjectSlim, IDisposeOnRequestEnd, IUmbracoContext
{
- private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IGlobalSettings _globalSettings;
private readonly IHostingEnvironment _hostingEnvironment;
private readonly ICookieManager _cookieManager;
@@ -32,7 +27,7 @@ namespace Umbraco.Web
// internal for unit tests
// otherwise it's used by EnsureContext above
// warn: does *not* manage setting any IUmbracoContextAccessor
- internal UmbracoContext(IHttpContextAccessor httpContextAccessor,
+ internal UmbracoContext(
IPublishedSnapshotService publishedSnapshotService,
IWebSecurity webSecurity,
IGlobalSettings globalSettings,
@@ -42,26 +37,14 @@ namespace Umbraco.Web
ICookieManager cookieManager,
IRequestAccessor requestAccessor)
{
- if (httpContextAccessor == null) throw new ArgumentNullException(nameof(httpContextAccessor));
if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService));
if (webSecurity == null) throw new ArgumentNullException(nameof(webSecurity));
VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
- _httpContextAccessor = httpContextAccessor;
_globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings));
_hostingEnvironment = hostingEnvironment;
_cookieManager = cookieManager;
_requestAccessor = requestAccessor;
- // ensure that this instance is disposed when the request terminates, though we *also* ensure
- // this happens in the Umbraco module since the UmbracoCOntext is added to the HttpContext items.
- //
- // also, it *can* be returned by the container with a PerRequest lifetime, meaning that the
- // container *could* also try to dispose it.
- //
- // all in all, this context may be disposed more than once, but DisposableObject ensures that
- // it is ok and it will be actually disposed only once.
- httpContextAccessor.HttpContext?.DisposeOnPipelineCompleted(this);
-
ObjectCreated = DateTime.Now;
UmbracoRequestId = Guid.NewGuid();
Security = webSecurity;
@@ -76,7 +59,7 @@ namespace Umbraco.Web
// the current domain during application startup.
// see: http://issues.umbraco.org/issue/U4-1890
//
- OriginalRequestUrl = new Uri(GetRequestFromContext()?.GetDisplayUrl() ?? "http://localhost");
+ OriginalRequestUrl = _requestAccessor.GetRequestUrl() ?? new Uri("http://localhost");
CleanedUmbracoUrl = uriUtility.UriToUmbraco(OriginalRequestUrl);
}
@@ -183,9 +166,9 @@ namespace Umbraco.Web
private void DetectPreviewMode()
{
- var request = GetRequestFromContext();
- if (request?.GetDisplayUrl() != null
- && new Uri(request.GetEncodedUrl()).IsBackOfficeRequest(_globalSettings, _hostingEnvironment) == false
+ var requestUrl = _requestAccessor.GetRequestUrl();
+ if (requestUrl != null
+ && requestUrl.IsBackOfficeRequest(_globalSettings, _hostingEnvironment) == false
&& Security.CurrentUser != null)
{
var previewToken = _cookieManager.GetCookieValue(Constants.Web.PreviewCookieName); // may be null or empty
@@ -204,18 +187,6 @@ namespace Umbraco.Web
return PublishedSnapshot.ForcedPreview(preview, orig => InPreviewMode = orig);
}
- private HttpRequest GetRequestFromContext()
- {
- try
- {
- return _httpContextAccessor.HttpContext?.Request;
- }
- catch (Exception)
- {
- return null;
- }
- }
-
protected override void DisposeResources()
{
// DisposableObject ensures that this runs only once
diff --git a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContextFactory.cs b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContextFactory.cs
index af1481e185..70be6161f3 100644
--- a/src/Umbraco.Web.Common/UmbracoContext/UmbracoContextFactory.cs
+++ b/src/Umbraco.Web.Common/UmbracoContext/UmbracoContextFactory.cs
@@ -6,6 +6,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Hosting;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
+using Umbraco.Web.Common.Security;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Security;
@@ -28,6 +29,7 @@ namespace Umbraco.Web
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ICookieManager _cookieManager;
+ private readonly IRequestAccessor _requestAccessor;
private readonly UriUtility _uriUtility;
///
@@ -43,7 +45,8 @@ namespace Umbraco.Web
IHostingEnvironment hostingEnvironment,
UriUtility uriUtility,
IHttpContextAccessor httpContextAccessor,
- ICookieManager cookieManager)
+ ICookieManager cookieManager,
+ IRequestAccessor requestAccessor)
{
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
_publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService));
@@ -55,6 +58,7 @@ namespace Umbraco.Web
_uriUtility = uriUtility;
_httpContextAccessor = httpContextAccessor;
_cookieManager = cookieManager;
+ _requestAccessor = requestAccessor;
}
private IUmbracoContext CreateUmbracoContext()
@@ -70,9 +74,17 @@ namespace Umbraco.Web
_variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture);
}
- var webSecurity = new WebSecurity(_httpContextAccessor, _userService, _globalSettings, _hostingEnvironment);
+ var webSecurity = new WebSecurity();
- return new UmbracoContext(_httpContextAccessor, _publishedSnapshotService, webSecurity, _globalSettings, _hostingEnvironment, _variationContextAccessor, _uriUtility, _cookieManager);
+ return new UmbracoContext(
+ _publishedSnapshotService,
+ webSecurity,
+ _globalSettings,
+ _hostingEnvironment,
+ _variationContextAccessor,
+ _uriUtility,
+ _cookieManager,
+ _requestAccessor);
}
///
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/Config/serilog.Release.config b/src/Umbraco.Web.UI.NetCore/Config/serilog.Release.config
new file mode 100644
index 0000000000..9aca408b36
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Config/serilog.Release.config
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI.NetCore/Config/serilog.config b/src/Umbraco.Web.UI.NetCore/Config/serilog.config
new file mode 100644
index 0000000000..9aca408b36
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Config/serilog.config
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI.NetCore/Config/serilog.user.Release.config b/src/Umbraco.Web.UI.NetCore/Config/serilog.user.Release.config
new file mode 100644
index 0000000000..8f207406e3
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Config/serilog.user.Release.config
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI.NetCore/Config/serilog.user.config b/src/Umbraco.Web.UI.NetCore/Config/serilog.user.config
new file mode 100644
index 0000000000..8f207406e3
--- /dev/null
+++ b/src/Umbraco.Web.UI.NetCore/Config/serilog.user.config
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI.NetCore/Program.cs b/src/Umbraco.Web.UI.NetCore/Program.cs
index f0504d77e3..1151f16be8 100644
--- a/src/Umbraco.Web.UI.NetCore/Program.cs
+++ b/src/Umbraco.Web.UI.NetCore/Program.cs
@@ -1,7 +1,5 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-using Serilog;
using Umbraco.Core.Composing;
namespace Umbraco.Web.UI.BackOffice
diff --git a/src/Umbraco.Web.UI.NetCore/Startup.cs b/src/Umbraco.Web.UI.NetCore/Startup.cs
index 468151fc9d..8305ed5a30 100644
--- a/src/Umbraco.Web.UI.NetCore/Startup.cs
+++ b/src/Umbraco.Web.UI.NetCore/Startup.cs
@@ -72,6 +72,7 @@ namespace Umbraco.Web.UI.BackOffice
});
//Finally initialize Current
+ // TODO: This should be moved to the UmbracoServiceProviderFactory when the container is cross-wired and then don't use the overload above to `out var factory`
Current.Initialize(
factory.GetInstance (),
factory.GetInstance(),
@@ -93,7 +94,9 @@ namespace Umbraco.Web.UI.BackOffice
{
app.UseDeveloperExceptionPage();
}
+ app.UseStatusCodePages();
app.UseUmbracoCore();
+ app.UseUmbracoRequestLogging();
app.UseUmbracoWebsite();
app.UseUmbracoBackOffice();
app.UseRouting();
@@ -117,7 +120,8 @@ namespace Umbraco.Web.UI.BackOffice
endpoints.MapGet("/", async context =>
{
- await context.Response.WriteAsync($"Hello World!{Current.Profiler.Render()}");
+ var profilerHtml = app.ApplicationServices.GetRequiredService();
+ await context.Response.WriteAsync($"Hello World!{profilerHtml.Render()}");
});
});
}
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 4b925e81c4..8e1e656a6f 100644
--- a/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj
+++ b/src/Umbraco.Web.UI.NetCore/Umbraco.Web.UI.NetCore.csproj
@@ -50,4 +50,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+ serilog.config
+
+
+ Designer
+ serilog.user.config
+
+
+
+
+
+ Designer
+
+
+ Designer
+
+
+
diff --git a/src/Umbraco.Web.UI/config/serilog.Release.config b/src/Umbraco.Web.UI/config/serilog.Release.config
index e3cf52b3c5..9aca408b36 100644
--- a/src/Umbraco.Web.UI/config/serilog.Release.config
+++ b/src/Umbraco.Web.UI/config/serilog.Release.config
@@ -19,7 +19,7 @@
-
+
@@ -27,7 +27,7 @@
-
+
diff --git a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs
index a82c5cab63..66a21af078 100644
--- a/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs
+++ b/src/Umbraco.Web/AspNet/AspNetRequestAccessor.cs
@@ -38,5 +38,6 @@ namespace Umbraco.Web.AspNet
}
public event EventHandler EndRequest;
public event EventHandler RouteAttempt;
+ public Uri GetRequestUrl() => _httpContextAccessor.HttpContext?.Request.Url;
}
}
diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs
index f9f056ff32..1ed217cc78 100644
--- a/src/Umbraco.Web/Composing/Current.cs
+++ b/src/Umbraco.Web/Composing/Current.cs
@@ -248,6 +248,8 @@ namespace Umbraco.Web.Composing
public static IProfiler Profiler => Factory.GetInstance();
+ public static IProfilerHtml ProfilerHtml => Factory.GetInstance();
+
public static IProfilingLogger ProfilingLogger => Factory.GetInstance();
public static AppCaches AppCaches => Factory.GetInstance();
diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
index 6c207dd15a..2795fe66c6 100644
--- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
+++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
@@ -30,7 +30,7 @@ namespace Umbraco.Web
///
public static IHtmlString RenderProfiler(this HtmlHelper helper)
{
- return new HtmlString(Current.Profiler.Render());
+ return new HtmlString(Current.ProfilerHtml.Render());
}
///
diff --git a/src/Umbraco.Web/Logging/WebProfiler.cs b/src/Umbraco.Web/Logging/WebProfiler.cs
index e390950c0b..6f15be7ecd 100755
--- a/src/Umbraco.Web/Logging/WebProfiler.cs
+++ b/src/Umbraco.Web/Logging/WebProfiler.cs
@@ -14,7 +14,7 @@ namespace Umbraco.Web.Logging
///
/// Profiling only runs when the app is in debug mode, see WebRuntime for how this gets created
///
- internal class WebProfiler : IProfiler
+ internal class WebProfiler : IProfiler, IProfilerHtml
{
private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest";
private readonly WebProfilerProvider _provider;
diff --git a/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs b/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs
index 1c596ff80c..6904aa103a 100644
--- a/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs
+++ b/src/Umbraco.Web/Mvc/ActionExecutedEventArgs.cs
@@ -3,6 +3,7 @@ using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
+ /// Migrated already to .Net Core
public class ActionExecutedEventArgs : EventArgs
{
public Controller Controller { get; set; }
diff --git a/src/Umbraco.Web/Mvc/Constants.cs b/src/Umbraco.Web/Mvc/Constants.cs
index 1794345746..c71ed6b104 100644
--- a/src/Umbraco.Web/Mvc/Constants.cs
+++ b/src/Umbraco.Web/Mvc/Constants.cs
@@ -3,6 +3,7 @@
///
/// constants
///
+ /// Migrated already to .Net Core
internal static class Constants
{
internal const string ViewLocation = "~/Views";
diff --git a/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs b/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs
index 34b857dfb4..f443abbb70 100644
--- a/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs
+++ b/src/Umbraco.Web/Mvc/EnsurePartialViewMacroViewContextFilterAttribute.cs
@@ -19,6 +19,7 @@ namespace Umbraco.Web.Mvc
/// this DataToken exists before the action executes in case the developer resolves an RTE value that contains
/// a partial view macro form.
///
+ /// Migrated already to .Net Core
internal class EnsurePartialViewMacroViewContextFilterAttribute : ActionFilterAttribute
{
///
diff --git a/src/Umbraco.Web/Mvc/IRenderController.cs b/src/Umbraco.Web/Mvc/IRenderController.cs
index 103a484869..0de585959c 100644
--- a/src/Umbraco.Web/Mvc/IRenderController.cs
+++ b/src/Umbraco.Web/Mvc/IRenderController.cs
@@ -5,6 +5,7 @@ namespace Umbraco.Web.Mvc
///
/// A marker interface to designate that a controller will be used for Umbraco front-end requests and/or route hijacking
///
+ /// Migrated already to .Net Core
public interface IRenderController : IController
{
diff --git a/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs b/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs
index 227c15b344..635a7314c5 100644
--- a/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs
+++ b/src/Umbraco.Web/Mvc/MinifyJavaScriptResultAttribute.cs
@@ -12,6 +12,7 @@ namespace Umbraco.Web.Mvc
///
/// Only minifies in release mode
///
+ /// Migrated already to .Net Core
public class MinifyJavaScriptResultAttribute : ActionFilterAttribute
{
private readonly IHostingEnvironment _hostingEnvironment;
diff --git a/src/Umbraco.Web/Mvc/PreRenderViewActionFilterAttribute.cs b/src/Umbraco.Web/Mvc/PreRenderViewActionFilterAttribute.cs
index 54e20f5d56..2e659eccf6 100644
--- a/src/Umbraco.Web/Mvc/PreRenderViewActionFilterAttribute.cs
+++ b/src/Umbraco.Web/Mvc/PreRenderViewActionFilterAttribute.cs
@@ -3,6 +3,7 @@ using System.Web.Mvc;
namespace Umbraco.Web.Mvc
{
+ /// Migrated already to .Net Core
public class PreRenderViewActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
diff --git a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs
index b1836c6ba4..727c29b93c 100644
--- a/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs
+++ b/src/Umbraco.Web/Mvc/StatusCodeFilterAttribute.cs
@@ -8,6 +8,7 @@ namespace Umbraco.Web.Mvc
///
/// Forces the response to have a specific http status code
///
+ /// Migrated already to .Net Core
internal class StatusCodeResultAttribute : ActionFilterAttribute
{
private readonly HttpStatusCode _statusCode;
diff --git a/src/Umbraco.Web/Runtime/WebInitialComposer.cs b/src/Umbraco.Web/Runtime/WebInitialComposer.cs
index 0b70121d38..36a997c2b8 100644
--- a/src/Umbraco.Web/Runtime/WebInitialComposer.cs
+++ b/src/Umbraco.Web/Runtime/WebInitialComposer.cs
@@ -25,6 +25,9 @@ using Umbraco.Web.Trees;
using Umbraco.Web.WebApi;
using Umbraco.Net;
using Umbraco.Web.AspNet;
+using Umbraco.Core.Diagnostics;
+using Umbraco.Core.Logging;
+using Umbraco.Web.Logging;
namespace Umbraco.Web.Runtime
{
@@ -50,7 +53,8 @@ namespace Umbraco.Web.Runtime
-
+ composition.RegisterUnique();
+ composition.RegisterUnique();
composition.ComposeWebMappingProfiles();
diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs
index cd72b2faf9..7679da2e2e 100644
--- a/src/Umbraco.Web/UmbracoApplication.cs
+++ b/src/Umbraco.Web/UmbracoApplication.cs
@@ -1,4 +1,7 @@
-using System.Threading;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
using System.Web;
using Umbraco.Core;
using Umbraco.Core.Cache;
@@ -34,10 +37,12 @@ namespace Umbraco.Web
var mainDom = new MainDom(logger, mainDomLock);
- var requestCache = new HttpRequestAppCache(() => HttpContext.Current?.Items);
+ var requestCache = new HttpRequestAppCache(() => HttpContext.Current != null ? HttpContext.Current.Items : null);
var umbracoBootPermissionChecker = new AspNetUmbracoBootPermissionChecker();
- return new WebRuntime(configs, umbracoVersion, ioHelper, logger, profiler, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom,
- GetTypeFinder(hostingEnvironment, logger, profiler), requestCache, umbracoBootPermissionChecker);
+ return new CoreRuntime(configs, umbracoVersion, ioHelper, logger, profiler, umbracoBootPermissionChecker, hostingEnvironment, backOfficeInfo, dbProviderFactoryCreator, mainDom,
+ GetTypeFinder(hostingEnvironment, logger, profiler), requestCache);
}
+
+
}
}
diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs
index d884366bf1..1f90bc7d13 100644
--- a/src/Umbraco.Web/UmbracoApplicationBase.cs
+++ b/src/Umbraco.Web/UmbracoApplicationBase.cs
@@ -1,4 +1,5 @@
-using System;
+using Serilog.Context;
+using System;
using System.IO;
using System.Reflection;
using System.Threading;
@@ -12,6 +13,8 @@ using Umbraco.Core.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Logging.Serilog;
+using Umbraco.Core.Logging.Serilog.Enrichers;
+using Umbraco.Net;
using Umbraco.Web.AspNet;
using Umbraco.Web.Hosting;
using Umbraco.Web.Logging;
@@ -34,12 +37,16 @@ namespace Umbraco.Web
var configFactory = new ConfigsFactory();
var hostingSettings = configFactory.HostingSettings;
- var coreDebug = configFactory.CoreDebugSettings;
var globalSettings = configFactory.GlobalSettings;
var hostingEnvironment = new AspNetHostingEnvironment(hostingSettings);
+ var loggingConfiguration = new LoggingConfiguration(
+ Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "App_Data\\Logs"),
+ Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "config\\serilog.config"),
+ Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "config\\serilog.user.config"));
var ioHelper = new IOHelper(hostingEnvironment, globalSettings);
- var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, new AspNetSessionManager(), () => _factory?.GetInstance(), coreDebug, ioHelper, new FrameworkMarchal());
+ var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration);
+
var configs = configFactory.Create();
var backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, ioHelper, logger, configFactory.WebRoutingSettings);
@@ -168,6 +175,11 @@ namespace Umbraco.Web
Umbraco.Composing.Current.BackOfficeInfo);
_factory = Current.Factory = _runtime.Configure(register);
+ // now we can add our request based logging enrichers (globally, which is what we were doing in netframework before)
+ LogContext.Push(new HttpSessionIdEnricher(_factory.GetInstance()));
+ LogContext.Push(new HttpRequestNumberEnricher(_factory.GetInstance()));
+ LogContext.Push(new HttpRequestIdEnricher(_factory.GetInstance()));
+
_runtime.Start();
}