224 lines
8.8 KiB
C#
224 lines
8.8 KiB
C#
using System.Reflection;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
using Serilog;
|
|
using Serilog.Core;
|
|
using Serilog.Extensions.Hosting;
|
|
using Serilog.Extensions.Logging;
|
|
using Umbraco.Cms.Core.Cache;
|
|
using Umbraco.Cms.Core.Composing;
|
|
using Umbraco.Cms.Core.Configuration.Models;
|
|
using Umbraco.Cms.Core.DependencyInjection;
|
|
using Umbraco.Cms.Core.Extensions;
|
|
using Umbraco.Cms.Core.Logging;
|
|
using Umbraco.Cms.Core.Logging.Serilog;
|
|
using Umbraco.Cms.Infrastructure.Logging.Serilog;
|
|
using Umbraco.Cms.Web.Common.Hosting;
|
|
using Umbraco.Cms.Web.Common.Logging;
|
|
using Umbraco.Cms.Web.Common.Logging.Enrichers;
|
|
using Constants = Umbraco.Cms.Core.Constants;
|
|
using IHostingEnvironment = Umbraco.Cms.Core.Hosting.IHostingEnvironment;
|
|
using ILogger = Serilog.ILogger;
|
|
|
|
namespace Umbraco.Extensions;
|
|
|
|
public static class ServiceCollectionExtensions
|
|
{
|
|
/// <summary>
|
|
/// Create and configure the logger
|
|
/// </summary>
|
|
[Obsolete("Use the extension method that takes an IHostEnvironment instance instead.")]
|
|
public static IServiceCollection AddLogger(
|
|
this IServiceCollection services,
|
|
IHostingEnvironment hostingEnvironment,
|
|
ILoggingConfiguration loggingConfiguration,
|
|
IConfiguration configuration)
|
|
{
|
|
// Create a serilog logger
|
|
var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration, configuration, out UmbracoFileConfiguration umbracoFileConfig);
|
|
services.AddSingleton(umbracoFileConfig);
|
|
|
|
// This is nessasary to pick up all the loggins to MS ILogger.
|
|
Log.Logger = logger.SerilogLog;
|
|
|
|
// 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.AddLogging(configure =>
|
|
{
|
|
configure.AddSerilog(logger.SerilogLog);
|
|
});
|
|
|
|
// 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<IDiagnosticContext>(diagnosticContext);
|
|
services.AddSingleton(loggingConfiguration);
|
|
|
|
return services;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create and configure the logger.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Additional Serilog services are registered during <see cref="HostBuilderExtensions.ConfigureUmbracoDefaults" />.
|
|
/// </remarks>
|
|
public static IServiceCollection AddLogger(
|
|
this IServiceCollection services,
|
|
IHostEnvironment hostEnvironment,
|
|
IConfiguration configuration)
|
|
{
|
|
LoggingSettings loggerSettings = GetLoggerSettings(configuration);
|
|
|
|
var loggingDir = loggerSettings.GetAbsoluteLoggingPath(hostEnvironment);
|
|
ILoggingConfiguration loggingConfig = new LoggingConfiguration(loggingDir);
|
|
|
|
var umbracoFileConfiguration = new UmbracoFileConfiguration(configuration);
|
|
|
|
services.TryAddSingleton(umbracoFileConfiguration);
|
|
services.TryAddSingleton(loggingConfig);
|
|
services.TryAddSingleton<ILogEventEnricher, ApplicationIdEnricher>();
|
|
|
|
///////////////////////////////////////////////
|
|
// Bootstrap logger setup
|
|
///////////////////////////////////////////////
|
|
|
|
Func<LoggerConfiguration, LoggerConfiguration> serilogConfig = cfg => cfg
|
|
.MinimalConfiguration(hostEnvironment, loggingConfig, umbracoFileConfiguration)
|
|
.ReadFrom.Configuration(configuration);
|
|
|
|
if (Log.Logger is ReloadableLogger reloadableLogger)
|
|
{
|
|
reloadableLogger.Reload(serilogConfig);
|
|
}
|
|
else
|
|
{
|
|
Log.Logger = serilogConfig(new LoggerConfiguration()).CreateBootstrapLogger();
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Runtime logger setup
|
|
///////////////////////////////////////////////
|
|
|
|
services.AddSingleton(sp =>
|
|
{
|
|
var logger = new RegisteredReloadableLogger(Log.Logger as ReloadableLogger);
|
|
|
|
logger.Reload(cfg =>
|
|
{
|
|
cfg.MinimalConfiguration(hostEnvironment, loggingConfig, umbracoFileConfiguration)
|
|
.ReadFrom.Configuration(configuration)
|
|
.ReadFrom.Services(sp);
|
|
|
|
return cfg;
|
|
});
|
|
|
|
return logger;
|
|
});
|
|
|
|
services.AddSingleton<ILogger>(sp =>
|
|
{
|
|
ILogger logger = sp.GetRequiredService<RegisteredReloadableLogger>().Logger;
|
|
return logger.ForContext(new NoopEnricher());
|
|
});
|
|
|
|
services.AddSingleton<ILoggerFactory>(sp =>
|
|
{
|
|
ILogger logger = sp.GetRequiredService<RegisteredReloadableLogger>().Logger;
|
|
return new SerilogLoggerFactory(logger);
|
|
});
|
|
|
|
// Registered to provide two services...
|
|
var diagnosticContext = new DiagnosticContext(Log.Logger);
|
|
|
|
// Consumed by e.g. middleware
|
|
services.TryAddSingleton(diagnosticContext);
|
|
|
|
// Consumed by user code
|
|
services.TryAddSingleton<IDiagnosticContext>(diagnosticContext);
|
|
|
|
return services;
|
|
}
|
|
|
|
private static LoggingSettings GetLoggerSettings(IConfiguration configuration)
|
|
{
|
|
var loggerSettings = new LoggingSettings();
|
|
configuration.GetSection(Constants.Configuration.ConfigLogging).Bind(loggerSettings);
|
|
return loggerSettings;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called to create the <see cref="TypeLoader" /> to assign to the <see cref="IUmbracoBuilder" />
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This should never be called in a web project. It is used internally by Umbraco but could be used in unit tests.
|
|
/// If called in a web project it will have no affect except to create and return a new TypeLoader but this will not
|
|
/// be the instance in DI.
|
|
/// </remarks>
|
|
[Obsolete("Please use alternative extension method.")]
|
|
public static TypeLoader AddTypeLoader(
|
|
this IServiceCollection services,
|
|
Assembly entryAssembly,
|
|
IHostingEnvironment hostingEnvironment,
|
|
ILoggerFactory loggerFactory,
|
|
AppCaches appCaches,
|
|
IConfiguration configuration,
|
|
IProfiler profiler) =>
|
|
services.AddTypeLoader(entryAssembly, loggerFactory, configuration);
|
|
|
|
/// <summary>
|
|
/// Called to create the <see cref="TypeLoader" /> to assign to the <see cref="IUmbracoBuilder" />
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This should never be called in a web project. It is used internally by Umbraco but could be used in unit tests.
|
|
/// If called in a web project it will have no affect except to create and return a new TypeLoader but this will not
|
|
/// be the instance in DI.
|
|
/// </remarks>
|
|
public static TypeLoader AddTypeLoader(
|
|
this IServiceCollection services,
|
|
Assembly? entryAssembly,
|
|
ILoggerFactory loggerFactory,
|
|
IConfiguration configuration)
|
|
{
|
|
TypeFinderSettings typeFinderSettings =
|
|
configuration.GetSection(Constants.Configuration.ConfigTypeFinder).Get<TypeFinderSettings>() ??
|
|
new TypeFinderSettings();
|
|
|
|
var assemblyProvider = new DefaultUmbracoAssemblyProvider(
|
|
entryAssembly,
|
|
loggerFactory,
|
|
typeFinderSettings.AdditionalEntryAssemblies);
|
|
|
|
var typeFinderConfig = new TypeFinderConfig(Options.Create(typeFinderSettings));
|
|
|
|
var typeFinder = new TypeFinder(
|
|
loggerFactory.CreateLogger<TypeFinder>(),
|
|
assemblyProvider,
|
|
typeFinderSettings.AdditionalAssemblyExclusionEntries,
|
|
typeFinderConfig);
|
|
|
|
var typeLoader = new TypeLoader(typeFinder, loggerFactory.CreateLogger<TypeLoader>());
|
|
|
|
// This will add it ONCE and not again which is what we want since we don't actually want people to call this method
|
|
// in the web project.
|
|
services.TryAddSingleton<ITypeFinder>(typeFinder);
|
|
services.TryAddSingleton(typeLoader);
|
|
|
|
return typeLoader;
|
|
}
|
|
}
|