Dependency Injection and Named Logins

I am interested in learning more about how people implement registration using dependency injection platforms. Although the links below and my examples relate to log4net and Unity, I will not necessarily use them. For dependency injection / IOC, I will probably use MEF, as this is the standard the rest of the project dwells on (large).

I am very new to / ioc dependency injection and quite new to C # and .NET (I wrote very little production code in C # /. NET after 10 years or so of VC6 and VB6). I have done a lot of research on the various logging solutions that are there, so I think I have a decent pen for their feature sets. I'm just not familiar enough with the real mechanics of getting one dependent injection (or maybe more "correct"), getting an abstracted version of one dependent injection).

I saw other posts related to registration and / or dependency, for example: attachment and dependency logging interfaces

Logging Protocols

What does the Wrapper Log4Net class look like?

again about the configuration of log4net and Unity IOC

My question is not particularly relevant to "How do I implement the xxx registration platform using the ioc yyy tool?" Rather, I'm interested in how people handled the completion of the registration platform (how often, but not always recommended) and configuration (i.e. app.config). For example, using log4net as an example, I could configure (in app.config) several registrars, and then get these registrars (without dependency injection) in a standard way to use the code as follows:

private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 

Alternatively, if my registrar is not named for the class, but rather for the functional area, I could do this:

 private static readonly ILog logger = LogManager.GetLogger("Login"); private static readonly ILog logger = LogManager.GetLogger("Query"); private static readonly ILog logger = LogManager.GetLogger("Report"); 

So, I think my “requirements” would be something like this:

  • I would like to isolate the source of my product from a direct dependence on the registration platform.

  • I would like to be able to resolve a specific instance of a named instance of a log (perhaps sharing the same instance among all requests of the same named instance) directly or indirectly using some kind of dependency injection, possibly using MEF.

  • I don’t know if I will call it a strict requirement, but I need the opportunity to get a named logger (different from the class registrar) on demand. For example, I can create a registrar for my class based on the class name, but one method requires special hard diagnostics, which I would like to control separately. In other words, I may need one class to “depend” on two separate log instances.

Let's start with number 1. I read a number of articles, primarily here in stackoverflow, about whether to wrap an idea. See the Best Practices link above and go to jeffrey hantin for one look at why log4net is poorly ported. If you made a wrapper (and if you could wrap it efficiently), would you stick strictly for the purpose of injecting / removing a direct dependency? Or will you also try to divert some or all of the information about log4net app.config?

Suppose I want to use System.Diagnostics, I would probably want to implement interface-based logic (perhaps even using the "common" ILogger / ILog interface), possibly based on TraceSource, so I can inject it. Do you add an interface, say, through TraceSource, and just use the information about the application. System.infigostics.config how ??

Something like that:

 public class MyLogger : ILogger { private TraceSource ts; public MyLogger(string name) { ts = new TraceSource(name); } public void ILogger.Log(string msg) { ts.TraceEvent(msg); } } 

And use it as follows:

 private static readonly ILogger logger = new MyLogger("stackoverflow"); logger.Info("Hello world!") 

Moving by number 2 ... How to allow a specific instance of the name log? Should I just use the app.config information for the registration platform that I choose (i.e. Allow registrars based on the naming scheme in app.config)? So, in the case of log4net, perhaps I prefer to “enter” the LogManager (note that I know this is not possible, since it is a static object)? I could wrap the LogManager (name it MyLogManager), give it an ILogManager interface, and then enable the MyLogManager.ILogManager interface. My other objects may have a dependency (Import in MEF parlance) on ILogManager (Export from assembly where it is implemented). Now I can have these objects:

 public class MyClass { private ILogger logger; public MyClass([Import(typeof(ILogManager))] logManager) { logger = logManager.GetLogger("MyClass"); } } 

When ILogManager is called, it will directly delegate log8net LogManager. Alternatively, can a wrapped LogManager take the ILogger instances it receives based on app.config and add them to the (??) MEF container by name. Later, when a logger with the same name is requested, a wrapped LogManager is requested for that name. If an ILogger is located there, it is allowed to do so. If this is possible with MEF, are there any benefits to this?

In this case, indeed, only the ILogManager is “injected”, and it can issue instances of ILogger as log4net normally does. How does this type of injection (essentially from a factory) compare to introducing named log instances? This makes it easier to use the app.config file for log4net (or another registration platform).

