log4net - Rendering an (object) message depending on the appender - c#

I'm using log4net, and a custom logg facade practically identical to log4net.ILog to access it.
I would like to log to a simple textfile, but also use a custom XML appender to write stuff to XML.
Now here's the issue. Say I'm doing something like
interface IMyILogFacade
{
// An implementation of this interface will eventually route this call to
// log4net.ILogger.Trace
void Trace(string format, params object[] args);
}
class Test
{
IMyILogFacade log; // assume this is given (injected)
public void testMethod(Assembly assembly)
{
string msg = "Entering Method 'testMethod'. Method Parameter 0 (assembly): {0}";
log.Trace(msg, assembly);
}
}
By default, log4net will render the message with a simple String.Format(msg, assembly), when accessing %message in any conversion pattern, or RenderedMessage explicitly.
This behaviour is fine for my text file log. However for my XML log, I want it to be rendered differently. In short, I would then like to reflect on the runtime type of the parameter (assembly) and dump all its public members, and its public members' public members, and.. so on... to a nested XML structure. So instead of rendering with String.Format(msg, assembly), I'd need to use something else, say String.Format(msg, MyXmlDumper.Dump(assembly)).
I haven't found any good way to make rendering depending on the type of the Appender though.
My current approach was to have my logger facade transform all calls to Trace, Debug, ... etc into an object of type LogMessage.
public class LogMessage
{
public string message { get; protected set; }
public object[] #params { get; protected set; }
public LogMessage(string message, params object[] #params)
{
this.message = message;
this.#params = #params;
}
}
I'd then use a class implementing IObjectRenderer to render this. This would be the one for a simple ToString-like function
public class LogMessageStringRenderer : IObjectRenderer
{
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
{
LogMessage logMessage = obj as LogMessage;
if(logMessage == null)
{
throw new InvalidOperationException("LogMessageStringRenderer can only render objects of type LogMessage");
} else
{
writer.Write(
String.Format(logMessage.message, logMessage.#params)
);
}
}
}
and of course it'd be straightforward to create the same for creating my Xml dump.
This seems like a not very ideal solution though. For a start, these renderes can (as far as I know) only be attached to a log4net repository. Either by code, or in the config file as a root element like
<renderer renderedClass="LogMessage" RenderingClass="LogMessageStringRenderer"/>
But that means I'd have to create two logging repositories in my application; one for the text file appender, another one for the xml appender, and set their rendering objects accordingly.
This seems very and unnecessarily complex though (let alone I don't have a clue how to best use logging repositories at the moment).
Is there possibly any better solution to this problem? Maybe either a way to somehow select renderers on an appender-basis instead of for the whole repository. Or some conceptually entirely different solution I haven't though of.
Any pointers would be very appreciated.
Thanks

Classic, been spending the entire day on this and just after posting here I do find a solution...
I'll wrap my messages in the LogMessage class as posted above. Then I'll create a converted deriving from PatternLayoutConverter and apply it to the message parameter:
<param name="ConversionPattern" value="%date - %thread - %level - %-80logger - %-40method - %message%newline" />
<footer value="
"/>
<converter>
<name value="message" />
<type value="MyMessageConverter"/>
</converter>
If I'm lazy, I may created two converters, one for a ToString implementation, one for my Xml-based dumping.
If I'm more ambitious, I may create a converter that allows to apply a custom IObjectRenderer in the log4net.config and uses this to render the message. My IObjcetRenderer implementations can then take care of transforming the LogMessage as described in my first post.
This should both work very nicely; will implement this next week, drop me a message if anyone in the future is looking for specific implementations.

Related

MvcSiteMapProvider : How to load extra web.sitemap files from MEF-enabled module in MVC 4

I have a separate Module in a C# class library project which is loaded via MEF Imports.
As an initial attempt at bringing sitemap information along with the Module I have added a web.sitemap file with the necessary mark-up but I can't seem to get a clear sense of how to load it and attach it to the Host MVC project sitemap in memory.
I also tried to use an MvcSiteMapNode attribute but haven't really been able to get this working yet.
Firstly, which is the easiest method to use, Attribute or SiteMap?
Secondly can anyone point me to guidance on how to do either of these please?
I have a preference for using the sitemap file because it should avoid dependencies on MvcSiteMapProvider in the MEF module.
You could embed XML into your modules and then somehow export them through MEF, I am pretty sure that is an option.
To load the XML from memory, you will need to use an external DI container and implement the IXmlSource interface yourself.
public class MyXmlSource : IXmlSource
{
public XDocument GetXml()
{
// Load the XML from wherever...
}
}
Then the DI configuration would just need to swap out the default XmlSource.
var excludeTypes = new Type[] {
typeof(IXmlSource)
};
// More DI config not shown...
this.For<IXmlSource>()
.Use<MyXmlSource>();
Of course, if you have multiple files, you would need multiple registrations of IXmlSource and XmlSiteMapNodeProvider. The XmlSiteMapNodeProvider only has the ability to merge the nodes below the root node, so you can't just place them anywhere in the SiteMap.
XML and Attributes are the least flexible options for configuring MvcSiteMapProvider.
Another Approach
The most flexible option is to use ISiteMapNodeProvider implementations to load your configuration data which also requires an external DI container. The second-best option is to use dynamic node provider implementations, but the limitation there is that they cannot contain the root node and require either an XML node or .NET attribute to host the provider on.
However, if you don't want any 3rd party dependencies, you will need a custom abstraction (DTO) that is defined in your own base library that is exported via MEF, and then used by either ISiteMapNodeProvider or IDynamicNodeProvider to load the data from the abstraction.
public class SiteMapNodeDto
{
public string Key { get; set; }
public string ParentKey { get; set; }
public string Title { get; set; }
public IDictionary<string, object> RouteValues { get; set; }
// Additional properties...
}
And then have an interface of some kind that your module can implement to provide nodes.
public interface IModuleSiteMapNodeProvider
{
IEnumerable<SiteMapNodeDto> GetNodes();
}
My brief experience with MEF was several years ago, so I don't recall how you export a type and import it somewhere else, so you will need to work that out on your own.
Then in your main application, you could create a method for converting from your DTO to the type that is expected by MvcSiteMapProvider (ISiteMapNodeToParentRelation or DynamicNode).
public static SiteMapNodeProviderExtensions
{
public static ISiteMapToParentRelation CreateNode(this ISiteMapNodeHelper helper, SiteMapNodeDto dto, string sourceName)
{
string key = helper.CreateNodeKey(
dto.ParentKey,
dto.Key,
dto.Url,
dto.Title,
dto.Area,
dto.Controller,
dto.Action,
dto.HttpMethod,
dto.Clickable);
var nodeParentMap = helper.CreateNode(key, attribute.ParentKey, sourceName);
var node = nodeParentMap.Node;
node.Title = title;
// Populate remaining properties...
return nodeParentMap;
}
}
And then in the ISiteMapNodeProvider you would simply need to call this method for each node.
public IEnumerable<ISiteMapNodeToParentRelation> GetSiteMapNodes(ISiteMapNodeHelper helper)
{
string sourceName = typeof(SiteMapNodeDto).Name;
IEnumerable<SiteMapNodeDto> dtos = someExternalSource.GetNodes();
foreach (var dto in dtos)
{
yield return helper.CreateNode(dto, sourceName);
}
}
If you want to make providing SiteMap nodes a first class feature for your module developers (and make it really easy to understand the hierarchy of nodes like you can with XML), you could create a Fluent API so you can express the nodes in code. Have a look at this pull request for one such approach.

c#: how to include filename when sending files to an asmx service

I have an asmx method that accepts a list of files like so:
[WebMethod]
public void UploadFiles(List<byte[]> files)
{
}
The Windows metadata is not included in the byte array. I tried using Dictionary<filename, byte[]> but classes that implement IDictionary are not serializable. I also tried using KeyValuePair<string, byte[]>[] but IMO it looks dirty.
Are there other ways to include the name of the file?
As mentioned in the comments, this can easily resolved by making a custom data class.
It's unfortunate that Dictionaries aren't serializable, but it's an inherent flaw of the XML serialization process. Same goes for data classes with circular references, it just doesn't work.
WCF, however, has managed to fix those issues. But you're using .asmx (SOAP), so you're stuck with the unfortunate incompatibility.
I'd simply make a custom class:
[Serializable]
public class File
{
public string FileName {get;set;}
public byte[] Payload {get;set;}
}
Then change your web method to:
[WebMethod]
public void UploadFiles(List<File> files)
{
//...
}
Simple, but effective :)

Generic dependency injection with Unity

We are wrapping an existing logging library in our own logging service in a C# application to surround it with predefined methods for certain logging situations.
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
public LoggingBlockLoggingService(LogWriter writer)
{
this.writer = writer;
}
....//logging convenience methods, LogWarning(), etc...
}
I would like to modify this implementation so that it takes in the Type of the class that instantiates it (the underlying logger, LogWriter in this case, would be a singleton). So either make this implementation (and the interface ILoggingService) generic:
public class LoggingBlockLoggingService<T> : ILoggingService<T>
{
...
private string typeName = typeof(T).FulName;
...
Or add an additional constructor parameter:
public class LoggingBlockLoggingService : ILoggingService
{
private LogWriter writer;
private string typeName;
public LoggingBlockLoggingService(LogWriter writer, Type type)
{
this.writer = writer;
this.typeName = type.FullName;
}
....//Include the typeName in the logs so we know the class that is logging.
}
Is there a way to configure this once in Unity when registering our types? I'd like to avoid having to add an entry for every class that wants to log. Ideally, if someone wants to add logging to a class in our project, they'd just add an ILoggingService to the constructor of the class they are working in, instead of adding another line to our unity config to register each class they are working on.
We are using run time/code configuration, not XML
Yes, you can use:
container.RegisterType(typeof(IMyGenericInterface<>), typeof(MyConcreteGenericClass<>));
In your case, when there's simple direct generic-param--to--generic-param mapping the Unity maybe actually handles that, but I doubt that any more advanced cases are not handled, because something at some point of time must provide the mapping of generic-parameters between the types (liek reordering Key-Value vs. Value-Key etc).
If Dave's answer is not enough, I'm fairly sure that you could write a plugin to Unity/ObjectBuilder that would register a new strategy or set of strategies that would cover just any type mapping you would like, including automatic assembly scanning or materialization of generics.
See the series of articles at http://www.orbifold.net/default/unity-objectbuilder-part-ii/ and the section near
Context.Strategies.AddNew< buildkeymappingstrategy >(UnityBuildStage.TypeMapping);

App.config multi-project access strategies

My current solution has 3 project with 2 app.config (one for common settings and another for service settings). As of now I'm simply creating static classes to act as a mediator to access values. I do this so I don't have to write ConfigurationManager.AppSettings["SomeKey"] everywhere. This works fine until you want to access an app.config file from a different project.
Here is what I'm currently doing (all properties omitted for brevity).
public class ServiceConfiguration
{
public static readonly string SyncEvery = ConfigurationManager.AppSettings["SyncEveryMinutes"];
}
How can I access an app.config file located in another project? I thought perhaps setting VS to copy the file to the output directory would do the trick however my configuration object is still null.
I can't imaging many good reasons to read another app's configuration in the first place, it just opens a can of worms that isn't worth dealing with.
Expose a class that exposes the project's configured values as properties, and access them from a consuming class.
public class FirstProjectClass
{
public static int SyncEveryMinutes
{
get { return (int)ConfigurationManager.AppSetting["SyncEveryMinutes"] };
}
}
public class SecondProjectClass
{
public void ShowConfigedValue()
{
Console.Writeline("Syncing every {0} minutes", FirstProjectClass.SyncEveryMinutes);
}
}
if you've got complex configuration requirements you can also look into custom configuration sections
ConfigurationManager.OpenExeConfiguration can be helpfull:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.openexeconfiguration.aspx
Also: what Jason said - it is usually a bad idea.

Dependency Injection - Choose DLL and class implementation at runtime through configuration file

I've an API DLL (API.dll, for example) which, in addition to many other thinks, makes available an abstract class (AbstractClass).
Now making use of that AbstractClass I've implemented it on two different dlls:
First.API.Implementation.dll with ConcreteImplementation1
Second.API.Implementation.dll with ConcreteImplementation2
Both ConcreteImplementation1 and ConcreteImplementation2 are implementation of the same abstract class.
What I want is an application where I can choose which of those two dlls to use and, through that, choose which implementation to use without the user having to change anything within the code and, if possible, without stopping the application.
Some configuration file where I can bring the application to use whatever implementation I want. Something like:
<appconfiguration>
<implementation_to_use>
<dll>First.API.Implementation.dll</dll>
<class>ConcreteImplementation1</class>
</implementation_to_use>
</appconfiguration>
I know near to nothing about dependency injection, apart from its concept, but I guess thats the perfect fit for this task.
I've researched several DI/IoC libraries but I'm not familiar with all the concepts and names. I can use whatever library I want. For what I can say these are the most used: StructureMap, Ninject and Sprint.NET
Moreover, apart from all the dlls and implementation I need to indicate a file to be used by that application. Can I indicate its path in that same file?
I just need some tips and directions to implement such a thing. Some examples using one of those libraries, would be awesome.
Thanks.
To get you started using StructureMap, create a console application, include in it:
structuremap.config:
<?xml version="1.0" encoding="utf-8" ?>
<StructureMap MementoStyle="Attribute">
<DefaultInstance
PluginType="DemoIoC.AbstractBase,DemoIoC"
PluggedType="DemoIoC.ConcreteImplementation1,DemoIoC"
Scope="Singleton" />
</StructureMap>
The PluginType and PluggedType attributes are "FullyQualifiedClassName,AssemblyName"
By default it will look for assemblies in the executable folder, I'm not sure how you would specify another location for the assemblies
There are plenty of options for Scope, e.g. Singleton, Transient, etc
Program.cs:
namespace DemoIoC
{
using System;
using StructureMap;
public static class Program
{
public static void Main(string[] args)
{
// here you initialize structuremap from the config file.
// You could probably use a FileSystemWatcher to reinitialize
// whenever the structuremap.config file changes
ObjectFactory.Initialize(x =>
{
x.UseDefaultStructureMapConfigFile = true;
});
var concrete = ObjectFactory.GetInstance<AbstractBase>();
concrete.Method1();
Console.ReadKey(true);
}
}
}
AbstractBase.cs:
namespace DemoIoC
{
public abstract class AbstractBase
{
public abstract void Method1();
}
}
ConcreteImplementation1.cs:
namespace DemoIoC
{
using System;
public class ConcreteImplementation1 : AbstractBase
{
public override void Method1()
{
Console.WriteLine("Called ConcreteImplementation1");
}
}
}

Categories

Resources