Allow for configuration of log file names (#19074)
* Added configuration for the log file name and format. * Added unit test for LoggingConfiguration. * Rely on configuration validation to verify supported log file format arguments. * Fixed unit test failing on build pipeline.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
// See LICENSE for more details.
|
||||
|
||||
using System.ComponentModel;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models;
|
||||
|
||||
@@ -13,6 +14,8 @@ public class LoggingSettings
|
||||
{
|
||||
internal const string StaticMaxLogAge = "1.00:00:00"; // TimeSpan.FromHours(24);
|
||||
internal const string StaticDirectory = Constants.SystemDirectories.LogFiles;
|
||||
internal const string StaticFileNameFormat = LoggingConfiguration.DefaultLogFileNameFormat;
|
||||
internal const string StaticFileNameFormatArguments = "MachineName";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the maximum age of a log file.
|
||||
@@ -31,4 +34,25 @@ public class LoggingSettings
|
||||
/// </value>
|
||||
[DefaultValue(StaticDirectory)]
|
||||
public string Directory { get; set; } = StaticDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file name format to use for log files.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The file name format.
|
||||
/// </value>
|
||||
[DefaultValue(StaticFileNameFormat)]
|
||||
public string FileNameFormat { get; set; } = StaticFileNameFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the file name format arguments to use for log files.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The file name format arguments as a comma delimited string of accepted values.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// Accepted values for format arguments are: MachineName, EnvironmentName.
|
||||
/// </remarks>
|
||||
[DefaultValue(StaticFileNameFormatArguments)]
|
||||
public string FileNameFormatArguments { get; set; } = StaticFileNameFormatArguments;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Core.Configuration.Models.Validation;
|
||||
|
||||
/// <summary>
|
||||
/// Validator for configuration representated as <see cref="LoggingSettings" />.
|
||||
/// </summary>
|
||||
public class LoggingSettingsValidator : ConfigurationValidatorBase, IValidateOptions<LoggingSettings>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public ValidateOptionsResult Validate(string? name, LoggingSettings options)
|
||||
{
|
||||
if (!ValidateFileNameFormatArgument(options.FileNameFormat, options.FileNameFormatArguments, out var message))
|
||||
{
|
||||
return ValidateOptionsResult.Fail(message);
|
||||
}
|
||||
|
||||
|
||||
return ValidateOptionsResult.Success;
|
||||
}
|
||||
|
||||
private bool ValidateFileNameFormatArgument(string fileNameFormat, string fileNameFormatArguments, out string message)
|
||||
{
|
||||
var fileNameFormatArgumentsAsArray = fileNameFormatArguments
|
||||
.Split([','], StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.ToArray();
|
||||
if (fileNameFormatArgumentsAsArray.Any(x => LoggingConfiguration.SupportedFileNameFormatArguments.Contains(x) is false))
|
||||
{
|
||||
message = $"The file name arguments '{string.Join(",", fileNameFormatArgumentsAsArray)}' contain one or more values that aren't in the supported list of values '{string.Join(",", LoggingConfiguration.SupportedFileNameFormatArguments)}'.";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ = string.Format(fileNameFormat, fileNameFormatArgumentsAsArray);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
message = $"The provided file name format '{fileNameFormat}' could not be used with the provided arguments '{string.Join(",", fileNameFormatArgumentsAsArray)}'.";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = string.Empty;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ public static partial class UmbracoBuilderExtensions
|
||||
builder.Services.AddSingleton<IValidateOptions<ContentSettings>, ContentSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<GlobalSettings>, GlobalSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<HealthChecksSettings>, HealthChecksSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<LoggingSettings>, LoggingSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<RequestHandlerSettings>, RequestHandlerSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<UnattendedSettings>, UnattendedSettingsValidator>();
|
||||
builder.Services.AddSingleton<IValidateOptions<SecuritySettings>, SecuritySettingsValidator>();
|
||||
|
||||
@@ -3,7 +3,17 @@ namespace Umbraco.Cms.Core.Logging;
|
||||
public interface ILoggingConfiguration
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the physical path where logs are stored
|
||||
/// Gets the physical path where logs are stored.
|
||||
/// </summary>
|
||||
string LogDirectory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name format for the log files.
|
||||
/// </summary>
|
||||
string LogFileNameFormat { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name format arguments for the log files.
|
||||
/// </summary>
|
||||
string[] GetLogFileNameFormatArguments();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,73 @@
|
||||
namespace Umbraco.Cms.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Implements <see cref="ILoggingConfiguration"/> to provide configuration for logging to files.
|
||||
/// </summary>
|
||||
public class LoggingConfiguration : ILoggingConfiguration
|
||||
{
|
||||
public LoggingConfiguration(string logDirectory) =>
|
||||
LogDirectory = logDirectory ?? throw new ArgumentNullException(nameof(logDirectory));
|
||||
/// <summary>
|
||||
/// The default log file name format.
|
||||
/// </summary>
|
||||
public const string DefaultLogFileNameFormat = "UmbracoTraceLog.{0}..json";
|
||||
|
||||
/// <summary>
|
||||
/// The default log file name format arguments.
|
||||
/// </summary>
|
||||
public const string DefaultLogFileNameFormatArguments = MachineNameFileFormatArgument;
|
||||
|
||||
/// <summary>
|
||||
/// The collection of supported file name format arguments.
|
||||
/// </summary>
|
||||
public static readonly string[] SupportedFileNameFormatArguments =
|
||||
{
|
||||
MachineNameFileFormatArgument,
|
||||
EnvironmentNameFileFormatArgument,
|
||||
};
|
||||
|
||||
private readonly string _logFileNameFormatArguments;
|
||||
|
||||
private const string MachineNameFileFormatArgument = "MachineName";
|
||||
private const string EnvironmentNameFileFormatArgument = "EnvironmentName";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoggingConfiguration"/> class with the default log file name format and arguments.
|
||||
/// </summary>
|
||||
/// <param name="logDirectory">The log file directory.</param>
|
||||
public LoggingConfiguration(string logDirectory)
|
||||
: this(logDirectory, DefaultLogFileNameFormat, DefaultLogFileNameFormatArguments)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LoggingConfiguration"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logDirectory">The log file directory.</param>
|
||||
/// <param name="logFileNameFormat">The log file name format.</param>
|
||||
/// <param name="logFileNameFormatArguments">The log file name format arguments as a comma delimited string.</param>
|
||||
public LoggingConfiguration(string logDirectory, string logFileNameFormat, string logFileNameFormatArguments)
|
||||
{
|
||||
LogDirectory = logDirectory;
|
||||
LogFileNameFormat = logFileNameFormat;
|
||||
_logFileNameFormatArguments = logFileNameFormatArguments;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string LogDirectory { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string LogFileNameFormat { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string[] GetLogFileNameFormatArguments() => _logFileNameFormatArguments.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim())
|
||||
.Select(GetValue)
|
||||
.ToArray();
|
||||
|
||||
private static string GetValue(string arg) =>
|
||||
arg switch
|
||||
{
|
||||
MachineNameFileFormatArgument => Environment.MachineName,
|
||||
EnvironmentNameFileFormatArgument => Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Umbraco.Extensions
|
||||
.Enrich.FromLogContext(); // allows us to dynamically enrich
|
||||
|
||||
logConfig.WriteTo.UmbracoFile(
|
||||
path: umbracoFileConfiguration.GetPath(loggingConfiguration.LogDirectory),
|
||||
path: umbracoFileConfiguration.GetPath(loggingConfiguration.LogDirectory, loggingConfiguration.LogFileNameFormat, loggingConfiguration.GetLogFileNameFormatArguments()),
|
||||
fileSizeLimitBytes: umbracoFileConfiguration.FileSizeLimitBytes,
|
||||
restrictedToMinimumLevel: umbracoFileConfiguration.RestrictedToMinimumLevel,
|
||||
rollingInterval: umbracoFileConfiguration.RollingInterval,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Logging.Serilog;
|
||||
|
||||
@@ -43,5 +44,8 @@ public class UmbracoFileConfiguration
|
||||
public int RetainedFileCountLimit { get; set; } = 31;
|
||||
|
||||
public string GetPath(string logDirectory) =>
|
||||
Path.Combine(logDirectory, $"UmbracoTraceLog.{Environment.MachineName}..json");
|
||||
GetPath(logDirectory, LoggingConfiguration.DefaultLogFileNameFormat, Environment.MachineName);
|
||||
|
||||
public string GetPath(string logDirectory, string fileNameFormat, params string[] fileNameArgs) =>
|
||||
Path.Combine(logDirectory, string.Format(fileNameFormat, fileNameArgs));
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ public static class ServiceCollectionExtensions
|
||||
LoggingSettings loggerSettings = GetLoggerSettings(configuration);
|
||||
|
||||
var loggingDir = loggerSettings.GetAbsoluteLoggingPath(hostEnvironment);
|
||||
ILoggingConfiguration loggingConfig = new LoggingConfiguration(loggingDir);
|
||||
ILoggingConfiguration loggingConfig = new LoggingConfiguration(loggingDir, loggerSettings.FileNameFormat, loggerSettings.FileNameFormatArguments);
|
||||
|
||||
var umbracoFileConfiguration = new UmbracoFileConfiguration(configuration);
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) Umbraco.
|
||||
// See LICENSE for more details.
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Configuration.Models.Validation;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Configuration.Models.Validation
|
||||
{
|
||||
[TestFixture]
|
||||
public class LoggingSettingsValidatorTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Success_ForValid_Configuration()
|
||||
{
|
||||
var validator = new LoggingSettingsValidator();
|
||||
LoggingSettings options = BuildLoggingSettings();
|
||||
ValidateOptionsResult result = validator.Validate("settings", options);
|
||||
Assert.True(result.Succeeded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Returns_Fail_For_Configuration_With_Invalid_FileNameFormatArguments()
|
||||
{
|
||||
var validator = new LoggingSettingsValidator();
|
||||
LoggingSettings options = BuildLoggingSettings(fileNameFormatArguments: "MachineName,Invalid");
|
||||
ValidateOptionsResult result = validator.Validate("settings", options);
|
||||
Assert.False(result.Succeeded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Returns_Fail_For_Configuration_With_Invalid_FileNameFormat()
|
||||
{
|
||||
var validator = new LoggingSettingsValidator();
|
||||
LoggingSettings options = BuildLoggingSettings(fileNameFormat: "InvalidAsTooManyPlaceholders_{0}_{1}");
|
||||
ValidateOptionsResult result = validator.Validate("settings", options);
|
||||
Assert.False(result.Succeeded);
|
||||
}
|
||||
|
||||
private static LoggingSettings BuildLoggingSettings(
|
||||
string fileNameFormat = LoggingConfiguration.DefaultLogFileNameFormat,
|
||||
string fileNameFormatArguments = LoggingConfiguration.DefaultLogFileNameFormatArguments) =>
|
||||
new LoggingSettings
|
||||
{
|
||||
FileNameFormat = fileNameFormat,
|
||||
FileNameFormatArguments = fileNameFormatArguments,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Cms.Core.Logging;
|
||||
|
||||
namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Logging;
|
||||
|
||||
[TestFixture]
|
||||
public class LoggingConfigurationTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Production");
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Supported_Log_File_Name_Format_Arguments()
|
||||
{
|
||||
var config = new LoggingConfiguration("c:\\logs\\", "UmbracoLogFile_{0}_{1}..json", "MachineName,EnvironmentName");
|
||||
var result = config.GetLogFileNameFormatArguments();
|
||||
|
||||
Assert.AreEqual(2, result.Length);
|
||||
|
||||
var expectedMachineName = Environment.MachineName;
|
||||
var expectedEnvironmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
|
||||
Assert.AreEqual(expectedMachineName, result[0]);
|
||||
Assert.AreEqual(expectedEnvironmentName, result[1]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user