Apache log4cxx (0.10.0版)简介

Short introduction to Apache log4cxx
Apache log4cxx 简介

Introduction 介绍

Apache log4cxx is a logging framework for C++ patterned after Apache log4j . Apache log4cxx uses Apache Portable Runtime for most platform-specific code and should be usable on any platform supported by APR. Apache log4cxx is licensed under the Apache License , an open source license certified by the Open Source Initiative .
Apache log4cxx是Apache log4j之后的C++日志框架。Apache log4cxx 将 Apache Portable Runtime 用于大多数特定于平台的代码,并且应该可以在 APR 支持的任何平台上使用。Apache log4cxx 是在 Apache 许可证下授权的,这是一个由开源促进会认证的开源许可证。

Almost every large application includes its own logging or tracing API. Inserting log statements into code is a low-tech method for debugging it. It may also be the only way because debuggers are not always available or applicable. This is usually the case for multithreaded applications and distributed applications at large.
几乎每个大型应用程序都包含自己的日志记录或跟踪 API。在代码中插入日志语句是一种低技术含量的调试方法。这也可能是唯一的方法,因为调试器并不总是可用或适用。这通常适用于多线程应用程序和分布式应用程序。

Experience indicates that logging is an important component of the development cycle. It offeres several advantages. It provides precise context about a run of the application. Once inserted into the code, the generation of logging output requires no human intervention. Moreover, log output can be saved in persistent medium to be studied at a later time. In addition to its use in the development cycle, a sufficiently rich logging package can also be viewed as an auditing tool.
经 验表明,日志记录是开发周期的重要组成部分。它提供了几个优点。它提供有关应用程序运行的精确上下文。一旦插入到代码中,日志记录输出的生成就不需要人工 干预。此外,日志输出可以保存在持久性介质中,以便以后研究。除了在开发周期中使用之外,足够丰富的日志记录包还可以被视为一种审计工具。

Logging does have its drawbacks. It can slow down an application. If too verbose, it can cause scrolling blindness. To alleviate these concerns, log4cxx is designed to be reliable, fast and extensible. Since logging is rarely the main focus of an application, the log4cxx API strives to be simple to understand and to use.
日志记录确实有其缺点。它可能会减慢应用程序的速度。如果太冗长,可能会导致滚动盲。为了减轻这些顾虑,log4cxx 被设计为可靠、快速和可扩展的。由于日志记录很少是应用程序的主要关注点,因此 log4cxx API 力求易于理解和使用。

Loggers, Appenders and Layouts
记录器、附加器和布局

Log4cxx has three main components: loggers, appenders and layouts. These three types of components work together to enable developers to log messages according to message type and level, and to control at runtime how these messages are formatted and where they are reported.
Log4cxx 有三个主要组件:记录器、附加器和布局。这三种类型的组件协同工作,使开发人员能够根据消息类型和级别记录消息,并在运行时控制这些消息的格式和报告位置。

Logger hierarchy 记录器层次结构

The first and foremost advantage of any logging API over plain std::cout resides in its ability to disable certain log statements while allowing others to print unhindered. This capability assumes that the logging space, that is, the space of all possible logging statements, is categorized according to some developer-chosen criteria.
与普通 std::cout API 相比,任何日志记录 API 的首要优势在于它能够禁用某些日志语句,同时允许其他日志语句不受阻碍地打印。此功能假定日志记录空间(即所有可能的日志记录语句的空间)根据开发人员选择的某些条件进行分类。

Loggers are named entities. Logger names are case-sensitive and they follow the hierarchical naming rule:
记录器是命名实体。记录器名称区分大小写,并遵循分层命名规则:

Named Hierarchy 命名层次结构

A logger is said to be an ancestor of another logger if its name followed by a dot is a prefix of the descendant logger name. A logger is said to be a parent of a child logger if there are no ancestors between itself and the descendant logger.
如果一个记录器的名称后跟一个点是后代记录器名称的前缀,则称其为另一个记录器的祖先。如果记录器与后代记录器之间没有祖先,则称其为子记录器的父级。

For example, the logger named "com.foo" is a parent of the logger named "com.foo.Bar". Similarly, "java" is a parent of "java.util" and an ancestor of "java.util.Vector". This naming scheme should be familiar to most developers.
例如,命名 "com.foo" 的记录器是名为 "com.foo.Bar" 的记录器的父级。同样, "java""java.util.Vector" 的父 "java.util" 级和祖先。大多数开发人员应该熟悉此命名方案。

The root logger resides at the top of the logger hierarchy. It is exceptional in two ways:
根记录器位于记录器层次结构的顶部。它在两个方面非常出色:

  1. it always exists, 它总是存在的,
  2. it cannot be retrieved by name.
    无法按名称检索它。

