Added Log4Net and supporting classes from v5 in order to start integration of new diagnostic logging system.

This commit is contained in:
shannon@ShandemVaio
2012-07-28 01:28:39 +06:00
parent b186d5f8e4
commit b09422ed5e
17 changed files with 60743 additions and 140 deletions

View File

@@ -0,0 +1,271 @@
using System;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using log4net.Appender;
using log4net.Core;
using log4net.Util;
namespace Umbraco.Core.Logging
{
/// <summary>
/// Based on code by Chris Haines http://cjbhaines.wordpress.com/2012/02/13/asynchronous-log4net-appenders/
/// </summary>
public class AsynchronousRollingFileAppender : RollingFileAppender
{
private readonly ManualResetEvent _manualResetEvent;
private int _bufferOverflowCounter;
private bool _forceStop;
private bool _hasFinished;
private DateTime _lastLoggedBufferOverflow;
private bool _logBufferOverflow;
private RingBuffer<LoggingEvent> _pendingAppends;
private int _queueSizeLimit = 1000;
private bool _shuttingDown;
public AsynchronousRollingFileAppender()
{
_manualResetEvent = new ManualResetEvent(false);
}
public int QueueSizeLimit
{
get { return _queueSizeLimit; }
set { _queueSizeLimit = value; }
}
public override void ActivateOptions()
{
base.ActivateOptions();
_pendingAppends = new RingBuffer<LoggingEvent>(QueueSizeLimit);
_pendingAppends.BufferOverflow += OnBufferOverflow;
StartAppendTask();
}
protected override void Append(LoggingEvent[] loggingEvents)
{
Array.ForEach(loggingEvents, Append);
}
protected override void Append(LoggingEvent loggingEvent)
{
if (FilterEvent(loggingEvent))
{
_pendingAppends.Enqueue(loggingEvent);
}
}
protected override void OnClose()
{
_shuttingDown = true;
_manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));
if (!_hasFinished)
{
_forceStop = true;
var windowsIdentity = WindowsIdentity.GetCurrent();
base.Append(new LoggingEvent(new LoggingEventData
{
Level = Level.Error,
Message =
"Unable to clear out the AsynchronousRollingFileAppender buffer in the allotted time, forcing a shutdown",
TimeStamp = DateTime.UtcNow,
Identity = "",
ExceptionString = "",
UserName = windowsIdentity != null ? windowsIdentity.Name : "",
Domain = AppDomain.CurrentDomain.FriendlyName,
ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
LocationInfo =
new LocationInfo(this.GetType().Name, "OnClose", "AsynchronousRollingFileAppender.cs", "59"),
LoggerName = this.GetType().FullName,
Properties = new PropertiesDictionary(),
})
);
}
base.OnClose();
}
private void StartAppendTask()
{
if (!_shuttingDown)
{
Task appendTask = new Task(AppendLoggingEvents, TaskCreationOptions.LongRunning);
appendTask.LogErrors(LogAppenderError).ContinueWith(x => StartAppendTask()).LogErrors(LogAppenderError);
appendTask.Start();
}
}
private void LogAppenderError(string logMessage, Exception exception)
{
var windowsIdentity = WindowsIdentity.GetCurrent();
base.Append(new LoggingEvent(new LoggingEventData
{
Level = Level.Error,
Message = "Appender exception: " + logMessage,
TimeStamp = DateTime.UtcNow,
Identity = "",
ExceptionString = exception.ToString(),
UserName = windowsIdentity != null ? windowsIdentity.Name : "",
Domain = AppDomain.CurrentDomain.FriendlyName,
ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
LocationInfo =
new LocationInfo(this.GetType().Name,
"LogAppenderError",
"AsynchronousRollingFileAppender.cs",
"100"),
LoggerName = this.GetType().FullName,
Properties = new PropertiesDictionary(),
}));
}
private void AppendLoggingEvents()
{
LoggingEvent loggingEventToAppend;
while (!_shuttingDown)
{
if (_logBufferOverflow)
{
LogBufferOverflowError();
_logBufferOverflow = false;
_bufferOverflowCounter = 0;
_lastLoggedBufferOverflow = DateTime.UtcNow;
}
while (!_pendingAppends.TryDequeue(out loggingEventToAppend))
{
Thread.Sleep(10);
if (_shuttingDown)
{
break;
}
}
if (loggingEventToAppend == null)
{
continue;
}
try
{
base.Append(loggingEventToAppend);
}
catch
{
}
}
while (_pendingAppends.TryDequeue(out loggingEventToAppend) && !_forceStop)
{
try
{
base.Append(loggingEventToAppend);
}
catch
{
}
}
_hasFinished = true;
_manualResetEvent.Set();
}
private void LogBufferOverflowError()
{
var windowsIdentity = WindowsIdentity.GetCurrent();
base.Append(new LoggingEvent(new LoggingEventData
{
Level = Level.Error,
Message =
string.Format(
"Buffer overflow. {0} logging events have been lost in the last 30 seconds. [QueueSizeLimit: {1}]",
_bufferOverflowCounter,
QueueSizeLimit),
TimeStamp = DateTime.UtcNow,
Identity = "",
ExceptionString = "",
UserName = windowsIdentity != null ? windowsIdentity.Name : "",
Domain = AppDomain.CurrentDomain.FriendlyName,
ThreadName = Thread.CurrentThread.ManagedThreadId.ToString(),
LocationInfo =
new LocationInfo(this.GetType().Name,
"LogBufferOverflowError",
"AsynchronousRollingFileAppender.cs",
"172"),
LoggerName = this.GetType().FullName,
Properties = new PropertiesDictionary(),
}));
}
private void OnBufferOverflow(object sender, EventArgs eventArgs)
{
_bufferOverflowCounter++;
if (_logBufferOverflow == false)
{
if (_lastLoggedBufferOverflow < DateTime.UtcNow.AddSeconds(-30))
{
_logBufferOverflow = true;
}
}
}
private class RingBuffer<T>
{
private readonly object _lockObject = new object();
private readonly T[] _buffer;
private readonly int _size;
private int _readIndex = 0;
private int _writeIndex = 0;
private bool _bufferFull = false;
public event Action<object, EventArgs> BufferOverflow;
public RingBuffer(int size)
{
this._size = size;
_buffer = new T[size];
}
public void Enqueue(T item)
{
lock (_lockObject)
{
_buffer[_writeIndex] = item;
_writeIndex = (++_writeIndex) % _size;
if (_bufferFull)
{
if (BufferOverflow != null)
{
BufferOverflow(this, EventArgs.Empty);
}
_readIndex = _writeIndex;
}
else if (_writeIndex == _readIndex)
{
_bufferFull = true;
}
}
}
public bool TryDequeue(out T ret)
{
if (_readIndex == _writeIndex && !_bufferFull)
{
ret = default(T);
return false;
}
lock (_lockObject)
{
if (_readIndex == _writeIndex && !_bufferFull)
{
ret = default(T);
return false;
}
ret = _buffer[_readIndex];
_readIndex = (++_readIndex) % _size;
_bufferFull = false;
return true;
}
}
}
}
}

