A reference architecture (part 11)

…continued from part 10

Logging

If we say logging, we automatically think about log4net, the de facto standard logging framework. So download it, and put the log4net.dll assembly in the \Trunk\Libraries\Log4Net folder. From the ArchitectureExample.Service.WebHost project, add a reference to it.

First, add the needed configuration in the web.config file of the ArchitectureExample.Service.WebHost project:

<?xml version="1.0"?>
<configuration>

<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>

<appSettings>
<!-- If you want log4net to add its own diagnostics messages -->
<add key="log4net.Internal.Debug" value="true" />
</appSettings>


<log4net debug="true">
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C:\\log\\architectureexample.txt" />
<appendToFile value="true" />
<rollingStyle value="Size" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="10MB" />
<staticLogFileName value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5p %d %5rms %-22.22c{1} %-18.18M - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RollingLogFileAppender" />
</root>
</log4net>

</configuration>

Next, we will start using log4net. The default usage says that you should put a static variable on top of each class where you want to log, but I don’t like that approach, so we’ll do it a bit different. As explained in a previous post we have to add a logger interface. So add the following interface to the ArchitectureExample.Infrastructure.Shared project:

public interface ILogger
{
void Debug(string message);
void Debug(string message, Exception ex);
void Info(string message);
void Info(string message, Exception ex);
void Warn(string message);
void Warn(string message, Exception ex);
void Error(string message);
void Error(string message, Exception ex);
void Fatal(string message);
void Fatal(string message, Exception ex);
}

Also add the concrete implementation Log4NetLogger, that uses log4net to do the actual logging:

public class Log4NetLogger : ILogger
{
private readonly ILog _log;

/// <summary>
/// Constructor.
/// </summary>
/// <param name="type"></param>
public Log4NetLogger(Type type)
{
_log = LogManager.GetLogger(type);
}

public void Debug(string message)
{
_log.Debug(message);
}

public void Debug(string message, Exception ex)
{
_log.Debug(message, ex);
}

public void Info(string message)
{
_log.Info(message);
}

public void Info(string message, Exception ex)
{
_log.Info(message, ex);
}

public void Warn(string message)
{
_log.Warn(message);
}

public void Warn(string message, Exception ex)
{
_log.Warn(message, ex);
}

public void Error(string message)
{
_log.Error(message);
}

public void Error(string message, Exception ex)
{
_log.Error(message, ex);
}

public void Fatal(string message)
{
_log.Fatal(message);
}

public void Fatal(string message, Exception ex)
{
_log.Fatal(message, ex);
}
}

In the ArchitectureExample.Service.WebHost project, make sure the log4net is correctly registered by adding this to the StructureMapRegistry:

// Logging
For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType));
var applicationPath = Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location);
var configFile = new FileInfo(Path.Combine(applicationPath, "web.config"));
XmlConfigurator.ConfigureAndWatch(configFile);

Finally, to actually log, it’s just a matter of injection the ILogger into the class where you want to use it, for example, in the AddBlogEntryRequestHandler of the ArchitectureExample.Application.RequestHandlers project:

public class AddBlogEntryRequestHandler : IRequestHandler<AddBlogEntryRequest, AddBlogEntryResponse>
{
private readonly ILogger _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly IBlogEntryRepository _blogEntryRepository;

public AddBlogEntryRequestHandler(ILogger logger, IUnitOfWork unitOfWork, IBlogEntryRepository blogEntryRepository)
{
_logger = logger;
_unitOfWork = unitOfWork;
_blogEntryRepository = blogEntryRepository;
}

public AddBlogEntryResponse Execute(AddBlogEntryRequest request)
{
var blogEntry = new BlogEntry(request.Title, request.Body);
_blogEntryRepository.Add(blogEntry);
_unitOfWork.Commit();
_logger.Debug("AddBlogEntryRequestHandler completed");
return new AddBlogEntryResponse(blogEntry.Id);
}
}

If you now run the AddBlogEntryTest again, you will see that the log file C:\log\architectureexample.txt contains:

DEBUG 2013-03-07 10:54:42,543  2921ms logEntryRequestHandler Debug              - AddBlogEntryRequestHandler completed

You can also easily add logging in the GlobalExceptionHandler like this:

public class GlobalExceptionHandler : IErrorHandler
{
private readonly ILogger _logger;

public GlobalExceptionHandler(ILogger logger)
{
_logger = logger;
}

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error is BusinessException)
{
var businessError = error as BusinessException;
var businessFault = new BusinessFault(businessError.ErrorCode, businessError.Message);
var faultException = new FaultException<BusinessFault>(businessFault, "Oops", new FaultCode("BusinessFault"));
var msgFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, error.TargetSite.Name);
_logger.Error("BusinessException", businessError);
}
else if (error is ValidationException)
{
var validationError = error as ValidationException;
var businessFault = new BusinessFault("VALIDATIONERROR", validationError.Message);
var faultException = new FaultException<BusinessFault>(businessFault, "Oops", new FaultCode("ValidationFault"));
var msgFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, error.TargetSite.Name);
_logger.Error("ValidationException", validationError);
}
else
{
var faultException = new FaultException(string.Format("{0}", error.TargetSite.Name));
var msgFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, msgFault, faultException.Action);
_logger.Error("Unknown exception", faultException);
}
}

public bool HandleError(Exception error)
{
return true;
}
}

Now run the AddBlogEntryWithoutTitleTest and check whether the ValidationException appears in the log file:

DEBUG 2013-03-07 10:54:42,543  2921ms logEntryRequestHandler Debug              - AddBlogEntryRequestHandler completed
DEBUG 2013-03-07 10:59:54,336 1919ms logEntryRequestHandler Debug - AddBlogEntryRequestHandler completed
DEBUG 2013-03-07 11:00:34,408 41992ms logEntryRequestHandler Debug - AddBlogEntryRequestHandler completed
ERROR 2013-03-07 11:01:08,511 76094ms GlobalExceptionHandler Error - ValidationException
FluentValidation.ValidationException: Validation failed:
-- 'Title' must not be empty.
-- 'Title' should not be empty.
-- 'Title' must be between 1 and 200 characters. You entered 0 characters.
at ArchitectureExample.Service.Shared.HandlerDispatcher.Execute[TRequest,TResponse](TRequest request, Boolean asynchronously) in C:\Projects\R&D\ArchitectureExample\Trunk\Sources\ArchitectureExample.Service.Shared\HandlerDispatcher.cs:line 32
at ArchitectureExample.Service.Adapter.ArchitectureExampleService.AddBlogEntry(AddBlogEntryRequest request) in C:\Projects\R&D\ArchitectureExample\Trunk\Sources\ArchitectureExample.Service.Adapter\ArchitectureExampleService.cs:line 27
at SyncInvokeAddBlogEntry(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)


Almost time for the conclusion

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s