Invoking the class static log4cxx::Logger::getRootLogger method retrieves it. All other loggers are instantiated and retrieved with the class static log4cxx::Logger::getLogger method. This method takes the name of the desired logger as a parameter. Some of the basic methods in the Logger class are listed below.
调 用类 static log4cxx::Logger::getRootLogger 方法将检索它。所有其他记录器都使用类静态 log4cxx::Logger::getLogger 方法实例化和检索。此方法将所需记录器的名称作为参数。下面列出了 Logger 类中的一些基本方法。

  namespace log4cxx {

    class Logger
 {
       public:
       // Creation & retrieval methods:
       static LoggerPtr getRootLogger();
       static LoggerPtr getLogger(const std::string& name);
       static LoggerPtr getLogger(const std::wstring& name);

     }
   }
//
//   Use these macros instead of calling Logger methods directly.
//   Macros will handle char or wchar_t pointers or strings
//   or most right-hand side expressions of an 
//   std::basic_string::operator<<.
//   
#define LOG4CXX_TRACE(logger, expression) ...   
#define LOG4CXX_DEBUG(logger, expression) ...   
#define LOG4CXX_INFO(logger, expression) ...   
#define LOG4CXX_WARN(logger, expression) ...   
#define LOG4CXX_ERROR(logger, expression) ...   
#define LOG4CXX_FATAL(logger, expression) ...   

Loggers may be assigned levels. The pre-defined levels: TRACE, DEBUG, INFO, WARN, ERROR and FATAL are defined in the log4cxx::Level class which provides accessor functions.
记录器可以被分配级别。预定义的级别:TRACE、DEBUG、INFO、WARN、ERROR 和 FATAL 在提供访问器函数的 log4cxx::Level 类中定义。

If a given logger is not assigned a level, then it inherits one from its closest ancestor with an assigned level. More formally:
如果给定的记录器未分配级别,则它会从其最接近的祖先那里继承一个具有指定级别的记录器。更正式地说:

Level Inheritance 级别继承

The inherited level for a given logger C , is equal to the first non-null level in the logger hierarchy, starting at C and proceeding upwards in the hierarchy towards the root logger.
给定记录器 C 的继承级别等于记录器层次结构中的第一个非空级别,从 C 开始,在层次结构中向上延伸到 root 记录器。

To ensure that all loggers can eventually inherit a level, the root logger always has an assigned level.
为了确保所有记录器最终都可以继承一个级别,根记录器始终具有分配的级别。

Below are four tables with various assigned level values and the resulting inherited levels according to the above rule.
下面是四个表,其中包含各种分配的级别值以及根据上述规则生成的继承级别。

Example 1 示例 1
Logger 日志
name 名字
Assigned 分配
level 水平
Inherited 继承
level 水平
rootProotProot
XnoneProot
X.YnoneProot
X.Y.ZnoneProot

In example 1 above, only the root logger is assigned a level. This level value, Proot, is inherited by the other loggers X, X.Y and X.Y.Z.
在上面的示例 1 中,仅为根记录器分配了一个级别。此级别值 Proot ,由其他记录器继承 XX.Y 并且 X.Y.Z

Example 2 示例 2
Logger 日志
name 名字
Assigned 分配
level 水平
Inherited 继承
level 水平
rootProotProot
XPxPx
X.YPxyPxy
X.Y.ZPxyzPxyz

In example 2, all loggers have an assigned level value. There is no need for level inheritence.
在示例 2 中,所有记录器都有一个分配的级别值。不需要级别继承。

Example 3 示例 3
Logger 日志
name 名字
Assigned 分配
level 水平
Inherited 继承
level 水平
rootProotProot
XPxPx
X.YnonePx
X.Y.ZPxyzPxyz

In example 3, the loggers root, X and X.Y.Z are assigned the levels Proot, Px and Pxyz respectively. The logger X.Y inherits its level value from its parent X.
在示例 3 中,记录器 rootX X.Y.Z Pxyz 分别被分配了级别 ProotPx 。记录器 X.Y 从其父 X 级继承其级别值。

Example 4 示例 4
Logger 日志
name 名字
Assigned 分配
level 水平
Inherited 继承
level 水平
rootProotProot
XPxPx
X.YnonePx
X.Y.ZnonePx

In example 4, the loggers root and X and are assigned the levels Proot and Px respectively. The loggers X.Y and X.Y.Z inherits their level value from their nearest parent X having an assigned level.
在示例 4 中,记录器 rootX 分别被分配了级别 ProotPx 。记录器 X.YX.Y.Z 从具有分配级别的最近的父级 X 继承其级别值。

Logging requests are made by invoking a method of a logger instance, preferrably through the use of LOG4CXX_INFO or similar macros which support short-circuiting if the threshold is not satisfied and use of the insertion operator (<<) in the message parameter.
日志记录请求是通过调用记录器实例的方法发出的,最好通过使用LOG4CXX_INFO或类似的宏来发出,这些宏在阈值不满足时支持短路,并在消息参数中使用插入运算符 (<<)。

   log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("com.foo"));
   const char* region = "World";
   LOG4CXX_INFO(logger, "Simple message text.");
   LOG4CXX_INFO(logger, "Hello, " << region);
   LOG4CXX_DEBUG(logger, L"Iteration " << i);
   LOG4CXX_DEBUG(logger, "e^10 = " << std::scientific << exp(10.0));
   //
   //  Use a wchar_t first operand to force use of wchar_t based stream.    
   //
   LOG4CXX_WARN(logger, L"" << i << L" is the number of the iteration.");

