在上一篇博文中对log4j的结构有了一定的了解,这一篇就深入地了解一下其中的LogManager类。首先,先了解一下与LogManager相关的类结构;然后,再仔细地说明每个类的作用及其之间的关系。
1 类图
图中的方框代表类,圆圈代表接口,实线箭头代表关联关系(箭头的一方为非箭头一方的属性),虚线箭头代表依赖关系(箭头的一方为非箭头一方的方法参数或局部变量),实线三角箭头表示继承关系。
从图中可知,LogManager类有RepositorySelector属性和LoggerRepository属性,并且依赖于Configurator接口。LoggerRepository类有LoggerFactory属性。DefaultLoggerFactory类有Logger属性。接下来简要介绍一下这些类和接口的作用。
LogManager类将Configurator和LoggerRepository整合在一起,它在初始化的时候(static)找到Log4J配置文件,并且将其解析到LoggerRepository中。
Configurator用来处理相关的配置文件。Log4J支持两种配置文件:properties文件和xml文件。
RepositorySelector负责缓存和管理LoggerRepository对象。
LoggerRepository是一个Logger的容器,它会调用LoggerFactory创建Logger并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。调用Configurator对配置文件进行解析,并将解析后的信息添加到LoggerRepository中。同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以”.”分隔。
Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现。
2 RootLogger类
在Log4J中,所有Logger都组装成以RootLogger为根的一棵树。
public final class RootLogger extends Logger {
/**
* 使用日志级别来构造一个日志记录器,记录器名称默认为root。
*/
public RootLogger(Level level) {
super("root");
setLevel(level);
}
}
3 LoggerFactory类
这里使用了创建型设计模式的工厂(Factory)模式。所有需要创建新Logger的方法都可以调用该接口的makeNewLoggerInstance方法。
/**
*实现该接口可以创建新的日志记录器实例。
*/
public interface LoggerFactory {
public Logger makeNewLoggerInstance(String name);
}
4 LoggerFactory的实现类
该实现类为DefaultLoggerFactory。
public final class DefaultLoggerFactory implements LoggerFactory {
public DefaultLoggerFactory() {}
public Logger makeNewLoggerInstance(String name) {
return new Logger(name);
}
}
5 Configurator类
Configurator用来处理相关的配置文件。Log4J支持两种配置文件:properties文件和xml文件。如下是配置器接口:
public interface Configurator {
}
6 Configurator的抽象实现类
该抽象实现类是ConfiguratorBase类。
/**
* 该类包含了一个大部分配置类都需要的功能。
*/
abstract public class ConfiguratorBase implements Configurator {
}
7 ConfiguratorBase的property实现类
该实现类是PropertyConfigurator类,负责对property属性文件进行解析。
public class PropertyConfigurator extends ConfiguratorBase
implements ConfiguratorEx {
}
8 ConfiguratorBase的XML实现类
该实现类是JoranConfigurator类。负责对XML配置文件进行解析。
public class JoranConfigurator extends ConfiguratorBase
implements ConfiguratorEx {
}
9 LoggerRepository接口
LoggerRepository它是一个Logger的容器,它会调用LoggerFactory创建Logger并缓存Logger实例,从而具有相同名字的Logger实例不会多次创建,以提高性能。调用Configurator对配置文件进行解析,并将解析后的信息添加到LoggerRepository中。
同时它也维护了Logger之间的关系,因为在Log4J中,所有Logger都组装成以RootLogger为根的一棵树,树的层次由Logger的Name来决定,其中以”.”分隔。
除此之外,它还有一个Threshold属性,用于过滤所有在Threshold级别以下的日志。以及其他和Logger操作相关的方法和属性。
public interface LoggerRepository {
// 添加一个事件到repository.
public void addHierarchyEventListener(HierarchyEventListener listener);
public void setThreshold(Level level);
public void setThreshold(String val);
public Level getThreshold();
public Logger getLogger(String name);
public Logger getLogger(String name, LoggerFactory factory);
public Logger getRootLogger();
public abstract Logger exists(String name);
public abstract void shutdown();
public Enumeration getCurrentLoggers();
public abstract void fireAddAppenderEvent(Category logger, Appender appender);
public abstract void resetConfiguration();
}
10 LoggerRepository的子接口
public interface LoggerRepositoryEx extends LoggerRepository {
void addLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);
void removeLoggerRepositoryEventListener(LoggerRepositoryEventListener listener);
void addLoggerEventListener(LoggerEventListener listener);
void removeLoggerEventListener(LoggerEventListener listener);
String getName();
void setName(String repoName);
void fireRemoveAppenderEvent(Category logger, Appender appender);
void fireLevelChangedEvent(Logger logger);
void fireConfigurationChangedEvent();
PluginRegistry getPluginRegistry();
Map getProperties();
String getProperty(String key);
void setProperty(String key, String value);
void setLoggerFactory(LoggerFactory loggerFactory);
LoggerFactory getLoggerFactory();
}
11 LoggerRepositoryEx的实现类
该实现类是Hierarchy类。
Hierarchy是Log4J中默认对LoggerRepository的实现类,它用于表达其内部的Logger是以层次结构存储的。在对LoggerRepository接口的实现中,getLogger()方法是其最核心的实现。
Hierarchy中用一个Hashtable来存储所有Logger实例,它以CategoryKey作为key,Logger作为value,其中CategoryKey是对Logger中Name字符串的封装,之所以要引入这个类是出于性能考虑,因为它会缓存Name字符串的hash code,这样在查找过程中计算hash code时就可以直接取得而不用每次都计算。
class CategoryKey {
String name;
int hashCache;
CategoryKey(String name) {
this.name = name;
hashCache = name.hashCode();
}
}
getLogger()方法首先根据传入name创建CategoryKey实例,而后从缓存ht字段中查找:
1. 如果找到对应的Logger实例,则直接返回该实例。
2. 如果没有找到任何实例,则使用LoggerFactory创建新的Logger实例,并将该实例缓存到ht集合中,同时更新新创建Logger实例的parent属性。更新parent属性最简单的做法是从后往前以’.’为分隔符截取字符串,使用截取后的字符串从ht集合中查找是否存在Logger实例,如果存在,则新创建的Logger实例的parent即为找到的实例,若在整个遍历过程中都没有找到相应的parent实例,则其parent实例为root。然而如果一个“x.y.z.w”Logger起初的parent设置为root,而后出现“x.y.z”Logger实例,那么就需要更新“x.y.z.w”Logger的parent为“x.y.z”Logger实例,此时就会遇到一个如何找到在集合中已经存在的“x.y.z”Logger实例子节点的问题。当然一种简单的做法是遍历ht集合中所有实例,判断那个实例是不是“x.y.z”Logger实例的子节点,是则更新其parent节点。由于每次的遍历会引起一些性能问题,因而Log4J使用ProvisionNode事先将所有的可能相关的子节点保存起来,并将ProvisionNode实例添加到ht集合中,这样只要找到对应的ProvisionNode实例,就可以找到所有相关的子节点了。比如对“x.y.z.w”Logger实例,它会产生三个ProvisionNode实例(当然如果相应的实例已经存在,则直接添加而无需创建,另外,如果相应节点已经是Logger实例,那么将“x.y.z.w”Logger实例的parent直接指向它即可):ProvisionNode(“x”), ProvisionNode(“x.y”), ProvisionNode(“x.y.z”),他们都存储了“x.y.z.w”Logger实例作为其子节点。
public class Hierarchy implements LoggerRepositoryEx, RendererSupport {
private LoggerFactory loggerFactory;
private final ArrayList repositoryEventListeners;
private final ArrayList loggerEventListeners;
String name;
Hashtable ht; // Loggers所在的Hashtable.
Logger root; // Root logger.
/**
* 构造一个logger hierarchy.
*/
public Hierarchy(final Logger rootLogger) {
ht = new Hashtable();
repositoryEventListeners = new ArrayList(1);
loggerEventListeners = new ArrayList(1);
this.root = rootLogger;
this.objectMap = new HashMap();
// Enable all level levels by default.
setThreshold(Level.ALL);
this.root.setHierarchy(this);
rendererMap = new RendererMap();
rendererMap.setLoggerRepository(this);
properties = new Hashtable();
loggerFactory = new DefaultLoggerFactory();
}
public Logger getLogger(final String loggerName) {
return getLogger(loggerName, loggerFactory);
}
public Logger getLogger(final String loggerName, final LoggerFactory factory) {
CategoryKey key = new CategoryKey(loggerName);
Logger logger;
synchronized (ht) {
Object o = ht.get(key);
if (o == null) {
LogLog.debug("Creating new logger [" + loggerName + "] in repository [" + getName() + "].");
logger = factory.makeNewLoggerInstance(loggerName);
logger.setHierarchy(this);
ht.put(key, logger);
updateParents(logger);
return logger;
} else if (o instanceof Logger) {
LogLog.debug("Returning existing logger [" + loggerName + "] in repository [" + getName() + "].");
return (Logger) o;
} else if (o instanceof ProvisionNode) {
logger = factory.makeNewLoggerInstance(loggerName);
logger.setHierarchy(this);
ht.put(key, logger);
updateChildren((ProvisionNode) o, logger);
updateParents(logger);
return logger;
} else {
// 代码运行时不会到达这里。
return null; // 但是这样可以避免编译器告警。
}
}
}
}
12 RepositorySelector接口
RepositorySelector负责缓存和管理LoggerRepository对象。
public interface RepositorySelector {
public LoggerRepository getLoggerRepository();
}
13 RepositorySelector的子接口
该子接口为RepositorySelectorEx 接口。
public interface RepositorySelectorEx extends RepositorySelector {
LoggerRepository detachRepository(String name);
}
14 RepositorySelectorEx的实现类
该实现类为DefaultRepositorySelector类。
public class DefaultRepositorySelector implements RepositorySelectorEx {
private LoggerRepository defaultRepository;
public DefaultRepositorySelector(final LoggerRepository repository) {
if (repository == null)
throw new NullPointerException();
this.defaultRepository = repository;
}
public LoggerRepository getLoggerRepository() {
return defaultRepository;
}
}
15 LogManager类
LogManager将Configurator和LoggerRepository整合在一起,它在初始化的时候(static)找到Log4J配置文件,并且将其解析到LoggerRepository中。
public class LogManager {
private static RepositorySelector repositorySelector;
private static boolean debug = false;
/**
* 默认的LoggerRepository实例。
*/
public static final LoggerRepository defaultLoggerRepository;
// 初始化部分。
static {
String debugProp = System.getProperty("log4j.debug");
if(Boolean.valueOf(debugProp).booleanValue()) {
debug = true;
}
if(debug) {
System.out.println("**Start of LogManager static initializer");
}
Hierarchy hierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
defaultLoggerRepository = hierarchy;
hierarchy.setName(Constants.DEFAULT_REPOSITORY_NAME);
repositorySelector = new DefaultRepositorySelector (defaultLoggerRepository);
String configuratorClassName = OptionConverter.getSystemProperty(Constants.CONFIGURATOR_CLASS_KEY, null);
String configurationOptionStr = OptionConverter.getSystemProperty(Constants.DEFAULT_CONFIGURATION_KEY, null);
if (configurationOptionStr == null) {
if (Loader.getResource(Constants.DEFAULT_XML_CONFIGURATION_FILE) != null) {
configurationOptionStr = Constants.DEFAULT_XML_CONFIGURATION_FILE;
} else if (Loader.getResource(Constants.DEFAULT_CONFIGURATION_FILE) != null) {
configurationOptionStr = Constants.DEFAULT_CONFIGURATION_FILE;
}
}
if(debug) {
System.out.println("*** configurationOptionStr=" + configurationOptionStr);
}
// 整合Configurator和LoggerRepository部分。
IntializationUtil.initialConfiguration(defaultLoggerRepository, configurationOptionStr, configuratorClassName);
String repositorySelectorStr = OptionConverter.getSystemProperty("log4j.repositorySelector", null);
if (repositorySelectorStr == null) {
// 不做任何事情, 默认的repository已经在前面配置好了。
} else if (repositorySelectorStr.equalsIgnoreCase("JNDI")) {
if(debug) {
System.out.println("*** Will use ContextJNDISelector **");
}
repositorySelector = new ContextJNDISelector();
guard = new Object();
} else {
Object r = OptionConverter.instantiateByClassName(repositorySelectorStr, RepositorySelector.class, null);
if (r instanceof RepositorySelector) {
if(debug) {System.out.println("*** Using [" + repositorySelectorStr+ "] instance as repository selector.");
}
repositorySelector = (RepositorySelector) r;
guard = new Object();
} else {
if(debug) {System.out.println("*** Could not insantiate [" + repositorySelectorStr+ "] as repository selector.");
System.out.println("*** Using default repository selector");
}
repositorySelector = new DefaultRepositorySelector(defaultLoggerRepository);
}
}
if(debug) {
System.out.println("** End of LogManager static initializer");
}
}
public static Logger getLogger(String name) {
return repositorySelector.getLoggerRepository().getLogger(name);
}
}
相关推荐
Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式...
项目继承log4j时需要的jar,没有会报错java.lang.NoClassDefFoundError: org/apache/log4j/LogManager
Apache Log4j_1.2.17 完整依赖包,在jdk1.8.201中测试通过。使用教程https://www.tutorialspoint.com/springmvc/springmvc_log4j.htm
log4net.ILog log = log4net.LogManager.GetLogger("testApp.Logging");//获取一个日志记录器 log.Info(DateTime.Now.ToString() + ": login success");//写入一条新log 这样就将信息同时输出到控制台和写入到文件名...
ILog m_log = LogManager.GetLogger(type); m_log.Debug("这是一个Debug日志"); m_log.Info("这是一个Info日志"); m_log.Warn("这是一个Warn日志"); m_log.Error("这是一个Error日志"); m_log.Fatal("这是一个...
Apache Log4j 2是Log4j的升级版,对Log4j的前身Log4j 1.x进行了重大改进,并提供了Logback中可用的许多改进,同时解决了Logback体系结构中的一些固有问题。 在Github上拉取请求 通过发送拉取请求,您授予Apache ...
2.如果运行程序出现错误:“java.lang.NoClassDefFoundError: org/apache/log4j/LogManager”,这是因为项目缺少log4j.jar这个jar包 3.错误:“Exception in thread "main" java.lang.NoSuchMethodError: org.slf4j....
我们在写日志的时候首先要获取logger,在每一个使用log4j的项目都有很多个地方要获取logger,这些logger是真实的被实例化的Logger对象,他们有可能被分散在无数不同的类中,日志体系结构讲的是这些logger对象是如何...
一、log4cxx命名规则Logger由一个String类的名字识别,logger的名字是大小写敏感的,且名字之间具有继承的关系,子名有父名作为前缀,用点号.分隔。如:x.y是x.y.z的父亲。根logger (root logger)是所有logger的祖先...
Laravel开发-logmanager Laravel 5界面用于预览、下载和删除Laravel日志文件。
第一步:nuget安装log4net 2.0.8,或者下载let4net.dll直接引用dll; 第二步:引用包内的LogManager.dll; 第三步:把Config文件夹添加到项目。 完成,调用Log.Info("日志内容"); 即可使用
LogManager.zip,log4j的jdk logmanager实现log4j、log4j2和logback的jdk logmanager实现。SLF4JBridgehandler的替代品。
日志LogManager
于是,我去博客园搜了下,发现还真没有,全部都是传统.NET那一套,直接LogManager.GetLogger,于是趁着周末有时间,决定调研下这个问题。去巨硬官网快速浏览了下,扩展第三方日志的核心就在于两个接口:ILogger和...
Unity log dll , 使用方法 Logger.LogManager.EnableLog = true; Logger.LogManager.Log ("hello !");
作为Coding4fun 的一部分创建以检查 Nancy、Owin 和 SignalR ##配置将 appender 添加到您的应用程序 var attachable = (( log4net . Repository . Hierarchy . Hierarchy ) LogManager . GetRepository ()). Root...
------\pkg_Core\Interface\Log\Ix_LogManager.h ------\pkg_Core\Interface\Log\LogHelper.h ------\pkg_Core\Interface\Module\Cx_Module.h ------\pkg_Core\Interface\Module\Cx_Object.h ------\pkg_Core\...
log-writer.js LogWriter {ログファイル出力} 安装: 例子: var LogWriter = require ( 'log-writer' ) ; var writer = new LogWriter ( 'log-file-name-%s.log' ) ; writer . write ( 'write\r\n' ) ; writer . ...
一个具备日志分级,自动回滚机制的日志类封装,便于移植到自己的程序中,作为调试日志。
在上文中,进行了简单的log4配置搭建,也在实操中启用了log4net的配置。这里做了一下总结。 方式一: 在运行时编程配置,代码如下: class Program { private readonly static ILog log = InitILog(); //...