using System;
using System.IO;
using System.Text;
using Serilog;
using Serilog.Configuration;
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting;
using Serilog.Formatting.Compact;
using Umbraco.Core.Hosting;
using Umbraco.Core.Logging.Serilog.Enrichers;
namespace Umbraco.Core.Logging.Serilog
{
public static class LoggerConfigExtensions
{
private const string AppDomainId = "AppDomainId";
///
/// This configures Serilog with some defaults
/// Such as adding ProcessID, Thread, AppDomain etc
/// 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,
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", loggingConfiguration.LogDirectory.TrimEnd("\\"), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("UMBLOGDIR", loggingConfiguration.LogDirectory, 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("AppDomainAppId", AppDomain.CurrentDomain.Id.ToString().ReplaceNonAlphanumericChars(string.Empty))
.Enrich.WithProperty("MachineName", Environment.MachineName)
.Enrich.With()
.Enrich.FromLogContext(); // allows us to dynamically enrich
logConfig.WriteTo.UmbracoFile(
Path.Combine(loggingConfiguration.LogDirectory, $"UmbracoTraceLog.{Environment.MachineName}..json")
);
return logConfig;
}
///
/// 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,
IHostingEnvironment hostingEnvironment,
LogEventLevel minimumLevel = LogEventLevel.Verbose)
{
//Main .txt logfile - in similar format to older Log4Net output
//Ends with ..txt as Date is inserted before file extension substring
logConfig.WriteTo.File(Path.Combine(hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.LogFiles), $"UmbracoTraceLog.{Environment.MachineName}..txt"),
shared: true,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: minimumLevel,
retainedFileCountLimit: null, //Setting to null means we keep all files - default is 31 days
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}");
return logConfig;
}
///
/// Used in config - If renamed or moved to other assembly the config file also has be updated.
///
public static LoggerConfiguration UmbracoFile(this LoggerSinkConfiguration configuration,
string path,
ITextFormatter formatter = null,
LogEventLevel restrictedToMinimumLevel = LogEventLevel.Verbose,
LoggingLevelSwitch levelSwitch = null,
long? fileSizeLimitBytes = 1073741824,
TimeSpan? flushToDiskInterval = null,
RollingInterval rollingInterval = RollingInterval.Day,
bool rollOnFileSizeLimit = false,
int? retainedFileCountLimit = 31,
Encoding encoding = null
)
{
if (formatter is null)
{
formatter = new CompactJsonFormatter();
}
return configuration.Async(
asyncConfiguration => asyncConfiguration.Map(AppDomainId, (_,mapConfiguration) =>
mapConfiguration.File(
formatter,
path,
restrictedToMinimumLevel,
fileSizeLimitBytes,
levelSwitch,
buffered:true,
shared:false,
flushToDiskInterval,
rollingInterval,
rollOnFileSizeLimit,
retainedFileCountLimit,
encoding,
null),
sinkMapCountLimit:0)
);
}
///
/// 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,
IHostingEnvironment hostingEnvironment,
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(),
Path.Combine(hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.LogFiles) ,$"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
restrictedToMinimumLevel: minimumLevel);
return logConfig;
}
}
}