A logging request is said to be enabled if its level is higher than or equal to the level of its logger. Otherwise, the request is said to be disabled. A logger without an assigned level will inherit one from the hierarchy. This rule is summarized below.
如果日志记录请求的级别高于或等于其记录器的级别,则称其已启用。否则,该请求将被禁用。没有分配级别的记录器将从层次结构中继承一个记录器。该规则总结如下。

Basic Selection Rule 基本选择规则

A log request of level p in a logger with (either assigned or inherited, whichever is appropriate) level q , is enabled if p >= q .
如果 p >= q,则在具有(分配或继承,以适当者为准)级别的记录器中启用级别 p 的日志请求。

This rule is at the heart of log4cxx. It assumes that levels are ordered. For the standard levels, we have TRACE < DEBUG < INFO < WARN < ERROR < FATAL.
此规则是 log4cxx 的核心。它假定级别是有序的。对于标准级别,我们有 TRACE < DEBUG < INFO < WARN < ERROR < FATAL .

Here is an example of this rule.
下面是此规则的示例。

   // get a logger instance named "com.foo"
   log4cxx::LoggerPtr  logger(log4cxx::Logger::getLogger("com.foo"));

   // Now set its level. Normally you do not need to set the
   // level of a logger programmatically. This is usually done
   // in configuration files.
   logger->setLevel(log4cxx::Level::getInfo());

   log4cxx::LoggerPtr barlogger(log4cxx::Logger::getLogger("com.foo.Bar");

   // This request is enabled, because WARN >= INFO.
   LOG4CXX_WARN(logger, "Low fuel level.");

   // This request is disabled, because DEBUG < INFO.
   LOG4CXX_DEBUG(logger, "Starting search for nearest gas station.");

   // The logger instance barlogger, named "com.foo.Bar",
   // will inherit its level from the logger named
   // "com.foo" Thus, the following request is enabled
   // because INFO >= INFO.
   LOG4CXX_INFO(barlogger. "Located nearest gas station.");

   // This request is disabled, because DEBUG < INFO.
   LOG4CXX_DEBUG(barlogger, "Exiting gas station search");

Calling the getLogger method with the same name will always return a reference to the exact same logger object.
使用相同的名称 getLogger 调用方法将始终返回对完全相同的记录器对象的引用。

For example, in  例如,在

   log4cxx::LoggerPtr x = log4cxx::Logger::getLogger("wombat");
   log4cxx::LoggerPtr y = log4cxx::Logger::getLogger("wombat");
x and y refer to exactly the same logger object.
xy 引用完全相同的记录器对象。

Thus, it is possible to configure a logger and then to retrieve the same instance somewhere else in the code without passing around references. In fundamental contradiction to biological parenthood, where parents always preceed their children, log4cxx loggers can be created and configured in any order. In particular, a "parent" logger will find and link to its descendants even if it is instantiated after them.
因此,可以配置记录器,然后在代码中的其他位置检索相同的实例,而无需传递引用。与亲生父母身份的根本矛盾是,父母总是先于孩子,log4cxx 记录器可以按任何顺序创建和配置。特别是,“父”记录器将找到并链接到其后代,即使它是在它们之后实例化的。

Configuration of the log4cxx environment is typically done at application initialization. The preferred way is by reading a configuration file. This approach will be discussed shortly.
log4cxx 环境的配置通常在应用程序初始化时完成。首选方法是读取配置文件。稍后将讨论这种方法。

Log4cxx makes it easy to name loggers by software component. This can be accomplished by statically instantiating a logger in each class, with the logger name equal to the fully qualified name of the class. This is a useful and straightforward method of defining loggers. As the log output bears the name of the generating logger, this naming strategy makes it easy to identify the origin of a log message. However, this is only one possible, albeit common, strategy for naming loggers. Log4cxx does not restrict the possible set of loggers. The developer is free to name the loggers as desired.
Log4cxx 可以很容易地按软件组件命名记录器。这可以通过静态实例化每个类中的记录器来实现,记录器名称等于类的完全限定名称。这是定义记录器的有用且直接的方法。 由于日志输出带有生成记录器的名称,因此此命名策略可以轻松识别日志消息的来源。但是,这只是命名记录器的一种可能(尽管很常见)的策略。Log4cxx 不限制可能的记录器集。开发人员可以根据需要自由命名记录器。

Nevertheless, naming loggers after the class where they are located seems to be the best strategy known so far.
尽管如此,以记录器所在的类命名记录器似乎是迄今为止已知的最佳策略。

Appenders and Layouts Appenders 和布局

The ability to selectively enable or disable logging requests based on their logger is only part of the picture. Log4cxx allows logging requests to print to multiple destinations. In log4cxx speak, an output destination is called an appender. Currently, appenders exist for the console , files , GUI components, remote socket servers, NT Event Loggers , and remote UNIX Syslog daemons. It is also possible to log asynchronously .
根 据记录器有选择地启用或禁用日志记录请求的能力只是其中的一部分。Log4cxx 允许将日志记录请求打印到多个目标。在 log4cxx 中,输出目标称为 appender。目前,控制台、文件、GUI 组件、远程套接字服务器、NT 事件记录器和远程 UNIX Syslog 守护程序存在附加程序。也可以异步记录。

More than one appender can be attached to a logger.
记录器可以附加多个附加器。

The addAppender method adds an appender to a given logger. Each enabled logging request for a given logger will be forwarded to all the appenders in that logger as well as the appenders higher in the hierarchy. In other words, appenders are inherited additively from the logger hierarchy. For example, if a console appender is added to the root logger, then all enabled logging requests will at least print on the console. If in addition a file appender is added to a logger, say C, then enabled logging requests for C and C's children will print on a file and on the console. It is possible to override this default behavior so that appender accumulation is no longer additive by setting the additivity flag to false.
addAppender 方法将 appender 添加到给定的记录器。给定记录器的每个启用的日志记录请求都将转发到该记录器中的所有追加者以及层次结构中更高的追加者。换言之,appender 是从记录器层次结构中累加继承的。例如,如果将控制台追加器添加到根记录器中,则所有启用的日志记录请求将至少打印在控制台上。此外,如果将文件追加器添 加到记录器(例如 C),则为 C 和 C 的子项启用的日志记录请求将打印在文件和控制台上。可以通过将 additivity 标志设置为 false 来覆盖此默认行为,以便 appender 累积不再是累加的。

The rules governing appender additivity are summarized below.
下面总结了控制附加物加性的规则。

Appender Additivity Appender 加性

The output of a log statement of logger C will go to all the appenders in C and its ancestors. This is the meaning of the term "appender additivity".
记录器 C 的日志语句的输出将转到 C 中的所有附加器及其祖先。这就是术语“附加物可加性”的含义。

However, if an ancestor of logger C , say P , has the additivity flag set to false, then C 's output will be directed to all the appenders in C and it's ancestors upto and including P but not the appenders in any of the ancestors of P .
但是,如果记录器 C 的祖先(比如 P)将加法标志设置为 false ,那么 C 的输出将定向到 C 中的所有附加器,并且它的祖先包括 P,但不会指向 P 的任何祖先中的附加器。

Loggers have their additivity flag set to true by default.
默认情况下,记录器的可加性标志设置为 true

The table below shows an example:
下表显示了一个示例:

Logger 日志
Name  名字
Added 添加
Appenders 附录
Additivity 添加性
Flag 旗
Output Targets 输出目标Comment
rootA1not applicable  不適用A1The root logger is anonymous but can be accessed with the log4cxx::Logger::getRootLogger() method. There is no default appender attached to root.
根记录器是匿名的,但可以使用 log4cxx::Logger::getRootLogger() 方法访问。没有附加到 root 的默认 appender。
xA-x1, A-x2trueA1, A-x1, A-x2 A1、A-x1、A-x2Appenders of "x" and root.
“x”和根的附加项。
x.ynonetrueA1, A-x1, A-x2 A1、A-x1、A-x2Appenders of "x" and root.
“x”和根的附加项。
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1
A1、A-x1、A-x2、A-xyz1
Appenders in "x.y.z", "x" and root.
“x.y.z”、“x”和根目录中的附加器。
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false.
由于加性标志设置为 false ,因此没有 appender 累积。
security.access  安全访问nonetrueA-secOnly appenders of "security" because the additivity flag in "security" is set to false.
只有 “security” 的附加项,因为 “security” 中的可加性标志设置为 false

More often than not, users wish to customize not only the output destination but also the output format. This is accomplished by associating a layout with an appender. The layout is responsible for formatting the logging request according to the user's wishes, whereas an appender takes care of sending the formatted output to its destination.
通常情况下,用户不仅希望自定义输出目的地,还希望自定义输出格式。这是通过将布局与附加器相关联来实现的。布局负责根据用户的意愿格式化日志记录请求,而 appender 负责将格式化的输出发送到其目标。

The PatternLayout , part of the standard log4cxx distribution, lets the user specify the output format according to conversion patterns similar to the C language printf function.
PatternLayout 是标准 log4cxx 发行版的一部分,允许用户根据类似于 C 语言 printf 函数的转换模式指定输出格式。

For example, the PatternLayout with the conversion pattern "%r [%t] %-5p %c - %m%n" will output something akin to:
例如,转换模式为 “%r [%t] %-5p %c - %m%n” 的 PatternLayout 将输出类似于以下内容的内容:

176 [main] INFO  org.foo.Bar - Located nearest gas station.

The first field is the number of milliseconds elapsed since the start of the program. The second field is the thread making the log request. The third field is the level of the log statement. The fourth field is the name of the logger associated with the log request. The text after the '-' is the message of the statement.
第一个字段是自程序启动以来经过的毫秒数。第二个字段是发出日志请求的线程。第三个字段是日志语句的级别。第四个字段是与日志请求关联的记录器的名称。“-”后面的文本是语句的消息。

Configuration 配置

Inserting log requests into the application code requires a fair amount of planning and effort. Observation shows that approximately 4 percent of code is dedicated to logging. Consequently, even moderately sized applications will have thousands of logging statements embedded within their code. Given their number, it becomes imperative to manage these log statements without the need to modify them manually.
将日志请求插入到应用程序代码中需要大量的规划和工作。观察表明,大约 4% 的代码专用于日志记录。因此,即使是中等大小的应用程序也会在其代码中嵌入数千条日志记录语句。鉴于它们的数量,必须管理这些日志语句,而无需手动修改它们。

The log4cxx environment is fully configurable programmatically. However, it is far more flexible to configure log4cxx using configuration files. Currently, configuration files can be written in XML or in Java properties (key=value) format.
log4cxx 环境完全可以通过编程方式进行配置。但是,使用配置文件配置 log4cxx 要灵活得多。目前,配置文件可以以 XML 或 Java 属性 (key=value) 格式编写。

Let us give a taste of how this is done with the help of an imaginary application MyApp that uses log4cxx.
让我们来了解一下如何在使用 log4cxx 的假想应用程序 MyApp 的帮助下完成此操作。

#include "com/foo/bar.h"
using namespace com::foo;

// include log4cxx header files.
#include "log4cxx/logger.h"
#include "log4cxx/basicconfigurator.h"
#include "log4cxx/helpers/exception.h"

using namespace log4cxx;
using namespace log4cxx::helpers;

LoggerPtr logger(Logger::getLogger("MyApp"));

int main(int argc, char **argv)
{
        int result = EXIT_SUCCESS;
        try
        {
                // Set up a simple configuration that logs on the console.
                BasicConfigurator::configure();

                LOG4CXX_INFO(logger, "Entering application.");
                Bar bar;
                bar.doIt();
                LOG4CXX_INFO(logger, "Exiting application.");
        }
        catch(Exception&)
        {
                result = EXIT_FAILURE;
        }

        return result;
}

MyApp begins by including log4cxx headers. It then defines a static logger variable with the name MyApp which happens to be the fully qualified name of the class.
MyApp 首先包括 log4cxx 标头。然后,它定义一个静态记录器变量,其名称 MyApp 恰好是类的完全限定名称。

MyApp uses the Bar class defined in header file com/foo/bar.h.
MyApp 使用头文件中 com/foo/bar.h 定义的 Bar 类。

//  file com/foo/bar.h
#include "log4cxx/logger.h"

namespace com {
   namespace foo {
      class Bar {
          static log4cxx::LoggerPtr logger;
          
      public:
          void doIt();
       }
    }
}
// file bar.cpp
#include "com/foo/bar.h"

using namespace com::foo;
using namespace log4cxx;

LoggerPtr Bar::logger(Logger::getLogger("com.foo.bar"));

void Bar::doIt() {
   LOG4CXX_DEBUG(logger, "Did it again!");
}

The invocation of the BasicConfigurator::configure method creates a rather simple log4cxx setup. This method is hardwired to add to the root logger a ConsoleAppender . The output will be formatted using a PatternLayout set to the pattern "%-4r [%t] %-5p %c %x - %m%n".
调 用 BasicConfigurator::configure 方法会创建一个相当简单的 log4cxx 设置。此方法是硬连线的,用于将 ConsoleAppender 添加到根记录器。输出将使用设置为模式“%-4r [%t] %-5p %c %x - %m%n”的 PatternLayout 进行格式化。

Note that by default, the root logger is assigned to Level::getDebug().
请注意,默认情况下,根记录器分配给 Level::getDebug()

The output of MyApp is:
MyApp 的输出为:

0    [12345] INFO  MyApp  - Entering application.
36   [12345] DEBUG com.foo.Bar  - Did it again!
51   [12345] INFO  MyApp  - Exiting application.

The previous example always outputs the same log information. Fortunately, it is easy to modify MyApp so that the log output can be controlled at run-time. Here is a slightly modified version.
前面的示例始终输出相同的日志信息。幸运的是,它很容易修改 MyApp ,因此可以在运行时控制日志输出。这是一个略微修改的版本。

// file MyApp2.cpp

#include "com/foo/bar.h"
using namespace com::foo;

// include log4cxx header files.
#include "log4cxx/logger.h"
#include "log4cxx/basicconfigurator.h"
#include "log4cxx/propertyconfigurator.h"
#include "log4cxx/helpers/exception.h"

using namespace log4cxx;
using namespace log4cxx::helpers;
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
LoggerPtr logger(Logger::getLogger("MyApp"));

int main(int argc, char **argv)
{
        int result = EXIT_SUCCESS;
        try
        {
                if (argc > 1)
                {
                        // BasicConfigurator replaced with PropertyConfigurator.
                        PropertyConfigurator::configure(argv[1]);
                }
                else
                {
                        BasicConfigurator::configure();
                }

                LOG4CXX_INFO(logger, "Entering application.");
                Bar bar
                bar.doIt();
                LOG4CXX_INFO(logger, "Exiting application.");
        }
        catch(Exception&)
        {
                result = EXIT_FAILURE;
        }

        return result;
}

This version of MyApp instructs PropertyConfigurator to parse a configuration file and set up logging accordingly.
此版本 MyApp 指示 PropertyConfigurator 解析配置文件并相应地设置日志记录。

Here is a sample configuration file that results in exactly same output as the previous BasicConfigurator based example.
下面是一个示例配置文件,其输出与上一个 BasicConfigurator 基于示例的输出完全相同。

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

# A1 is set to be a ConsoleAppender.
log4j.appender.A1=org.apache.log4j.ConsoleAppender

# A1 uses PatternLayout.
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

It can be noticed that the PropertyConfigurator file format is the same as log4j.
可以注意到,PropertyConfigurator 文件格式与 log4j 相同。

Suppose we are no longer interested in seeing the output of any component belonging to the com::foo package. The following configuration file shows one possible way of achieving this.
假设我们不再有兴趣查看属于包的任何 com::foo 组件的输出。以下配置文件显示了实现此目的的一种可能方法。

log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN

The output of MyApp configured with this file is shown below.
使用此文件配置的 MyApp 输出如下所示。

2000-09-07 14:07:41,508 [12345] INFO  MyApp - Entering application.
2000-09-07 14:07:41,529 [12345] INFO  MyApp - Exiting application.

As the logger com.foo.Bar does not have an assigned level, it inherits its level from com.foo, which was set to WARN in the configuration file. The log statement from the Bar::doIt method has the level DEBUG, lower than the logger level WARN. Consequently, doIt() method's log request is suppressed.
由于记录器 com.foo.Bar 没有分配的级别,因此它继承了其级别 com.foo ,该级别在配置文件中设置为 WARN。 Bar::doIt 该方法的日志语句的级别为 DEBUG,低于记录器级别 WARN。因此, doIt() 方法的日志请求将被禁止显示。

Here is another configuration file that uses multiple appenders.
这是另一个使用多个附加器的配置文件。

log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

Calling the enhanced MyApp with the this configuration file will output the following on the console.
使用此配置文件调用增强的 MyApp 将在控制台上输出以下内容。

 INFO [12345] (MyApp2.cpp:31) - Entering application.
DEBUG [12345] (Bar.h:16) - Doing it again!
 INFO [12345] (MyApp2.cpp:34) - Exiting application.

In addition, as the root logger has been allocated a second appender, output will also be directed to the example.log file. This file will be rolled over when it reaches 100KB. When roll-over occurs, the old version of example.log is automatically moved to example.log.1.
此外,由于已为根记录器分配了第二个附加器,因此输出也将定向到 example.log 该文件。此文件将在达到 100KB 时滚动。发生滚动更新时,旧版本会 example.log 自动移动到 example.log.1

Note that to obtain these different logging behaviors we did not need to recompile code. We could just as easily have logged to a UNIX Syslog daemon, redirected all com.foo output to an NT Event logger, or forwarded logging events to a remote log4cxx server, which would log according to local server policy, for example by forwarding the log event to a second log4cxx server.
请注意,要获取这些不同的日志记录行为,我们不需要重新编译代码。我们可以很容易地记录到 UNIX Syslog 守护程序,将所有 com.foo 输出重定向到 NT 事件记录器,或将日志记录事件转发到远程 log4cxx 服务器,该服务器将根据本地服务器策略进行记录,例如将日志事件转发到第二个 log4cxx 服务器。

Default Initialization Procedure
默认初始化过程

The log4cxx library does not make any assumptions about its environment. In particular, there are no default log4cxx appenders. Under certain well-defined circumstances however, the static inializer of the Logger class will attempt to automatically configure log4cxx.
log4cxx 库不对其环境做出任何假设。特别是,没有默认的 log4cxx 追加器。但是,在某些明确定义的情况下, Logger 类的静态初始化程序将尝试自动配置 log4cxx。

The exact default initialization algorithm is defined as follows:
确切的默认初始化算法定义如下:

  1. Set the configurationOptionStr string variable to the value of the LOG4CXX_CONFIGURATION environment variable if set, otherwise the value of the log4j.configuration or LOG4CXX_CONFIGURATION environment variable if set, otherwise the first of the following file names which exist in the current working directory, "log4cxx.xml", "log4cxx.properties", "log4j.xml" and "log4j.properties". If configurationOptionStr has not been set, then disable logging.
    将 configurationOptionStr 字符串变量设置为LOG4CXX_CONFIGURATION环境变量的值(如果已设置),否则设置为 log4j.configuration 的值或LOG4CXX_CONFIGURATION环境变量的值(如果设置),否则设置为当前工作目录中存在的以下文件名中的第一 个:“log4cxx.xml”、“log4cxx.properties”、“log4j.xml”和“log4j.properties”。如果尚未 设置 configurationOptionStr,则禁用日志记录。
  2. Unless a custom configurator is specified using the LOG4CXX_CONFIGURATOR_CLASS or log4j.configuratorClass environment variable, the PropertyConfigurator will be used to configure log4cxx unless the file name ends with the ".xml" extension, in which case the DOMConfigurator will be used. If a custom configurator is specified, the environment variable should contain a fully qualified class name of a class that implements the Configurator interface.
    除 非使用 LOG4CXX_CONFIGURATOR_CLASS 或 log4j.configuratorClass 环境变量指定自定义配置器,否则将使用 PropertyConfigurator 来配置 log4cxx,除非文件名以“.xml”扩展名结尾,在这种情况下,将使用 DOMConfigurator。如果指定了自定义配置器,则环境变量应包含实现 Configurator 接口的类的完全限定类名。

Nested Diagnostic Contexts
嵌套诊断上下文

Most real-world systems have to deal with multiple clients simultaneously. In a typical multithreaded implementation of such a system, different threads will handle different clients. Logging is especially well suited to trace and debug complex distributed applications. A common approach to differentiate the logging output of one client from another is to instantiate a new separate logger for each client. This promotes the proliferation of loggers and increases the management overhead of logging.
大 多数现实世界的系统必须同时处理多个客户端。在这种系统的典型多线程实现中,不同的线程将处理不同的客户端。日志记录特别适合跟踪和调试复杂的分布式应用 程序。区分一个客户端的日志记录输出与另一个客户端的常用方法是为每个客户端实例化一个新的单独的记录器。这促进了伐木器的激增,并增加了伐木的管理开 销。

A lighter technique is to uniquely stamp each log request initiated from the same client interaction. Neil Harrison described this method in the book "Patterns for Logging Diagnostic Messages," in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997).
一 种更轻量级的技术是对从同一客户端交互发起的每个日志请求进行唯一标记。Neil Harrison 在 R. Martin、D. Riehle 和 F. Buschmann 编辑的 Pattern Languages of Program Design 3 (Addison-Wesley, 1997) 一书中的“Patterns for Logging Diagnostic Messages”一书中描述了这种方法。