I know that I can get named instances from the MEF container as follows:

 var container = new CompositionContainer(<catalogs and other stuff>); ILogger logger = container.GetExportedValue<ILogger>("ThisLogger"); 

But how do I get named instances in a container? I know about an attribute-based model where I can have different implementations of ILogger, each of which is called (via the MEF attribute), but that doesn't really help me. Is there a way to create something like app.config (or a section in it) that will list the registrars (all the same implementations) by name and what MEF can read? Could / should there be a central "manager" (for example, MyLogManager) that resolves log names through the base app.config, and then inserts the allowed logger into the MEF container? This way, it will be available to someone else with access to the same MEF container (although without MyLogManager knowing how to use the log4net app.config information, it seems that the container will not be able to directly resolve any named logs).

This has been a fairly long time. Hope this will be consistent. Feel free to share any specific information about how you depend on, implement the registration platform (we are most likely looking at log4net, NLog or something (hopefully subtle) based on System.Diagnostics) in your application.

Did you enter "manager" and did it return instances of the magazine?

Have you added your own configuration information to your own configuration section or to your configuration platform DI configuration in order to simplify / possibly directly import the registrar instances (i.e. make your dependencies on ILogger, not ILogManager).

How about having a static or global container that has an ILogManager interface in it or a set of named instances of ILogger. Thus, instead of typing in the usual sense (via the constructor, property, or member data), the dependency on logging is explicitly resolved on demand. This is a good or bad way to inject addiction.

I mark this as a wiki community, since it doesn't seem like a question with a specific answer. If someone feels different, feel free to change them.

Thanks for any help!

+65
c # dependency-injection logging log4net nlog
Aug 10 2018-10-1818:
source share
5 answers

This is useful for anyone trying to figure out how to add a log dependency when the logger you want to enter provides a logging platform like log4net or NLog. My problem was that I could not understand how I could make a class (e.g. MyClass) dependent on an interface like ILogger, when I knew that the resolution of a particular ILogger would depend on knowing the type of the class depending on ILogger (e.g. MyClass ) How does the DI / IoC platform / container get the correct ILogger?

Ok, I looked at the source for Castle and NInject and saw how they work. I also looked at AutoFac and StructureMap.

Castle and NInject provide log execution. Both support log4net and NLog. The castle also supports System.Diagnostics. In both cases, when the platform allows dependencies for a given object (for example, when the platform creates MyClass and MyClass depends on the ILogger), it delegates the creation of the dependency (ILogger) to the ILogger provider (a general term may appear instead). The ILogger provider implementation is then responsible for actually creating the ILogger instance and passing it back, and then injecting it into a dependent class (e.g. MyClass). In both cases, the provider / resolver knows the type of the dependent class (e.g. MyClass). So, when MyClass was created and its dependencies resolved, ILRGER "resolver" knows that the MyClass class. When using logging or NInject solutions provided for logging, this means that the logging solution (implemented as a wrapper through log4net or NLog) gets the type (MyClass), so it can delegate to log4net.LogManager.GetLogger () or NLog.LogManager .GetLogger (). (Not 100% sure of the syntax for log4net and NLog, but you get the idea).

While AutoFac and StructureMap do not provide logging facilities (at least I could tell by looking), they seem to provide the ability to implement custom converters. So, if you want to write your own level of recording abstraction, you can also write the corresponding custom converter. That way, when the container wants to enable ILogger, your resolver will be used to get the correct ILogger. And it will have access to the current context (that is, which object dependencies are currently being satisfied - which object depends on the ILogger). Get the type of object, and you are ready to delegate the creation of ILogger to the currently configured registration platform (which you probably abstracted behind the interface and for which you wrote a resolver).

So, a few key points that I suspected were necessary, but which I still didn't understand:

  • Ultimately, the DI container must be aware of what logging platform to use. This is usually done by indicating that the "ILogger" will be resolved by a "resolver" that is specific to the logging platform (therefore, the lock has log4net, NLog, and System.Diagnostics "resolvers" (among others)). The specification of which you can use a recognizer. via the configuration file or programmatically.

  • The resonator must know the context for which the dependency (ILogger). What if MyClass is created and it depends on ILogger, then when the recognizer tries to create the correct ILogger, it (resolver) must know the current type (My classes). Thus, the resolver can use the basic implementation log (log4net, NLog, etc.) to obtain the correct logger.

