Motivation
Log is an indispensable function for the development of a software system. Whether it is problem tracing or status analysis, with log assistance, developers can trace the problem. For the implementation modules of these log functions, developers can choose the EventLog built in. net, or the third-party log4net .... And so on. There are so many implementation modules that can be used to simplify the workload of developers, but it also brings another problem: "The system adds dependencies between log implementation modules 」.
Suppose we are developing a user module, which uses EventLog to complete the log function. After a long period of verification, it is confirmed that the user module is stable and powerful. Another project now needs to use functions related to the user module, and this project uses log4net to complete the log function. At this time, it will be embarrassing to find that the new project and user module adopt two sets of implementation modules with different log functions.
Of course, during program development, developers can ignore this issue, and compilation will always pass the function normally. However, as a result, the deployment personnel who subsequently take over must understand the setting methods of two different implementation modules. Of course, this can also be handled by means of non-program development. However, when the system grows larger and the number of log modules increases, some part of the team will attack developers one day. In addition, for developers themselves, these additional Module Settings will increase the workload of developers more or less during unit testing of the development system.
This article introduces an inversion of logging implementation that applies the IOC mode. This implementation defines the responsibilities and interactions between objects and is used to reverse the system's dependency on log modules and solve the above problems. Make a record for yourself and hope to help developers who need it.
Implementation
Model column download
For more information, see the sample program content: inversion of logging.
(* The execution example can be executed normally only after the system administrator privilege is granted .)
Ilogger
To switch the Log Module, you must first create an abstract ilogger interface. So that the system can use the log function only by relying on this abstraction module. The Log Module for system replacement needs to implement this ilogger interface to provide the log function.
namespace CLK.Logging{ public enum Level { Error, Warning, Information, SuccessAudit, FailureAudit, }}
namespace CLK.Logging{ public interface ILogger { // Methods void Log(Level level, string message); void Log(Level level, string message, System.Exception exception); }}
Logmanager
Then proceed to process the logmanager object that generates the ilogger object. This logmanager object applies the singleton mode, so that only a unique logmanager object exists in the system, and only a unique logmanager object exists between different modules in the system. In this way, the ilogger object between different modules can be controlled by a unique logmanager object.
Logmanager is an abstract class that provides the function for generating ilogger objects and releasing their own objects. To implement the logmanager class, you need to implement these two interfaces to provide the function of managing the ilogger object lifecycle. In addition, different log settings may be required between different modules. Therefore, a string parameter is added to the function of generating the ilogger object to let the logmanager category identify different modules to generate different set ilogger objects. As for the function of releasing objects, only the entry for uniformly releasing resources is reserved for objects in the logmanager category.
namespace CLK.Logging{ public abstract class LogManager : IDisposable { // Singleton private static LogManager _current; public static LogManager Current { get { // Require if (_current == null) throw new InvalidOperationException(); // Return return _current; } set { // Require if (_current != null) throw new InvalidOperationException(); // Return _current = value; } } // Methods public abstract ILogger CreateLogger(string name); public abstract void Dispose(); } }
Eventlogmanager
In the sample program, the Log Module implements EventLog and writes log information to Windows event records. The related program code is as follows. Interested developers can spend some time learning. When they need to expand the Log Module (for example, log4net), they can add relevant implementations on their own.
namespace CLK.Logging.Implementation{ public static class EventLogLevelConvert { public static EventLogEntryType ToEventLogEntryType(Level level) { switch (level) { case Level.Error: return EventLogEntryType.Error; case Level.Warning: return EventLogEntryType.Warning; case Level.Information: return EventLogEntryType.Information; case Level.SuccessAudit: return EventLogEntryType.SuccessAudit; case Level.FailureAudit: return EventLogEntryType.FailureAudit; default: return EventLogEntryType.Error; } } }}
namespace CLK.Logging.Implementation{ public class EventLogLogger : ILogger { // Fields private readonly string _sourceName = null; // Constructors public EventLogLogger(string sourceName) { #region Contracts if (string.IsNullOrEmpty(sourceName) == true) throw new ArgumentException(); #endregion _sourceName = sourceName; } // Methods public void Log(Level level, string message) { this.Log(level, message, null); } public void Log(Level level, string message, Exception exception) { #region Contracts if (string.IsNullOrEmpty(message) == true) throw new ArgumentException(); #endregion if (EventLog.SourceExists(_sourceName)==false) { EventLog.CreateEventSource(_sourceName, null); } EventLog eventLog = new EventLog(); eventLog.Source = _sourceName; eventLog.WriteEntry(string.Format("Message={0}, Exception={1}", message, exception == null ? string.Empty : exception.Message), EventLogLevelConvert.ToEventLogEntryType(level)); } } }
namespace CLK.Logging.Implementation{ public class EventLogManager : LogManager { // Methods public override ILogger CreateLogger(string name) { return new EventLogLogger(name); } public override void Dispose() { } }}
Use
Usermodule. Logging. Logger
Next, write a virtual usermodule to demonstrate how to apply inversion of logging. First, add the namespace logging to the usermodule. You need to reference this namespace if you need to use the log function in the usermodule. In addition, create a logger object in the logging naming room, copy and paste the following program code into the logger object, and then write the logger object.
This logger object uses the component name as the identification and generates an ilogger object for log implementation by calling a unique logmanager object. In addition, the ilogger object function is encapsulated to become a static function that makes the system more convenient to use.
namespace UserModule.Logging{ internal static class Logger { // Singleton private static ILogger _currentLogger; private static ILogger CurrentLogger { get { if (_currentLogger == null) { _currentLogger = LogManager.Current.CreateLogger(typeof(Logger).Assembly.GetName().Name); } return _currentLogger; } } // static Methods public static void Log(Level level, string message) { CurrentLogger.Log(level, message); } public static void Log(Level level, string message, System.Exception exception) { CurrentLogger.Log(level, message); } }}
Usermodule. User
After writing these programs, you only need to use the abstract ilogger implementation to use the log function in the usermodule, instead of relying on the currently used log module.
namespace UserModule{ public class User { public void Test(string message) { // Do // Log Logger.Log(CLK.Logging.Level.Information, "OK"); } }}
Run
Finally, the usermodule system inversionofloggingsample is created, and logmanager is injected into the inversionofloggingsample. current is used to determine which Log Module the system uses. As long as the log information is in the same system, the log module is used to complete the log function.
Developers can use the di framework to generate a logmanager and inject it into logmanager. Current to complete the function of the log module. In unit test scenarios, emptylogger provided in the sample file can be injected to simplify the log settings during unit test.
namespace InversionOfLoggingSample{ class Program { static void Main(string[] args) { // Init LogManager.Current = new EventLogManager(); // Test User user = new User(); user.Test("Clark=_=y-~"); } }}