To uniquely stamp each request, the user pushes contextual information into the NDC, the abbreviation of Nested Diagnostic Context. The NDC class is shown below.
为了唯一地标记每个请求,用户将上下文信息推送到 NDC(嵌套诊断上下文的缩写)中。NDC 类如下所示。

namespace log4cxx {
  class NDC {
  public:
    //  pushes the value on construction and pops on destruction.
    NDC(const std::string& value);
    NDC(const std::wstring& value);

    // Remove the top of the context from the NDC.
    static LogString pop();

    // Add diagnostic context for the current thread.
    static void push(const std::string& message);
    static void push(const std::wstring& message);
  }

The NDC is managed per thread as a stack of contextual information. Note that all methods of the log4cxx::NDC class are static. Assuming that NDC printing is turned on, every time a log request is made, the appropriate log4cxx component will include the entire NDC stack for the current thread in the log output. This is done without the intervention of the user, who is responsible only for placing the correct information in the NDC by using the push and pop methods at a few well-defined points in the code. In contrast, the per-client logger approach commands extensive changes in the code.
NDC 作为上下文信息堆栈按线程进行管理。请注意,该 log4cxx::NDC 类的所有方法都是静态的。假设 NDC 打印处于打开状态,则每次发出日志请求时,相应的 log4cxx 组件都会在日志输出中包含当前线程的整个 NDC 堆栈。这是在没有用户干预的情况下完成的,用户只负责通过在代码中几个明确定义的 push 点使用 and pop 方法将正确的信息放入 NDC 中。相比之下,每个客户端的记录器方法会命令对代码进行大量更改。

To illustrate this point, let us take the example of a servlet delivering content to numerous clients. The servlet can build the NDC at the very beginning of the request before executing other code. The contextual information can be the client's host name and other information inherent to the request, typically information contained in cookies. Hence, even if the servlet is serving multiple clients simultaneously, the logs initiated by the same code, i.e. belonging to the same logger, can still be distinguished because each client request will have a different NDC stack. Contrast this with the complexity of passing a freshly instantiated logger to all code exercised during the client's request.
为 了说明这一点,让我们以 Servlet 向众多客户端提供内容为例。Servlet 可以在请求的一开始就构建 NDC,然后再执行其他代码。上下文信息可以是客户端的主机名和请求固有的其他信息,通常是 Cookie 中包含的信息。因此,即使 servlet 同时为多个客户端提供服务,仍然可以区分由同一代码启动的日志,即属于同一记录器的日志,因为每个客户端请求将具有不同的 NDC 堆栈。与此形成鲜明对比的是,将新实例化的记录器传递给客户端请求期间执行的所有代码的复杂性。

Nevertheless, some sophisticated applications, such as virtual hosting web servers, must log differently depending on the virtual host context and also depending on the software component issuing the request. Recent log4cxx releases support multiple hierarchy trees. This enhancement allows each virtual host to possess its own copy of the logger hierarchy.
然而,一些复杂的应用程序,如虚拟主机 Web 服务器,必须根据虚拟主机上下文以及发出请求的软件组件进行不同的记录。最近的 log4cxx 版本支持多个层次结构树。此增强功能允许每个虚拟主机拥有自己的记录器层次结构副本。

Performance 性能

One of the often-cited arguments against logging is its computational cost. This is a legitimate concern as even moderately sized applications can generate thousands of log requests. Much effort was spent measuring and tweaking logging performance. Log4cxx claims to be fast and flexible: speed first, flexibility second.
反对日志记录的经常被引用的论点之一是它的计算成本。这是一个合理的担忧,因为即使是中等大小的应用程序也会生成数千个日志请求。我们花了很多精力来测量和调整日志记录性能。Log4cxx 声称快速而灵活:速度第一,灵活性第二。

The user should be aware of the following performance issues.
用户应注意以下性能问题。