These points may be obvious to those DI / IoC users, but I'm getting into it now, so it took me a while to plunge into it.

One thing I haven't figured out yet is how or something similar is possible with MEF. Can I have an object dependent on an interface and then execute my code after MEF has created the object and while the interface / dependency is resolved? So, suppose I have a class like this:

 public class MyClass { [Import(ILogger)] public ILogger logger; public MyClass() { } public void DoSomething() { logger.Info("Hello World!"); } } 

When MEF allows import for MyClass, can I use my own code (through an attribute through an additional interface to implement ILogger, somewhere else), execute and enable ILogger import based on the fact that it is MyClass, which is currently in context and returns a (potentially) different instance of ILogger than would be retrieved for YourClass? Am I implementing some kind of MEF provider?

At this point, I still do not know about MEF.

+13
Aug 19 '10 at 19:25
source share

I use Ninject to resolve the name of the current class for the log instance in this way:

 kernel.Bind<ILogger>().To<NLogLogger>() .WithConstructorArgument("currentClassName", x => x.Request.ParentContext.Request.Service.FullName); 

The NLog implementation constructor might look like this:

 public NLogLogger(string currentClassName) { _logger = LogManager.GetLogger(currentClassName); } 

This approach should also work with other IOC containers.

+36
Jun 26 '11 at 10:19
source share

You can also use the Common.Logging facade or Simple logging .

Both of them use the service locator style pattern to retrieve ILogger.

Honestly, registration is one of those dependencies that I see little with automatic input.

Most of my classes that require logging services look like this:

 public class MyClassThatLogs { private readonly ILogger log = Slf.LoggerService.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName); } 

Using the Simple Logging Facade, I switched the project from log4net to NLog, and I added registration from a third-party library that used log4net in addition to registering my application with NLog. That is, the facade served us well.

One caveat that is hard to avoid is the loss of features specific to a single logging system, or perhaps the most common example of which is user-level logging.

+15
Aug 19 '10 at 20:07
source share

I see that you understand your own answer :) But for people in the future who have this question about how NOT to attach themselves to a specific logging structure, this library: Common.Logging helps in this particular scenario.

+5
Aug 19 '10 at 19:38
source share

I made my own ServiceExportProvider, the provider registered log4Net logger for dependency injection using MEF. As a result, you can use a registrar for various types of injections.

Injection Example:

 [Export] public class Part { [ImportingConstructor] public Part(ILog log) { Log = log; } public ILog Log { get; } } [Export(typeof(AnotherPart))] public class AnotherPart { [Import] public ILog Log { get; set; } } 

Usage example:

 class Program { static CompositionContainer CreateContainer() { var logFactoryProvider = new ServiceExportProvider<ILog>(LogManager.GetLogger); var catalog = new AssemblyCatalog(typeof(Program).Assembly); return new CompositionContainer(catalog, logFactoryProvider); } static void Main(string[] args) { log4net.Config.XmlConfigurator.Configure(); var container = CreateContainer(); var part = container.GetExport<Part>().Value; part.Log.Info("Hello, world! - 1"); var anotherPart = container.GetExport<AnotherPart>().Value; anotherPart.Log.Fatal("Hello, world! - 2"); } } 

Result in the console:

 2016-11-21 13:55:16,152 INFO Log4Mef.Part - Hello, world! - 1 2016-11-21 13:55:16,572 FATAL Log4Mef.AnotherPart - Hello, world! - 2 

ServiceExportProvider implementation :

 public class ServiceExportProvider<TContract> : ExportProvider { private readonly Func<string, TContract> _factoryMethod; public ServiceExportProvider(Func<string, TContract> factoryMethod) { _factoryMethod = factoryMethod; } protected override IEnumerable<Export> GetExportsCore(ImportDefinition definition, AtomicComposition atomicComposition) { var cb = definition as ContractBasedImportDefinition; if (cb?.RequiredTypeIdentity == typeof(TContract).FullName) { var ce = definition as ICompositionElement; var displayName = ce?.Origin?.DisplayName; yield return new Export(definition.ContractName, () => _factoryMethod(displayName)); } } } 
0
Nov 21 '16 at 10:58
source share



All Articles