/*
<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.Collections.Specialized;
using System.Configuration;
using System.Configuration.Provider;
using System.Diagnostics;
using System.Security;
using System.Security.Permissions;
using System.Web;
using System.Web.Configuration;
using System.Web.Management;
using System.Web.Security;
using Orpius.Logging.Configuration;
using Orpius.Logging.LogEntries;
using Orpius.Logging.LogStrategies;
namespace Orpius.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 class Log
{
static ILogStrategyProvider provider;
static LogStrategyProviderCollection providers;
static ClientLoggingConfigSection section;
static readonly List<IFilter> filters = new List<IFilter>();
static bool debugMode;
static bool useAspNetMembership;
const string traceCategory = "Clog";
static bool partialTrust = true;
static Log()
{
InitFromConfig();
BuildFilters();
}
static void BuildFilters()
{
filters.Clear();
foreach (FilterElement element in section.FilterElements)
{
IFilter filter = element.BuildFilter();
filters.Add(filter);
}
}
static bool ValidateFilters(LogEntryOrigin origin, IClientInfo entry)
{
foreach (IFilter filter in filters)
{
if (!filter.IsValid(origin, entry))
{
return false;
}
}
return true;
}
internal static LogLevel GetLogLevel(IClientInfo info)
{
if (provider == null || provider.LogStrategy == null)
{
return LogLevel.None;
}
return provider.LogStrategy.GetLogLevel(info);
}
internal static void LogMessage(IClientLogEntry logEntry)
{
if (IsEnabled(LogEntryOrigin.Remote, logEntry))
{
provider.LogStrategy.Write(logEntry);
}
}
/// <summary>
/// Loads the providers.
/// </summary>
static void InitFromConfig()
{
partialTrust = !IsPermissionGranted(new SecurityPermission(PermissionState.Unrestricted));
section = (ClientLoggingConfigSection)WebConfigurationManager.GetSection("ClientLogging");
debugMode = section.Debug;
useAspNetMembership = section.UseAspNetMembership && partialTrust;
providers = new LogStrategyProviderCollection();
if (!partialTrust)
{
// try
// {
ProvidersHelper.InstantiateProviders(
section.Providers, providers, typeof(ILogStrategyProvider));
// partialTrust = false;
// }
// catch (SecurityException ex)
// {
// /* We're in a minimal trust environment.
// * There is a demand on ProvidersHelper.InstantiateProviders
// * for AspNetHostingPermissionLevel.Low that will fail
// * in a partial trust WPF Browser application. */
// WriteDebugTrace(string.Format("ProvidersHelper.InstantiateProviders threw exception."
// + " Assuming low trust internet client environment. \n{0}\n{1}",
// ex.Message, ex.StackTrace));
// }
}
if (partialTrust)
{ /* We will assume we are in a sandbox and do things manually. */
providers = new LogStrategyProviderCollection();
ProviderSettings providerSettings = section.Providers[section.DefaultProvider];
Type type = Type.GetType(providerSettings.Type);
ProviderBase tempProvider = (ProviderBase)Activator.CreateInstance(type);
tempProvider.Initialize("ClientProvider",
new NameValueCollection() {{"LogStrategy", typeof(ClientStrategy).AssemblyQualifiedName} });
provider = (ILogStrategyProvider)tempProvider;
providers.Add(tempProvider);
}
else
{
provider = providers[section.DefaultProvider];
}
if (provider == null)
{
throw new ProviderException("Unable to load default ILogProvider");
}
if (provider.LogStrategy is IClientStrategy)
{
/* We disable ASP.NET Membership use for client strategies. */
useAspNetMembership = false;
}
}
static bool IsPermissionGranted(IPermission requestedPermission)
{
try
{
/* Try and get this permission. */
requestedPermission.Demand();
return true;
}
catch
{
return false;
}
}
internal static void WriteDebugTrace(string message)
{
if (section == null || !section.Debug)
{
return;
}
Trace.WriteLine(message, traceCategory);
}
/// <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="origin">The origin.</param>
/// <returns>
/// <c>true</c> if logging is enable; otherwise, <c>false</c>.
/// </returns>
public static bool IsEnabled(LogEntryOrigin origin, IClientInfo info)
{
try
{
return IsEnabledAux(origin, info);
}
catch (Exception ex)
{
DebugError("Error determining enabled status of log.", ex);
}
return false;
}
static bool IsEnabledAux(LogEntryOrigin origin, IClientInfo info)
{
return provider != null && ValidateFilters(origin, info);
}
#region Log writing overloads.
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Debug"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
public static void Debug(string message)
{
WriteLogEntry(LogLevel.Debug, message, null);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Debug"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="exception">The exception that this message
/// is regarding.</param>
public static void Debug(string message, Exception exception)
{
WriteLogEntry(LogLevel.Debug, message, exception);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Info"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
public static void Info(string message)
{
WriteLogEntry(LogLevel.Info, message, null);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Info"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="exception">The exception that this message
/// is regarding.</param>
public static void Info(string message, Exception exception)
{
WriteLogEntry(LogLevel.Info, message, exception);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Warn"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
public static void Warn(string message)
{
WriteLogEntry(LogLevel.Warn, message, null);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Warn"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="exception">The exception that this message
/// is regarding.</param>
public static void Warn(string message, Exception exception)
{
WriteLogEntry(LogLevel.Warn, message, exception);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Error"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
public static void Error(string message)
{
WriteLogEntry(LogLevel.Error, message, null);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Error"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="exception">The exception that this message
/// is regarding.</param>
public static void Error(string message, Exception exception)
{
WriteLogEntry(LogLevel.Error, message, exception);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Fatal"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
public static void Fatal(string message)
{
WriteLogEntry(LogLevel.Fatal, message, null);
}
/// <summary>
/// Writes a log entry at the <see cref="LogLevel.Fatal"/>
/// level to the active <see cref="ILogStrategy"/>.
/// </summary>
/// <param name="message">The message to write.</param>
/// <param name="exception">The exception that this message
/// is regarding.</param>
public static void Fatal(string message, Exception exception)
{
WriteLogEntry(LogLevel.Fatal, message, exception);
}
#endregion
static void DebugError(string message, Exception exception)
{
if (debugMode)
{
string errorMessage = message;
if (exception != null)
{
errorMessage = string.Format("Clog: {0}\n{1} {2}",
message, exception.Message, exception.StackTrace);
}
Trace.WriteLine(errorMessage, traceCategory);
System.Diagnostics.Debug.Fail(errorMessage);
}
}
static void WriteLogEntry(LogLevel logLevel, string message, Exception exception)
{
try
{
WriteLogEntryAux(logLevel, message, exception);
}
catch (Exception ex)
{
DebugError("Error writing log.", ex);
}
}
static void WriteLogEntryAux(LogLevel logLevel, string message, Exception exception)
{
CodeLocation location = GetCallerLocation(4);
LogEntryData info = new LogEntryData();
info.LogName = location.ClassName;
info.MachineName = SafeEnvironmentValues.MachineName;
info.UserName = SafeEnvironmentValues.UserName;
PopulateMembershipInfo(info);
if (IsEnabled(LogEntryOrigin.Local, info))
{
ServerLogEntry entry = new ServerLogEntry(logLevel, message)
{
CodeLocation = location,
OccuredAt = DateTime.Now,
LogName = location.ClassName
};
if (exception != null)
{
entry.Exception = exception;
}
provider.LogStrategy.Write(entry);
OnLogEntrySent(new LogEventArgs(entry));
}
}
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;
errorMessage = string.Format("Unable to user ASP.NET Membership to get username. Will not try again. Exception: {0}", ex);
}
else
{
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;
}
}
}
/// <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)
{
StackTrace trace;
string className;
string methodName;
string fileName;
int lineNumber;
StackFrame frame = null;
try
{
trace = new StackTrace(methodCallCount, true);
frame = trace.GetFrame(0);
}
catch (MethodAccessException ex)
{
System.Diagnostics.Debug.Fail("Unable to get stack trace." + ex);
}
if (frame != null)
{
className = frame.GetMethod().ReflectedType.FullName;
methodName = frame.GetMethod().Name;
fileName = frame.GetFileName();
lineNumber = frame.GetFileLineNumber();
}
else
{
className = string.Empty;
methodName = string.Empty;
fileName = string.Empty;
lineNumber = -1;
}
CodeLocation location = new CodeLocation()
{
ClassName = className,
MethodName = methodName,
FileName = fileName,
LineNumber = lineNumber
};
return location;
}
#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
}
}