  1. Logging performance when logging is turned off.
    关闭日志记录时的日志记录性能。

    When logging is turned off entirely or just for a set of levels, the cost of a log request consists of a method invocation plus an integer comparison. The LOG4CXX_DEBUG and similar macros suppress unnecessary expression evaluation if the request is not enabled.
    当完全关闭日志记录或仅关闭一组级别的日志记录时,日志请求的成本包括方法调用和整数比较。如果未启用请求,则 LOG4CXX_DEBUG 和类似宏将禁止不必要的表达式计算。
  2. The performance of deciding whether to log or not to log when logging is turned on.
    打开日志记录时决定是否记录的性能。

    This is essentially the performance of walking the logger hierarchy. When logging is turned on, log4cxx still needs to compare the level of the log request with the level of the request logger. However, loggers may not have an assigned level; they can inherit them from the logger hierarchy. Thus, before inheriting a level, the logger may need to search its ancestors.
    这实质上是遍历记录器层次结构的性能。开启日志记录后,log4cxx 仍需要将日志请求的级别与请求记录器的级别进行比较。但是,记录器可能没有指定的级别;他们可以从记录器层次结构中继承它们。因此,在继承一个级别之前,记录者可能需要搜索其祖先。

    There has been a serious effort to make this hierarchy walk to be as fast as possible. For example, child loggers link only to their existing ancestors. In the BasicConfigurator example shown earlier, the logger named com.foo.Bar is linked directly to the root logger, thereby circumventing the nonexistent com or com.foo loggers. This significantly improves the speed of the walk, especially in "sparse" hierarchies.
    为了让这个层次结构尽可能快地走下去,我们付出了巨大的努力。例如,子记录器仅链接到其现有祖先。在前面显示的 BasicConfigurator 示例中,命名 com.foo.Bar 的记录器直接链接到根记录器,从而规避不存在 com 的或 com.foo 记录器。这显著提高了步行速度,尤其是在“稀疏”层次结构中。

