/*
<File>
<Copyright>Copyright © 2007, Daniel Vaughan. All rights reserved.</Copyright>
<License see="prj:///Documentation/License.txt"/>
<Owner Name="Daniel Vaughan" Email="dbvaughan@gmail.com"/>
<CreationDate>2007/11/19 20:00</CreationDate>
<LastSubmissionDate>$Date: $</LastSubmissionDate>
<Version>$Revision: $</Version>
</File>
*/
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Security;
using System.Threading;
using System.Web.Management;
using System.Web.Security;
using System.Xml;
using DanielVaughan.Logging.Configuration;
using DanielVaughan.Logging.LogEntries;
namespace DanielVaughan.Logging
{
/// <summary>
/// The main application log.
/// Use this static class to write to a
/// <see cref="ILogStrategy"/> defined in the current
/// <see cref="ILogStrategyProvider"/>.
/// </summary>
public static partial class Log
{
// static bool useAspNetMembership;
// static bool partialTrust = true;
static Log()
{
SkipFrameCount = 4;
try
{
InitFromConfig();
}
catch (Exception ex)
{
if (InternalLog.FatalEnabled)
{
InternalLog.Fatal("Unable to init from config.", ex);
}
}
}
public static int SkipFrameCount { get; set; }
static bool ValidateFilters(ILogStrategy logStrategy, LogEntryOrigin origin, IClientInfo entry)
{
ArgumentValidator.AssertNotNull(logStrategy, "logStrategy");
ArgumentValidator.AssertNotNull(entry, "entry");
var filters = LogRepository.Instance.GetFilters(logStrategy);
foreach (IFilter filter in filters)
{
if (!filter.IsValid(origin, entry))
{
return false;
}
}
return true;
}
internal static LogLevel GetLogLevel(IClientInfo info)
{
LogLevel minumLevel = LogLevel.None;
foreach (var logStrategy in LogRepository.Instance.LogStrategies)
{
var level = logStrategy.GetLogLevel(info);
if (level < minumLevel)
{
minumLevel = level;
}
}
return minumLevel;
}
internal static void LogMessage(IClientLogEntry logEntry)
{
/* TODO: use parallel foreach. */
foreach (var logStrategy in LogRepository.Instance.LogStrategies)
{
if (IsEnabled(logStrategy, LogEntryOrigin.Remote, logEntry))
{
logStrategy.Write(logEntry);
}
}
}
/// <summary>
/// Loads the providers.
/// </summary>
static void InitFromConfig()
{
InternalLog.Debug("Initializing from config."); /* TODO: Make localizable resource. */
var configurationElement = ConfigurationManager.GetSection("Clog") as XmlElement;
if (configurationElement != null)
{
var logLevelAttribute = configurationElement.Attributes["InternalLogLevel"];
LogLevel logLevel = LogLevel.None;
bool configurationInvalid = false;
if (logLevelAttribute != null && !string.IsNullOrEmpty(logLevelAttribute.Value))
{
try
{
logLevel = (LogLevel)Enum.Parse(typeof(LogLevel), logLevelAttribute.Value);
}
catch (Exception ex)
{
configurationInvalid = true;
InternalLog.Error("InternalLogLevel attribute is invalid.", ex); /* TODO: Make localizable resource. */
}
}
InternalLog.LogLevel = logLevel;
var skipFrameCountAttribute = configurationElement.Attributes["SkipFrameCount"];
if (skipFrameCountAttribute != null && !string.IsNullOrEmpty(skipFrameCountAttribute.Value))
{
int frameCount;
if (int.TryParse(skipFrameCountAttribute.Value, out frameCount))
{
SkipFrameCount = frameCount;
}
else
{
configurationInvalid = true;
InternalLog.Error("SkipFrameCount attribute is invalid. Value should be an integer. Attribute value: "
+ skipFrameCountAttribute.Value);
}
}
LogRepository.Instance.Load(configurationElement);
InternalLog.Info(string.Format("Clog configuration loaded {0}.",
configurationInvalid ? "with errors" : "successfully")); /* TODO: Make localizable resource. */
}
else
{
InternalLog.Info("Clog config section not found."); /* TODO: Make localizable resource. */
}
// partialTrust = !IsPermissionGranted(new SecurityPermission(PermissionState.Unrestricted));
// section = (ClientLoggingConfigSection)WebConfigurationManager.GetSection("ClientLogging");
// useAspNetMembership = section.UseAspNetMembership && partialTrust;
}
static bool IsPermissionGranted(IPermission requestedPermission)
{
try
{
/* Try and get this permission. */
requestedPermission.Demand();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Determines whether logging to the specified origin is enabled.
/// Checks whether the current provider is null
/// and if not, then whether all filters are validate.
/// Each <see cref="IFilter"/> makes use of the origin
/// parameter and decides whether it is, itself, valid.
/// If any filters are not valid, then this method
/// returns <code>false</code>.
/// </summary>
/// <param name="logStrategy">The log strategy to test for whether it is enabled.</param>
/// <param name="origin">The origin.</param>
/// <param name="info">Client information used during evaluation.</param>
/// <returns>
/// <c>true</c> if logging is enable; otherwise, <c>false</c>.
/// </returns>
public static bool IsEnabled(ILogStrategy logStrategy, LogEntryOrigin origin, IClientInfo info)
{
try
{
return IsEnabledAux(logStrategy, origin, info);
}
catch (Exception ex)
{
InternalLog.Error("Error determining enabled status of log.", ex);
}
return false;
}
public static bool IsEnabled(LogEntryOrigin origin, IClientInfo info)
{
foreach (var logStrategy in LogRepository.Instance.LogStrategies)
{
if (IsEnabledAux(logStrategy, origin, info))
{
/* If even one strategy is enabled, then logging is enabled. */
return true;
}
}
return false;
}
static bool IsEnabledAux(ILogStrategy logStrategy, LogEntryOrigin origin, IClientInfo info)
{
return ValidateFilters(logStrategy, origin, info);
}
static bool IsLevelEnabled(LogLevel logLevel)
{
try
{
return IsLevelEnabledAux(logLevel);
}
catch (Exception ex)
{
InternalLog.Error("IsLevelEnabledAux raised exception.", ex);
}
return false;
}
static bool IsLevelEnabledAux(LogLevel logLevel)
{
foreach (var logStrategy in LogRepository.Instance.LogStrategies)
{
LogLevel strategyLevel;
try
{
strategyLevel = logStrategy.GetLogLevel(null);
}
catch (Exception ex)
{
InternalLog.Error("Problem evaluating LogStrategy LogLevel.", ex);
continue;
}
if (strategyLevel < logLevel)
{
return true;
}
}
return false;
}
static void WriteLogEntry(LogLevel logLevel, string message, Exception exception, IDictionary<string, object> properties)
{
try
{
WriteLogEntryAux(logLevel, message, exception, properties);
}
catch (Exception ex)
{
if (InternalLog.ErrorEnabled)
{
InternalLog.Error("Error writing log.", ex);
}
}
}
static void WriteLogEntryAux(LogLevel logLevel, string message, Exception exception, IDictionary<string, object> properties)
{
var codeLocation = GetCallerLocation(SkipFrameCount);
var entry = new ServerLogEntry(logLevel, message)
{
CodeLocation = codeLocation,
LogName = codeLocation.ClassName,
MachineName = SafeEnvironmentValues.MachineName,
UserName = SafeEnvironmentValues.UserName,
OccuredAt = DateTime.Now,
Properties = properties
};
PopulateMembershipInfo(entry);
bool eventRaised = false;
object eventRaisedLock = new object();
foreach (var logStrategy in LogRepository.Instance.LogStrategies)
{
try
{
if (!IsEnabled(logStrategy, LogEntryOrigin.Local, entry))
{
continue;
}
}
catch (Exception ex)
{
InternalLog.Error(string.Format("Log strategy {0} failed on call to IsEnabled.", logStrategy), ex);
continue;
}
if (exception != null)
{
entry.Exception = exception;
}
ILogStrategy logStrategy1 = logStrategy;
ThreadPool.QueueUserWorkItem(delegate {
try
{
logStrategy1.Write(entry);
if (!eventRaised)
{
lock (eventRaisedLock)
{
if (!eventRaised)
{
OnLogEntrySent(new LogEventArgs(entry));
eventRaised = true;
}
}
}
}
catch (Exception ex)
{
if (InternalLog.ErrorEnabled)
{
InternalLog.Error("Problem writing log to ILogStrategy " + logStrategy1.GetType(), ex);
}
}
});
}
}
static bool membershipEnabled;
static void PopulateMembershipInfo(LogEntryData data)
{
if (/*useAspNetMembership && */membershipEnabled && string.IsNullOrEmpty(data.UserName))
{ /* Populate the UserName property using the Membership provider. */
MembershipUser user = null;
string errorMessage = null;
try
{
user = Membership.GetUser(true);
}
catch (Exception ex)
{
if (ex is SqlExecutionException
|| ex is SecurityException
|| ex.InnerException is SqlExecutionException)
{
membershipEnabled = false;
/* TODO: Make localizable resource. */
errorMessage = string.Format("Unable to user ASP.NET Membership to get username. Will not try again. Exception: {0}", ex);
}
else
{
/* TODO: Make localizable resource. */
errorMessage = string.Format("Unable to user ASP.NET Membership to get username. Exception: {0}", ex);
}
//System.Diagnostics.Debug.WriteLine(errorMessage, traceCategory);
}
if (!string.IsNullOrEmpty(errorMessage))
{
Info(errorMessage);
}
if (user != null)
{
data.UserName = user.UserName;
}
}
}
/* TODO: Make an extensibility point for GetCallerLocation. */
/// <summary>
/// Gets the caller location from the <see cref="StackTrace"/>.
/// </summary>
/// <returns>The code location that the call to log originated.</returns>
static CodeLocation GetCallerLocation(int methodCallCount)
{
string className;
string methodName;
string fileName;
int lineNumber;
var stackTrace = new StackTrace(methodCallCount, true);
var stackFrame = stackTrace.GetFrame(0);
if (stackFrame != null)
{
className = stackFrame.GetMethod().ReflectedType.FullName;
methodName = stackFrame.GetMethod().Name;
fileName = stackFrame.GetFileName();
lineNumber = stackFrame.GetFileLineNumber();
}
else
{
className = string.Empty;
methodName = string.Empty;
fileName = string.Empty;
lineNumber = -1;
}
CodeLocation callerLocation = new CodeLocation
{
ClassName = className,
MethodName = methodName,
FileName = fileName,
LineNumber = lineNumber
};
return callerLocation;
}
#region Write event
static event EventHandler<LogEventArgs> write;
/// <summary>
/// Occurs when the active <see cref="ILogStrategy"/>
/// is called upon to write a log entry.
/// </summary>
public static event EventHandler<LogEventArgs> Write
{
add
{
write += value;
}
remove
{
write -= value;
}
}
static void OnLogEntrySent(LogEventArgs e)
{
if (write != null)
{
write(null, e);
}
}
#endregion
}
}