View File

@@ -1,153 +1,156 @@
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using log4net;
//namespace Umbraco.Core.Logging
//{
// ///<summary>
// /// Used for logging
// ///</summary>
// internal static class LogHelper
// {
// static LogHelper()
// {
// //var appSetting = ConfigurationManager.AppSettings["log4net-config-path"];
// //if (appSetting != null && File.Exists(appSetting))
// // XmlConfigurator.ConfigureAndWatch(new FileInfo(appSetting));
// //else
// //XmlConfigurator.Configure();
// }
namespace Umbraco.Core.Logging
{
///<summary>
/// Used for logging
///</summary>
internal static class LogHelper
{
static LogHelper()
{
//var appSetting = ConfigurationManager.AppSettings["log4net-config-path"];
//if (appSetting != null && File.Exists(appSetting))
// XmlConfigurator.ConfigureAndWatch(new FileInfo(appSetting));
//else
//XmlConfigurator.Configure();
}
// ///<summary>
// /// Returns a logger for the type specified
// ///</summary>
// ///<typeparam name="T"></typeparam>
// ///<returns></returns>
// public static ILog LoggerFor<T>()
// {
// return LogManager.GetLogger(typeof(T));
// }
///<summary>
/// Returns a logger for the type specified
///</summary>
///<typeparam name="T"></typeparam>
///<returns></returns>
public static ILog LoggerFor<T>()
{
return LogManager.GetLogger(typeof(T));
}
// /// <summary>
// /// Returns a logger for the object's type
// /// </summary>
// /// <param name="getTypeFromInstance"></param>
// /// <returns></returns>
// public static ILog LoggerFor(object getTypeFromInstance)
// {
// Mandate.ParameterNotNull(getTypeFromInstance, "getTypeFromInstance");
// return LogManager.GetLogger(getTypeFromInstance.GetType());
// }
/// <summary>
/// Returns a logger for the object's type
/// </summary>
/// <param name="getTypeFromInstance"></param>
/// <returns></returns>
public static ILog LoggerFor(object getTypeFromInstance)
{
if (getTypeFromInstance == null) throw new ArgumentNullException("getTypeFromInstance");
return LogManager.GetLogger(getTypeFromInstance.GetType());
}
// /// <summary>
// /// Adds an error log
// /// </summary>
// /// <typeparam name="T"></typeparam>
// /// <param name="message"></param>
// /// <param name="exception"></param>
// public static void Error<T>(string message, Exception exception)
// {
// var logger = LoggerFor<T>();
// if (logger != null)
// logger.Error(PrefixThreadId(message), exception);
// }
/// <summary>
/// Adds an error log
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="message"></param>
/// <param name="exception"></param>
public static void Error<T>(string message, Exception exception)
{
var logger = LoggerFor<T>();
if (logger != null)
logger.Error(PrefixThreadId(message), exception);
}
// /// <summary>
// /// Traces a message, only generating the message if tracing is actually enabled. Use this method to avoid calling any long-running methods such as "ToDebugString" if logging is disabled.
// /// </summary>
// /// <typeparam name="T"></typeparam>
// /// <param name="generateMessageFormat">The generate message format.</param>
// /// <param name="formatItems">The format items.</param>
// /// <remarks></remarks>
// public static void TraceIfEnabled<T>(string generateMessageFormat, params Func<object>[] formatItems)
// {
// var logger = LoggerFor<T>();
// if (logger == null || !logger.IsInfoEnabled) return;
// var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
// logger.InfoFormat(PrefixThreadId(generateMessageFormat), executedParams);
// }
// /// <summary>
// /// Useful if the logger itself is running on another thread
// /// </summary>
// /// <param name="generateMessageFormat"></param>
// /// <returns></returns>
// private static string PrefixThreadId(string generateMessageFormat)
// {
// return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat;
// }
/// <summary>
/// Useful if the logger itself is running on another thread
/// </summary>
/// <param name="generateMessageFormat"></param>
/// <returns></returns>
private static string PrefixThreadId(string generateMessageFormat)
{
return "[Thread " + Thread.CurrentThread.ManagedThreadId + "] " + generateMessageFormat;
}
// /// <summary>
// /// Traces a message, only generating the message if tracing is actually enabled. Use this method to avoid calling any long-running methods such as "ToDebugString" if logging is disabled.
// /// </summary>
// /// <typeparam name="T"></typeparam>
// /// <param name="generateMessage">The delegate to generate a message.</param>
// /// <remarks></remarks>
// public static void TraceIfEnabled<T>(Func<string> generateMessage)
// {
// TraceIfEnabled(typeof(T), generateMessage);
// }
public static void Warn(Type callingType, string message)
{
var logger = LogManager.GetLogger(callingType);
if (logger != null)
logger.Warn(PrefixThreadId(message));
}
// public static void TraceIfEnabled(Type callingType, Func<string> generateMessage)
// {
// var logger = LogManager.GetLogger(callingType);
// if (logger == null || !logger.IsInfoEnabled) return;
// logger.Info(PrefixThreadId(generateMessage.Invoke()));
// }
public static void Warn(Type callingType, string message, params object[] format)
{
var logger = LogManager.GetLogger(callingType);
if (logger != null)
logger.WarnFormat(PrefixThreadId(message), format);
}
// public static void Warn(Type callingType, string message)
// {
// var logger = LogManager.GetLogger(callingType);
// if (logger != null)
// logger.Warn(PrefixThreadId(message));
// }
/// <summary>
/// Adds a warn log
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="message"></param>
public static void Warn<T>(string message)
{
var logger = LoggerFor<T>();
if (logger != null)
logger.Warn(PrefixThreadId(message));
}
// public static void Warn(Type callingType, string message, params object[] format)
// {
// var logger = LogManager.GetLogger(callingType);
// if (logger != null)
// logger.WarnFormat(PrefixThreadId(message), format);
// }
/// <summary>
/// Adds a warn log
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="format"></param>
/// <param name="items"></param>
public static void Warn<T>(string format, params object[] items)
{
var logger = LoggerFor<T>();
if (logger != null)
logger.WarnFormat(PrefixThreadId(format), items);
}
// /// <summary>
// /// Adds a warn log
// /// </summary>
// /// <typeparam name="T"></typeparam>
// /// <param name="message"></param>
// public static void Warn<T>(string message)
// {
// var logger = LoggerFor<T>();
// if (logger != null)
// logger.Warn(PrefixThreadId(message));
// }
/// <summary>
/// Traces a message, only generating the message if tracing is actually enabled. Use this method to avoid calling any long-running methods such as "ToDebugString" if logging is disabled.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="generateMessage">The delegate to generate a message.</param>
/// <remarks></remarks>
public static void Info<T>(Func<string> generateMessage)
{
Info(typeof(T), generateMessage);
}
// /// <summary>
// /// Adds a warn log
// /// </summary>
// /// <typeparam name="T"></typeparam>
// /// <param name="format"></param>
// /// <param name="items"></param>
// public static void Warn<T>(string format, params object[] items)
// {
// var logger = LoggerFor<T>();
// if (logger != null)
// logger.WarnFormat(PrefixThreadId(format), items);
// }
public static void Info(Type callingType, Func<string> generateMessage)
{
var logger = LogManager.GetLogger(callingType);
if (logger == null || !logger.IsInfoEnabled) return;
logger.Info(PrefixThreadId(generateMessage.Invoke()));
}
// /// <summary>
// /// Traces if tracing is enabled.
// /// </summary>
// /// <param name="type">The type for the logging namespace.</param>
// /// <param name="generateMessageFormat">The message format.</param>
// /// <param name="formatItems">The format items.</param>
// public static void TraceIfEnabled(Type type, string generateMessageFormat, params Func<object>[] formatItems)
// {
// var logger = LogManager.GetLogger(type);
// if (logger == null || !logger.IsInfoEnabled) return;
// var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
// logger.InfoFormat(PrefixThreadId(generateMessageFormat), executedParams);
// }
// }
//}
/// <summary>
/// Traces if tracing is enabled.
/// </summary>
/// <param name="type">The type for the logging namespace.</param>
/// <param name="generateMessageFormat">The message format.</param>
/// <param name="formatItems">The format items.</param>
public static void Info(Type type, string generateMessageFormat, params Func<object>[] formatItems)
{
var logger = LogManager.GetLogger(type);
if (logger == null || !logger.IsInfoEnabled) return;
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
logger.InfoFormat(PrefixThreadId(generateMessageFormat), executedParams);
}
/// <summary>
/// Traces a message, only generating the message if tracing is actually enabled. Use this method to avoid calling any long-running methods such as "ToDebugString" if logging is disabled.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="generateMessageFormat">The generate message format.</param>
/// <param name="formatItems">The format items.</param>
/// <remarks></remarks>
public static void Info<T>(string generateMessageFormat, params Func<object>[] formatItems)
{
Info(typeof (T), generateMessageFormat, formatItems);
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Threading.Tasks;
namespace Umbraco.Core.Logging
{
internal static class LoggingTaskExtension
{
/// <summary>
/// This task shouldn't be waited on (as it's not guaranteed to run), and you shouldn't wait on the parent task either (because it might throw an
/// exception that doesn't get handled). If you want to be waiting on something, use LogErrorsWaitable instead.
///
/// None of these methods are suitable for tasks that return a value. If you're wanting a result, you should probably be handling
/// errors yourself.
/// </summary>
public static Task LogErrors(this Task task, Action<string, Exception> logMethod)
{
return task.ContinueWith(t => LogErrorsInner(t, logMethod), TaskContinuationOptions.OnlyOnFaulted);
}
/// <summary>
/// This task can be waited on (as it's guaranteed to run), and you should wait on this rather than the parent task. Because it's
/// guaranteed to run, it may be slower than using LogErrors, and you should consider using that method if you don't want to wait.
///
/// None of these methods are suitable for tasks that return a value. If you're wanting a result, you should probably be handling
/// errors yourself.
/// </summary>
public static Task LogErrorsWaitable(this Task task, Action<string, Exception> logMethod)
{
return task.ContinueWith(t => LogErrorsInner(t, logMethod));
}
private static void LogErrorsInner(Task task, Action<string, Exception> logAction)
{
if (task.Exception != null)
{
logAction("Aggregate Exception with " + task.Exception.InnerExceptions.Count + " inner exceptions: ", task.Exception);
foreach (var innerException in task.Exception.InnerExceptions)
{
logAction("Inner exception from aggregate exception: ", innerException);
}
}
}
}
}

View File

@@ -31,6 +31,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.0\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core" />
@@ -54,6 +57,8 @@
<Compile Include="IO\SystemDirectories.cs" />
<Compile Include="IO\SystemFiles.cs" />
<Compile Include="LambdaExpressionCacheKey.cs" />
<Compile Include="Logging\AsynchronousRollingFileAppender.cs" />
<Compile Include="Logging\LoggingTaskExtension.cs" />
<Compile Include="Logging\LogHelper.cs" />
<Compile Include="RazorDataTypeModelStaticMappingItem.cs" />
<Compile Include="Resolving\ManyWeightedResolved.cs" />
@@ -81,6 +86,9 @@
<Compile Include="WriteLock.cs" />
<Compile Include="XmlHelper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.0" targetFramework="net40" />
</packages>

View File

@@ -14,3 +14,5 @@ using System.Runtime.CompilerServices;
//tg forcing .NET 2.0 security rules, since otherwise it wasn't possible to run in medium trust
//(got an inheritance security rules violated by type error)
[assembly: System.Security.SecurityRules(System.Security.SecurityRuleSet.Level1)]
[assembly: log4net.Config.XmlConfigurator(Watch = true)]

View File

@@ -113,6 +113,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="log4net">
<HintPath>..\packages\log4net.2.0.0\lib\net40-full\log4net.dll</HintPath>
</Reference>
<Reference Include="Lucene.Net, Version=2.9.4.1, Culture=neutral, PublicKeyToken=85089178b9ac3181, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\Lucene.Net.dll</HintPath>
@@ -264,6 +267,10 @@
<None Include="config\ClientDependency.Release.config">
<DependentUpon>ClientDependency.config</DependentUpon>
</None>
<Content Include="config\log4net.config" />
<Content Include="config\log4net.Release.config">
<DependentUpon>log4net.config</DependentUpon>
</Content>
<None Include="config\xsltExtensions.Release.config">
<DependentUpon>xsltExtensions.config</DependentUpon>
</None>
@@ -307,6 +314,7 @@
<None Include="config\Dashboard.Release.config">
<DependentUpon>Dashboard.config</DependentUpon>
</None>
<Content Include="packages.config" />
<None Include="umbraco\config\create\UI.Release.xml">
<DependentUpon>UI.xml</DependentUpon>
</None>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<log4net>
<root>
<priority value="Info"/>
<appender-ref ref="AsynchronousLog4NetAppender" />
</root>
<!--To Change the way logging works for certain namespaces, insert a new logger like: -->
<!--
<logger name="Umbraco.Core">
<level value="WARN" />
</logger>-->
<appender name="AsynchronousLog4NetAppender" type="Umbraco.Core.Logging.AsynchronousRollingFileAppender, Umbraco.Core">
<file value="App_Data\Logs\UmbracoTraceLog.txt" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<maximumFileSize value="5MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0"?>
<log4net>
<root>
<priority value="Info"/>
<appender-ref ref="AsynchronousLog4NetAppender" />
</root>
<!--To Change the way logging works for certain namespaces, insert a new logger like: -->
<!--
<logger name="Umbraco.Core">
<level value="WARN" />
</logger>-->
<appender name="AsynchronousLog4NetAppender" type="Umbraco.Core.Logging.AsynchronousRollingFileAppender, Umbraco.Core">
<file value="App_Data\Logs\UmbracoTraceLog.txt" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<maximumFileSize value="5MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
</layout>
</appender>
</log4net>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.0" targetFramework="net40" />
</packages>

View File

@@ -57,6 +57,8 @@
<add key="enableSimpleMembership" value="false"/>
<add key="autoFormsAuthentication" value="false"/>
<!-- End of added in Umbraco 4.6.2 -->
<add key="log4net.Config" value="config\log4net.config" />
</appSettings>
<system.net>
<mailSettings>

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<repositories>
<repository path="..\Umbraco.Core\packages.config" />
<repository path="..\Umbraco.Tests\packages.config" />
<repository path="..\Umbraco.Web.UI\packages.config" />
</repositories>