    The cost of walking the hierarchy is typically 3 times slower than when logging is turned off entirely.
    遍历层次结构的成本通常比完全关闭日志记录时慢 3 倍。

  3. Actually outputting log messages
    实际输出日志消息

    This is the cost of formatting the log output and sending it to its target destination. Here again, a serious effort was made to make layouts (formatters) perform as quickly as possible. The same is true for appenders.
    这是格式化日志输出并将其发送到目标目标的成本。在这里,我们再次做出了认真的努力,使布局(格式化程序)尽可能快地执行。附录也是如此。

Conclusions 结论

Apache Log4cxx is a popular logging package written in C++. One of its distinctive features is the notion of inheritance in loggers. Using a logger hierarchy it is possible to control which log statements are output at arbitrary granularity. This helps reduce the volume of logged output and minimize the cost of logging.
Apache Log4cxx 是用 C++ 编写的流行日志包。它的显着特点之一是伐木工的继承概念。使用记录器层次结构,可以控制以任意粒度输出哪些日志语句。这有助于减少记录的输出量,并最大限度地降低日志记录成本。

One of the advantages of the log4cxx API is its manageability. Once the log statements have been inserted into the code, they can be controlled with configuration files. They can be selectively enabled or disabled, and sent to different and multiple output targets in user-chosen formats. The log4cxx package is designed so that log statements can remain in shipped code without incurring a heavy performance cost.
log4cxx API 的优点之一是其可管理性。将日志语句插入到代码中后,可以使用配置文件对其进行控制。它们可以有选择地启用或禁用,并以用户选择的格式发送到不同的多个输 出目标。log4cxx 包旨在使日志语句可以保留在附带的代码中,而不会产生沉重的性能成本。


  • 17
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值