/*
<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.Diagnostics;
using System.Threading;
using System.Windows.Browser;
using Orpius.Logging.ClientLogging;
namespace Orpius.Logging
{
/// <summary>
/// The default implementation of an <see cref="ILog"/>.
/// </summary>
class Log : ILog
{
readonly Type hostType;
readonly string name;
JsonLoggingService loggingServiceField;
readonly object loggingServiceLock = new object();
ClientConfigurationData clientConfig;
readonly object loggingConfigLock = new object();
static Uri pageUri = HtmlPage.DocumentUri;
#region event InternalError
event EventHandler<InternalErrorEventArgs> internalError;
public event EventHandler<InternalErrorEventArgs> InternalError
{
add
{
internalError += value;
}
remove
{
internalError -= value;
}
}
protected void OnInternalError(InternalErrorEventArgs e)
{
if (internalError != null)
{
internalError(this, e);
}
}
#endregion
public string Name
{
get
{
if (hostType != null)
{
return GetName(hostType);
}
return name;
}
}
/// <summary>
/// Gets the name for the log using the specified hostType.
/// <seealso cref="Log.GetUrlForLogName"/>
/// </summary>
/// <param name="hostType">Type of the host where the log is located.</param>
/// <returns>The name of the log.</returns>
public static string GetName(Type hostType)
{
return string.Format("{0},{1}", hostType.FullName, GetUrlForLogName());
}
/// <summary>
/// Gets the name of the URL for the log data.
/// If we are logging from localhost then we do
/// not use the port number, because it is assumed
/// that we are debugging.
/// </summary>
/// <returns>The url of the page without the querystring.</returns>
static string GetUrlForLogName()
{
string result;
// Uri uri;
// try
// {
// uri = HtmlPage.DocumentUri;
// }
// catch (InvalidOperationException ex)
// { /* We can't retrieve the document uri
// if the log is in*/
// return string.Empty;
// }
if (pageUri.Host.ToLower() == "localhost" || pageUri.Host.Contains("127.0.0."))
{
result = string.Format("{0}", pageUri.LocalPath);
}
else
{
if (pageUri.Port == 80 || pageUri.Port == 443)
{
result = string.Format("{0}://{1}{2}", pageUri.Scheme, pageUri.Host, pageUri.LocalPath);
}
else
{
result = string.Format("{0}://{1}:{2}{3}", pageUri.Scheme, pageUri.Host, pageUri.Port, pageUri.LocalPath);
}
}
return result.ToLower();
}
const int webserviceTimeout = 15000;
const int webserviceSleep = 500;
ClientConfigurationData GetConfigurationData(ClientInfo clientInfo)
{
if (clientConfig == null
|| clientConfig.RetrievedOn.AddSeconds(clientConfig.ExpiresInSeconds) < DateTime.Now)
{
lock (loggingConfigLock)
{
if (clientConfig == null
|| clientConfig.RetrievedOn.AddSeconds(clientConfig.ExpiresInSeconds) < DateTime.Now)
{
try
{
clientConfig = LoggingService.GetConfiguration(clientInfo);
clientConfig.RetrievedOn = DateTime.Now;
}
catch (InvalidOperationException)
{
clientConfig = null;
SynchronizationContext.Current.Send(delegate
{
clientConfig = LoggingService.GetConfiguration(clientInfo);
clientConfig.RetrievedOn = DateTime.Now;
}, null);
int accumulator = webserviceTimeout;
while (clientConfig == null && accumulator > 0)
{
accumulator -= webserviceSleep;
Thread.Sleep(webserviceSleep);
}
}
}
}
}
return clientConfig;
}
JsonLoggingService LoggingService
{
get
{
if (loggingServiceField == null)
{
lock (loggingServiceLock)
{
if (loggingServiceField == null)
{
loggingServiceField = new JsonLoggingService();
/* TODO: create setting for url of logging service.
* Maybe useful when we have cross domain web service support. */
loggingServiceField.Url = "JsonClogService.asmx";
}
}
}
return loggingServiceField;
}
}
#region LogEntrySent
event EventHandler<LogEventArgs> logEntrySent;
public event EventHandler<LogEventArgs> LogEntrySent
{
add
{
logEntrySent += value;
}
remove
{
logEntrySent -= value;
}
}
protected void OnLogEntrySent(LogEventArgs e)
{
if (logEntrySent != null)
{
logEntrySent(this, e);
}
}
#endregion
#region WriteRequested
event EventHandler<LogEventArgs> logEntrySendAttempt;
public event EventHandler<LogEventArgs> WriteRequested
{
add
{
logEntrySendAttempt += value;
}
remove
{
logEntrySendAttempt -= value;
}
}
protected void OnLogEntrySendAttempt(LogEventArgs e)
{
if (logEntrySendAttempt != null)
{
logEntrySendAttempt(this, e);
}
}
#endregion
public Log(Type hostType)
{
this.hostType = hostType;
}
public Log(string name)
{
this.name = name;
}
#region Logging Overloads
public void Debug(string message)
{
WriteLogEntry(LogLevel.Debug, message, null);
}
public void Debug(string message, Exception exception)
{
WriteLogEntry(LogLevel.Debug, message, exception);
}
public void Info(string message)
{
WriteLogEntry(LogLevel.Info, message, null);
}
public void Info(string message, Exception exception)
{
WriteLogEntry(LogLevel.Info, message, exception);
}
public void Warn(string message)
{
WriteLogEntry(LogLevel.Warn, message, null);
}
public void Warn(string message, Exception exception)
{
WriteLogEntry(LogLevel.Warn, message, exception);
}
public void Error(string message)
{
WriteLogEntry(LogLevel.Error, message, null);
}
public void Error(string message, Exception exception)
{
WriteLogEntry(LogLevel.Error, message, exception);
}
public void Fatal(string message)
{
WriteLogEntry(LogLevel.Fatal, message, null);
}
public void Fatal(string message, Exception exception)
{
WriteLogEntry(LogLevel.Fatal, message, exception);
}
#endregion
void WriteLogEntry(LogLevel logLevel, string message, Exception exception)
{
try
{
WriteLogEntryAux(logLevel, message, exception);
}
catch (Exception ex)
{
string errorMessage = string.Format("Unable to write to log: {0} {1}{2}",
ex, Environment.NewLine, ex.StackTrace);
Console.WriteLine(errorMessage);
OnInternalError(new InternalErrorEventArgs(ex));
}
}
void WriteLogEntryAux(LogLevel logLevel, string message, Exception exception)
{
ExceptionMemento memento = null;
if (exception != null)
{
memento = CreateMemento(exception);
}
LogEntryData entry = new LogEntryData()
{
LogLevel = logLevel,
Message = message,
ExceptionMemento = memento,
CodeLocation = GetLocation(),
LogName = Name,
ThreadName = System.Threading.Thread.CurrentThread.Name,
ManagedThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId,
Url = pageUri.ToString(),
OccuredAt = DateTime.Now
};
OnLogEntrySendAttempt(new LogEventArgs(entry));
ClientInfo info = new ClientInfo();
info.LogName = entry.LogName;
info.MachineName = entry.MachineName;
info.Url = entry.Url;
info.UserName = entry.UserName;
ClientConfigurationData config = GetConfigurationData(info);
if (config == null || !config.LogEnabled
/* Enable enum and remove cast
* when Silverlight supports Enum serialization. */
|| (int)entry.LogLevel < config.LogLevel)
{
return;
}
/* Send of the log entry to the web service. */
try
{
LoggingService.BeginWriteEntry(entry, delegate(IAsyncResult result)
{
OnLogEntrySent(new LogEventArgs(entry));
}, null);
}
catch (InvalidOperationException)
{
SynchronizationContext.Current.Send(delegate
{
LoggingService.BeginWriteEntry(entry, delegate(IAsyncResult result)
{
OnLogEntrySent(new LogEventArgs(entry));
}, null);
}, null);
}
}
static ExceptionMemento CreateMemento(Exception exception)
{
ExceptionMemento memento = new ExceptionMemento()
{
TypeName = exception.GetType().AssemblyQualifiedName,
Message = exception.Message,
Source = string.Empty,//exception.Source, No Source property in Silverlight BCL.
StackTrace = exception.StackTrace ?? string.Empty,
HelpLink = string.Empty
};
return memento;
}
CodeLocation GetLocation()
{
CodeLocation location = new CodeLocation()
{
ClassName = hostType != null ? hostType.AssemblyQualifiedName : string.Empty,
MethodName = string.Empty,
FileName = string.Empty,
LineNumber = -1
};
return location;
}
/// <summary>
/// Gets the caller location.
/// Throws MethodAccessException.
/// We can't do this in Silverlight, so don't use it.
/// Left here for your amusement.
/// </summary>
/// <returns>The source code location that the call
/// to log was made.</returns>
static CodeLocation GetCallerLocation()
{
StackTrace trace;
string className;
string methodName;
string fileName;
int lineNumber;
StackFrame frame = null;
try
{
trace = new System.Diagnostics.StackTrace(true);
frame = trace.GetFrame(2);
}
catch (MethodAccessException ex)
{
string stackTrace = ex.StackTrace;
}
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 = 0;
}
CodeLocation location = new CodeLocation()
{
ClassName = className,
MethodName = methodName,
FileName = fileName,
LineNumber = lineNumber
};
return location;
}
}
}