im new to Enterprise Library (5, with the optional update), and am trying to use my own CustomTraceListener with the Logging Block.
Logging works with a supplied flat file listener, but i dont seem to be able to extend that to my custom class.
Logs to FlatFileListener, but Not to my CustomListener:
static UnityContainer _container;
static LogWriter _writer;
internal static void Inf(this string message)
{
if (_container == null)
{
_container = new UnityContainer();
_container.AddNewExtension<EnterpriseLibraryCoreExtension>();
}
if (_writer == null && _container != null)
{
_writer = _container.Resolve<LogWriter>();
LogEntry log = new LogEntry();
log.Message = message;
log.Categories.Add("Information");
log.Severity = TraceEventType.Information;
_writer.Write(log);
}
}
My CustomListener based on bychkov:
namespace Test.Catch
{
[ConfigurationElementType(typeof(CustomTraceListenerData))]
public class LoggerCustom : CustomTraceListener
{
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
if (data is LogEntry && this.Formatter != null)
{
this.WriteLine(this.Formatter.Format(data as LogEntry));
} else
{
this.WriteLine(data.ToString());
}
}
public override void Write(string message)
{
Trace.Write(message);
}
public override void WriteLine(string message)
{
Trace.WriteLine(message);
}
}
}
First i tried to add the customtracelistener with the Configuration Editor, but the configuration editor did not list/detect any customtracelisteners, which i suspect is part, or whole of the problem?
So instead, i added the following, again, based on bychkov:
app.config listener manual addition
<add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
type="Test.Catch.LoggerCustom, Test.Catch" traceOutputOptions="None"
name="Custom Trace Listener" initializeData="" formatter="Text Formatter Plain" />
Now when I call Inf("logme"), with the above app.config addition included, then an exception is thrown at _container.AddNewExtension():
The type 'Test.Catch.LoggerCustom, Test.Catch' cannot be resolved.
Given I dont understand the underlying mechanisms, how could i get to logging with my custom listener?
Do i need to create my own TraceListenerData, like the Tavares solution ?
Or do i need to understand and mess with UnityDefaultBehaviorExtension, as detailed by MacLeod ?
Is the existing code wrong?
Or the solution lies elsewhere?
Thank you for your expertise.
Go grab the Entlib 5 extensibility labs. There's an entire chapter on writing custom trace listeners. You'll learn the underlying technologies, why things work the way they do, how it fits together, and then you'll be able to answer your own questions.
As for why the above doesn't work: when using CustomXXXData configuration classes, your provider needs a constructor that takes a NameValueCollection. You don't have one, so your code is in fact wrong. Work through the labs, you'll learn that and more.
Related
I want to add some additional claims to a Principal during authentication. I am trying to implement a custom ClaimsAuthenticationManager in my MVC 4.5 project which uses Windows Authentication:
namespace Project.Infrastructure
{
public class ClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "Admin"));
}
return incomingPrincipal;
}
}
}
I have the web.config set up to use my custom class:
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</configSections>
and
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="Project.Infrastructure.ClaimsTransformer, [AssemblyName]" />
</identityConfiguration>
</system.identityModel>
But the Authenticate method never gets called. Am I missing something?
The missing step is that you need to add an HTTP Module to kick all of this off.
So, you need a class that looks like this:
public class MyClaimsAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
}
public void Dispose()
{
// Nothing to dispose, method required by IHttpModule
}
void Context_PostAuthenticateRequest(object sender, EventArgs e)
{
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
if (transformer != null)
{
var context = ((HttpApplication)sender).Context;
var principal = context.User as ClaimsPrincipal;
var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, principal);
context.User = transformedPrincipal;
Thread.CurrentPrincipal = transformedPrincipal;
}
}
}
This will pick up the transformer you specified in the web.config and call authenticate on it, then attach the returned principal to the HttpContext and the current thread.
You will also need something like the following in your web.config:
<system.webServer>
<modules>
<add name="MyClaimsAuthenticationModule" type="MyApplication.MyHttpModels.MyClaimsAuthenticationModule, MyApplicationAssembly" />
</modules>
</system.webServer>
Update
You can, of course, put the code from the method Context_PostAuthenticationRequest in the PostAuthenticateRequest handler in your Global.asax.cs class file. However, I prefer to keep the responsibilities of the classes small so I go for an implementation of IHttpModule so that the module does its thing and it is obvious what that is, and it is separate from other things that may be happening at various stages of the pipeline.
If your Global.asax.cs file is small then there is no problem with putting the code there. It should still work. However, you are mixing responsibilities in the class and it could get unwieldy in the future.
Are you calling the Authenticate method with something like this, and the Authentication is not taking place?
ClaimsTransformer manager = new ClaimsTransformer();
manager.Authenticate("resource", incomingPrincipal )
You may want to replace the "return incomingPrincipal" from inside the ClaimsTransformer class with a call to:
return base.Authenticate(resourceName, incomingPrincipal);
Also why do you need Windows Authentication?
i am using the NLog built in support for castle and trying to find a way to alter the connection string at run time.
this is my latest swing and miss, im sure it has to do the life cycle at this point as all of the configuration is null so i am guessing that castle has not yet wired up the guts on NLog.
private const string NLogConnectionString = "NLogConnection";
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<LoggingFacility>(l => l.UseNLog());
var config = new NLog.Config.LoggingConfiguration();
var dbtarget = config.FindTargetByName("database") as DatabaseTarget;
if (dbtarget != null)
{
dbtarget.ConnectionString = MethodThatGiveMeConnectionString(NLogConnectionString);
}
}
looking at this post it could be an option but based on the way things have been done here i dont want to change that and much prefer just providing the connection string directly to NLog.
looking here I know i can configure this at run time but i much prefer let most of the settings come from the config file and then just override the connection string.
So I found a solution that works but not sure it's the best approach.
Using this post as a reference. This comment was the most helpful:
Initially I tried to implement a custom ILoggerFactory and inject it into LoggingFacility via the customLoggerFactory. But that soon proved to be a dead end. Then I looked into the NLog integration and noticed that there's already a NLogFactory that has it's methods marked as virtual. So I was able to derive from this class
The problem the author is solving is different than my own but it got me to come up with this solution:
public class LoggerInstall : IWindsorInstaller
{
private const string NLogConnectionString = "NLogConnection";
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var config = new NLog.Config.LoggingConfiguration();
container.AddFacility<LoggingFacility>(l => l.LogUsing(new OwnNLogFactory(GetYourConnectionStringMethod(NLogConnectionString))));
}
}
public class OwnNLogFactory : NLogFactory
{
public OwnNLogFactory(string connectionString)
{
foreach (var dbTarget in LogManager.Configuration.AllTargets.OfType<DatabaseTarget>().Select(aTarget => aTarget))
{
dbTarget.ConnectionString = connectionString;
}
}
}
Still not sure this is the best solution but it works for now, would love to see other solutions if anyone has one
Lately I am working on exception logging module of a WCF service. Unfortunately the service hasn't been introduced with unit tests, therefore there are many unexpected exceptions occurring. And so far I have accomplished to get the exceptions with interceptor aproach, by implementing IErrorHandler interface and tying it to the service interface with IServiceBehaviour. I liked this functionality very much actually. But it brought me into a next step of desire of getting the details of exception. Like on which line did the exception occurred?
I can satisfy this desire by 2 ways in my mind:
By having a variable for keeping track of the lines I've passed through successfully, and including it in the exception thrown.
By catching exceptions from all lines seperately.
But both approaches seem very lousy to me. I am wondering is there a known design pattern or a tool to achive this goal?
In my opinion you might try using logging, such as log4net. Then you can find out where is and what happened. Exception object not always contains the stack info, because of "inlining", that occur during optimization etc.
include the PDB files for your service and the line numbers will be included in exception.ToString()
The way we have solved this problem is twofold:
Our services are dumb wrappers around commands. So when a service method is entered it delegates its work to a command.
We wrap every command call in a logging proxy that is responsible for logging input, output and errors and executing the command.
For example:
public FooServiceModel GetFoo(int fooId)
{
return new ILogged<GetFooCommand>().Target.Execute(fooId);
}
This delegates execution of the command to ILogged which:
Logs the command name
Logs the command parameters
Logs the execution result
Logs any exceptions
It also does some other stuff to link up the client request with the server call using custom message headers so that a call can be completely debugged from client to server and back. This is incredibly useful and allows us to diagnose even complex problems off site.
We use the Castle.Core dynamic proxy to implement ILogged with an interceptor that looks something like this (ILog is a log4net logger):
public class LoggingInterceptor : IInterceptor
{
public LoggingInterceptor([NotNull] object target, [NotNull] ILog logger)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (logger == null)
{
throw new ArgumentNullException("logger");
}
this.Target = target;
this.Logger = logger;
}
public object Target { get; set; }
public ILog Logger { get; set; }
public void Intercept(IInvocation invocation)
{
try
{
this.Logger.Debug(invocation);
invocation.ReturnValue = invocation.Method.Invoke(
this.Target, invocation.Arguments);
this.Logger.Debug("Invocation return value:");
this.Logger.Debug(invocation.ReturnValue);
}
catch (TargetInvocationException ex)
{
this.Logger.Error("Unable to execute invocation", ex);
if (ex.InnerException != null)
{
throw ex.InnerException;
}
throw;
}
}
}
The invocation itself is rendered by a custom log4net object renderer:
public class InvocationRenderer : IObjectRenderer
{
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
{
var invocation = (IInvocation)obj;
var builder = new StringBuilder();
builder.AppendFormat(
"Invoking Method: {0} --> '{1}' with parameters (",
invocation.Method.DeclaringType != null
? invocation.Method.DeclaringType.FullName : "{Unknown Type}",
invocation.Method);
var parameters = invocation.Method
.GetParameters()
.Zip(invocation.Arguments, (p, a) => new { Parameter = p, Argument = a })
.ToArray();
var index = 0;
foreach (var parameter in parameters)
{
builder.AppendFormat(
"{0}: {1}",
parameter.Parameter.Name,
rendererMap.FindAndRender(parameter.Argument));
if (++index < parameters.Length)
{
builder.Append(", ");
}
}
builder.Append(")");
writer.Write(builder.ToString());
}
}
Hopefully that will give you some ideas on how to tackle this problem.
Approach
I am employing MEF to create a plugin-nable, if you will, application. My MEF host has an ILogger which exposes TraceMessage(string message). Class Logger implements ILogger and is decorated with an Export attribute so Logger looks like:
[Export(typeof (ILogger))]
public class Logger : ILogger
{ }
The idea being that the various plugins can be offered a central logger that they can write to. Thus, instantiation would be via [Import] attribue, for example:
[Export(typeof (ILogger))]
public class Logger : ILogger
{
private readonly IWindsorContainer _container;
public ICloudTrace CloudTrace
{
get { return _container.Resolve<ICloudTrace>(); }
}
public Logger()
{
_container = new WindsorContainer(new XmlInterpreter());
}
public void TraceMessage(string categoryName, string componentName, string message)
{
CloudTrace.TraceMessage(categoryName, componentName, message);
}
}
And subsequently a log message would be written via Logger.TraceMessage(string message).
Problem
However, this approach throws an InvalidOperationException in my host when its trying to resolve exports, with an error message Sequence contains no matching element.
Exports are resolved in ResolveType(string commandType) (where commandType is the command line parameter needed to execute relevant plugin). ResolveType() looks like:
public dynamic ResolveType(string commandType)
{
try
{
return this.Container.GetExports<ICommand, ICommandMetaData>()
.First(contract => contract.Metadata.CommandType.Equals(commandType, StringComparison.OrdinalIgnoreCase))
.Value;
}
catch (Exception e)
{
Console.WriteLine(e.message);
}
}
I should mention that each plugin has an Execute(Dictionary<string, string> parameters) which is the entry point for the plugin and the class containing this method is decorated with [Export(typeof(ICommand))] [ExportMetadata("CommandType","CommandLine Command string goes here")] attributes.
The problem was in the CompositionContainer construction. Currently, it just loads the plugin assembly specified in the command line instead of doing directory scan or loading currently executing assembly. This has been done for various reasons. So:
var assemblyCatalog = new AssemblyCatalog(Assembly.LoadFrom(assemblyFile: assemblyToLoad));
where assemblyToLoad is a string to the .dll file for the specific plugin. However, the logger is in the host so the host's assembly needs to be loaded. Thus:
var assemblyCatalog = new AggregateCatalog(new AssemblyCatalog(Assembly.GetExecutingAssembly()),
new AssemblyCatalog(Assembly.LoadFrom(assemblyFile: assemblyToLoad)));
fixes the issue.
Thanks to #Matthew Abbott for pointing this out
Well, I'm making my foray into this fantastic site with a question about the correct way to inject configuration settings into application components. So, the overview is : I have an application written in C# .Net 3.5. It consists of 3 assemblies - a Core, a Data and a Service. The data & service assemblies require settings retrieved from the app.config, which is done via a settings file, eg.
Code :
public static String RequestQueueConnectionString
{
get { return ConnectionSettings.Default.RequestQueueConnectionString; }
}
Config :
<applicationSettings>
<MyNamespace.Data.ConnectionSettings>
<setting name="RequestQueueConnectionString" serializeAs="String">
...
Now, the assemblies are all setup using StructureMap for IoC - which to my mind should provide the answer to what I am looking for, but I just can't quite see it!
IoC :
public static void ConfigureStructureMap(IContainer container)
{
container.Configure(x => ...
...
What I want to be able to do is to inject a configuration class already populated into the IoC container such that those settings are used for that assembly, NOT those specified in the settings file / app.config. So perhaps :
public static void ConfigureStructureMap(IContainer container, MyConfigClass config)
{
container.Configure(x => x.For<DataConfig>()
.Singleton()
.Use ???
...
I hope I have provided enough details here - forgive a newbie if I have not and please let me know what else would be helpful in answering this!
So, after a lot of searching and trial and error, I was presented with #default.kramer's link, which I duely followed! With a little bit of trial and error, again (best way in my opinion), I managed to get the solution I was looking for. Now, whilst you can follow the link (and I would highly suggest doing so), I am going to post the solution to my question as I implemented it. Hopefully this might help someone with a similar problem.
So, I now have my configuration setup class like so :
public static class DispatchConfiguration
{
public static void ConfigureStructureMap(IContainer container, IDispatchConfiguration dispatchConfig)
{
DispatchProcessBatchSize = dispatchConfig.DispatchProcessBatchSize;
ServiceIsActive = dispatchConfig.ServiceIsActive;
...
}
Now, before I was using a settings file to retrieve the configuration out of the app.config file. This was obviously good for ensuring I had flexibility in changing my config settings, but it left me with the problem of not being able to easily test those settings. Say 9/10 tests required the service to be active, but 1 test wanted to test "ServiceIsActive = false;", now I'm in trouble.
Now, however, I am able to inject the configuration from the test :
[Given(#"Config\.IsServiceActive returns false")]
public void GivenConfig_IsServiceActiveReturnsFalse()
{
var settings = new DispatchSettings
{
ServiceIsActive = false,
DispatchProcessBatchSize = 100,
UpdatedBy = "Unit Test"
};
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, settings);
}
And then in the real world I am able to get the settings from app.config :
public void Start(String[] args)
{
var dispatchConfig = this.GetDispatchConfiguration();
DispatchConfiguration.ConfigureStructureMap(ObjectFactory.Container, dispatchConfig);
...
}
private IDispatchConfiguration GetDispatchConfiguration()
{
var config = (DispatchSettings)ConfigurationManager.GetSection("DispatchSettings");
return config;
}
And then the actual config class looks like :
[XmlRoot(ElementName = "DispatchSettings", Namespace = "")]
public sealed class DispatchSettings : IDispatchConfiguration
{
public Int32 DispatchProcessBatchSize { get; set; }
public Boolean ServiceIsActive { get; set; }
...
}
For the sake of completeness the interface looks like so :
public interface IDispatchConfiguration
{
Int32 DispatchProcessBatchSize { get; }
Boolean ServiceIsActive { get; }
...
}
And finally, the config file looks like this :
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="DispatchSettings" type="MyNamespace.XmlConfigurator, MyNamespace.Core" />
</configSections>
<DispatchSettings type="MyNamespace.DispatchSettings, MyNamespace.Core">
<ServiceIsActive>True</ServiceIsActive>
<DispatchProcessBatchSize>100</DispatchProcessBatchSize>
</DispatchSettings>
Now, anyone with a keen eye will spot "MyNamespace.XmlConfigurator". I found this on one of my Google journeys, and the code allows you to deserialize an Xml config into a class of your desire (as shown in this example). So, to ensure you have the complete code to make this technique work, below is the code for the XmlConfigurator. I cannot remember where I came across it, but a big thanks to the person who wrote it!!
public sealed class XmlConfigurator : IConfigurationSectionHandler
{
public XmlConfigurator()
{
}
public object Create(object parent, object configContext, XmlNode section)
{
XPathNavigator navigator = null;
String typeName = null;
Type sectionType = null;
XmlSerializer xs = null;
XmlNodeReader reader = null;
try
{
Object settings = null;
if (section == null)
{
return settings;
}
navigator = section.CreateNavigator();
typeName = (string)navigator.Evaluate("string(#type)");
sectionType = Type.GetType(typeName);
xs = new XmlSerializer(sectionType);
reader = new XmlNodeReader(section);
settings = xs.Deserialize(reader);
return settings;
}
finally
{
xs = null;
}
}
}
And there you have it! I hope this allows anyone with a similiar issue to resolve it and is clear enough to follow!