|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Please visit the CodePlex Project Site for the latest releases and source code.
Contents
IntroductionSo you've deployed your swanky new Silverlight application to test. But wait! There's a problem. Your testers tell you that it breaks when they click the orange button! They send you a multitude of screen shots, but to no avail! It appears unresolvable, and with deadlines looming, it's crunch time. With no hope in site, desperation sets in. You raise your fists in the air and exclaim "If only I could know what was happening on the client side!". Enter Clog. Ok, the previous hypothetical scenario is melodramatic, but it highlights the need for an integrated client side logging solution. Thus I decided to create Clog. Clog is a log provider system that allows you to harness your existing logging system to log client side messages to your server. It is fully customizable, can serialize and log all
Figure: Consuming Clog from a web client application.
This article will discuss how Clog works, how to set up Clog on both client and server, including configuration of the Silverlight Log Viewer control, an example use of the .NET provider model, and also some more advanced topics such as the Silverlight security model. Although this article's client side focus is primarily on a Silverlight implementation, Clog is capable of providing logging services to any .NET or web service consumer. Thus, in future articles, I intend to provide editions for WPF and ASP.NET AJAX. BackgroundA solid server side logging system is the mainstay of most web based applications. With the advent of WPF, Silverlight, and a client side CLR, it is my view that we will see a shift in focus away from the traditional and primarily server based logging scenario, to cater to a more client centric environment. While Visual Studio allows us to readily debug Silverlight and WPF, without the debugger or some means of tracing client side events, we can find ourselves left in the dark. We have no built-in mechanism for logging to e.g., an event log on a client machine, and we lack an immediate feedback mechanism that could allow us to know if our client side .NET applications are behaving correctly. Clog bridges this client-server divide. We are now able to selectively capture logging events that originate from both client and server side applications. I recall, some years ago, my first experiences while hand coding form validation JavaScript, and trying to provide myself with client side feedback. It was, and still is, common practice to use message boxes for displaying information while scripting. It's a fairly slipshod and haphazard approach, and also potentially embarrassing if you should forget to comment out the code when you're through! Today there are one or two Ajax JavaScript client to server logging projects out there. And while they may work well with loosely typed JavaScript, they address a different need. There are numerous logging libraries for .NET, and many of us have come to know and rely on a particular system over time; sometimes customizing it to suit our own requirements. Clog allows us to keep our existing system, by wrapping it; allowing us to perform both client and server logging in the same manner.
Figure: Local and remote clients consuming Clog.
Clog System OverviewClog's core component is the Orpius.Logging.dll. It provides for most of the server side functionality. Silverlight logging functionality is located in Orpius.Logging.Silverlight.dll, and auxiliary to this is the optional component Orpius.Logging.Silverlight.UI.dll, which contains the
Figure: Clog Component Diagram.
Using ClogTo enable Clog for client side logging we complete a two stage process. First we configure the server based project to use Clog. Then we configure our client side project to use Clog. Server Side ConfigurationTo enable Clog on the server side, add a reference to the <section name="ClientLogging"
type="Orpius.Logging.ClientLoggingConfigSection"/>
Next, create the <ClientLogging defaultProvider="Log4NetProvider">
<providers>
<clear />
<add name="Log4NetProvider"
type="Orpius.Logging.LogStrategyProvider, Orpius.Logging"
LogStrategy="Orpius.Logging.Log4NetStrategy,
Orpius.Logging.Log4NetLogStrategy" />
<add name="CustomProvider"
type="Orpius.Logging.LogStrategyProvider, Orpius.Logging"
LogStrategy="ExampleWebsite.SimpleLogStrategy, ExampleWebsite" />
</providers>
<filters>
<add name="IPAddressRange"
type="Orpius.Logging.Filters.IPAddressRangeFilter, Orpius.Logging"
begin="127.0.0.0" end="127.0.0.10"/>
<add name="RoleMembership"
type="Orpius.Logging.Filters.RoleMembershipFilter, Orpius.Logging"
roles="Developer, Administrator"/>
</filters>
</ClientLogging>
Create a new file in your Web project called ClientLoggingService.asmx, open it and paste the following content: <%@ WebService Language="C#" Class="Orpius.Logging.ClientLoggingService,
Orpius.Logging" %>
The web service code is actually located in the Orpius.Logging.dll assembly. It is up to you to define your preferred logging method. For this demonstration we are using log4net. While configuring log4net is outside the scope of this article, you can view the example website download to see how it's done. Briefly, it requires adding an assembly reference to log4net.dll, adding a config section in the web.config, and then initialising log4net within your website. I do this by performing an arbitrary logging request when the application starts. Clog for Silverlight ConfigurationTo use Clog in your Silverlight project, add a reference to the Writing to the Log from SilverlightTo use Clog on the client side, within a Silverlight application, we add a reference to the static readonly ILog log = LogManager.GetLog(typeof(Page));
The
Figure: Client log writing process.
The client-side Silverlight 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 = HtmlPage.DocumentUri.ToString(),
OccuredAt = DateTime.Now
};
OnLogEntrySendAttempt(new LogEventArgs(entry));
if (ConfigurationData == null || !ConfigurationData.LogEnabled
/* Enable enum and remove cast
* when Silverlight supports Enum serialization. */
|| (int)entry.LogLevel < ConfigurationData.LogLevel)
{
return;
}
/* Send of the log entry to the web service. */
LoggingService.BeginWriteEntry(entry, delegate(IAsyncResult result)
{
OnLogEntrySent(new LogEventArgs(entry));
}, null);
}
Silverlight Log ViewerOverviewThe Log Viewer is a Silverlight control that can be placed on a Canvas to automatically monitor the
Figure: Clog Silverlight Log Viewer with Log4Net Viewer.
Using the Log ViewerTo include the Log Viewer in your Silverlight application, add a reference to the xmlns:Orpius="clr-namespace:Orpius.Logging.Silverlight.UI;
assembly=ClientBin/Orpius.Logging.Silverlight.UI.dll"
Then place the <Log:LogViewer x:Name="LogViewer" Canvas.Top="180" Width="640" Height="300"/>
The number of rows displayed in the Inside the Log ViewerSilverlight doesn't have many controls provided with its download yet; the viewer is constructed from the ground up. When a Silverlight application requests the writing of a log entry, two events may be raised by the active
Figure: Log Viewer processing a log entry flowchart.
Silverlight Security ModelSilverlight does not use Code Access Security (CAS). Silverlight uses the transparency model introduced in .NET 2.0. In this model there are three levels: Transparent, Safe Critical, and Critical. In the Silverlight CLR, all code is "Transparent" by default, and, therefore, so is the user code. This is the opposite of the desktop CLR, which is "Critical" by default (.NET Security Blog). Any method decorated with a
Figure: Transparent code cannot call Critical code directly.
The Silverlight To take a look at what methods are available to our user code, fire up Reflector and replace the Framework
Figure:
StackTrace class disassembled in Reflector. Extending ClogClog Provider Model
Clog uses An
Figure:
ILogStrategy and ILogStrategyProvider class diagram. Integrating Clog with your Existing 3rd Party Logging SystemTo integrate Clog with an existing logging system, implement the <add name="CustomProvider"
type="Orpius.Logging.LogStrategyProvider, Orpius.Logging"
LogStrategy="YourAssembly.Strategy, YourAssembly" />
The The Log Strategy determines how a log entry is written to a log. It is here that we connect our existing logging system, such as log4net, to Clog. When a log write is requested, the current Log Strategy must take the information present in the This release of Clog comes with a log4net strategy ( public void Write(IClientLogEntry logEntry)
{
ILog log = defaultLog;
if (logEntry.LogName != null)
{
log = LogManager.GetLogger(logEntry.LogName);
}
/* Create a Log4Net event data instance,
and populate it with our log entry information. */
LoggingEventData data = new LoggingEventData();
if (logEntry.ExceptionMemento != null)
{ /* Use the exception memento to write the message
and stack trace etc. */
data.ExceptionString = logEntry.ExceptionMemento.ToString();
}
data.Level = GetLog4NetLevel(logEntry.LogLevel);
ICodeLocation location = logEntry.CodeLocation;
if (location != null)
{
data.LocationInfo = new LocationInfo
(location.ClassName, location.MethodName,
location.FileName, location.LineNumber.ToString());
}
data.LoggerName = logEntry.LogName;
data.Message = logEntry.Message;
data.ThreadName = logEntry.ThreadName;
data.TimeStamp = logEntry.OccuredAt;
//data.Properties = logEntry.Properties;
if (string.IsNullOrEmpty(logEntry.UserName))
{ /* Populate the UserName property using the Membership provider. */
MembershipUser user = Membership.GetUser(true);
if (user != null)
{
data.UserName = user.UserName;
}
}
else
{
data.UserName = logEntry.UserName;
}
LoggingEvent loggingEvent = new LoggingEvent(data);
log.Logger.Log(loggingEvent);
}
FiltersClog uses server side filters to determine what log entries to discard before they are sent to the active Log Strategy. Filters are evaluated when retrieving
Figure:
Filter class diagram. The current /// <summary>
/// Restricts logging based on an IP address range.
/// </summary>
class IPAddressRangeFilter : HttpRequestFilterBase
{
uint begin;
uint end;
public override bool IsValid(LogEntryOrigin origin)
{
if (origin != LogEntryOrigin.Client || HttpRequest == null)
{
return true;
}
string addressValue = HttpRequest.UserHostAddress;
return IsWithinRange(begin, addressValue, end);
}
/// <summary>
/// Initialises the specified filter element.
/// </summary>
/// <param name="filtereElement">The filtere element.</param>
public override void Init(FilterElement filtereElement)
{
begin = ToUInt(IPAddress.Parse(filtereElement.Begin));
end = ToUInt(IPAddress.Parse(filtereElement.End));
}
static bool IsWithinRange
(uint beginAddress, string addressValue, uint endAddress)
{
IPAddress address = IPAddress.Parse(addressValue);
uint ip = ToUInt(address);
return ip >= beginAddress && ip <= endAddress;
}
/// <summary>
/// Converts and <see cref="IPAddress"/> to an unsigned int.
/// </summary>
/// <param name="ipAddress">The ip address to convert.</param>
/// <returns>A <code>uint</code> representing
/// the specified ipAddress.</returns>
static uint ToUInt(IPAddress ipAddress)
{
byte[] bytes = ipAddress.GetAddressBytes();
uint result = (uint)bytes[0] << 24;
result += (uint)bytes[1] << 16;
result += (uint)bytes[2] << 8;
result += bytes[3];
return result;
}
}
Logging Exceptions the Clog WayWhen a request to log an Log Entries
Figure: Log Entry class diagram.
Points of InterestJSON SerializationCommunicating with web services from Silverlight is one of its great features. Though, at this early stage, Silverlight Alpha 1.1 does have some limitations. The first is that there is no support for Silverlight 1.1 Alpha and Web ServicesYou may notice in our ClogLoggingService.cs that we have a return type of ApplicationUnhandledException Doesn't FireI had intended to handle the Silverlight Future Enhancements
ConclusionThis article discussed the implementation of Clog; a client server logging provider system. It showed how to set it up, including configuration of the Silverlight Log Viewer control, an example use of the .NET provider model. The article also touched on some more advanced topics such as the Silverlight security model. I intend to release "Clog WPF edition" in the coming weeks. Although Clog is still just a prototype, I believe it shows a lot of promise for becoming quite a useful tool. I hope you find this project useful. If so, then you may like to rate it and/or leave feedback below. References
History
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||