Log4j осигурява контрол върху регистрирането

Почти всяко голямо приложение включва собствен API за регистриране или проследяване. Опитът показва, че сечът представлява важен компонент от цикъла на разработка. Като такова регистрирането предлага няколко предимства. Първо, той може да предостави точен контекст за изпълнение на приложението. Веднъж вмъкнат в кода, генерирането на изходни данни за регистриране не изисква човешка намеса. Второ, изходните данни на регистрационния файл могат да бъдат запазени в постоянна среда, за да бъдат проучени по-късно. И накрая, в допълнение към използването му в цикъла на разработка, достатъчно богат пакет за регистриране може да се използва и като инструмент за одит.

В съответствие с това правило, в началото на 1996 г. проектът на ЕС SEMPER (Secure Electronic Marketplace for Europe) реши да напише свой собствен API за проследяване. След безброй подобрения, няколко въплъщения и много работа, този API се превърна в log4j, популярен пакет за регистриране за Java. Пакетът се разпространява под публичния лиценз на IBM, сертифициран от инициативата с отворен код.

Регистрацията има своите недостатъци. Може да забави приложението. Ако е твърде подробен, това може да доведе до превъртане на слепота. За да облекчи тези опасения, log4j е проектиран да бъде бърз и гъвкав. Тъй като регистрирането рядко е основният фокус на приложението, API на log4j се стреми да бъде лесен за разбиране и използване.

Тази статия започва с описване на основните компоненти на архитектурата log4j. Той продължава с прост пример, изобразяващ основно използване и конфигурация. Той завършва, като докосва проблемите с производителността и предстоящия API за регистриране от Sun.

Категории, приложения и оформления

Log4j има три основни компонента:

  • Категории
  • Апепенси
  • Оформления

Трите компонента работят заедно, за да дадат възможност на разработчиците да регистрират съобщения според типа и приоритета на съобщенията и да контролират по време на изпълнение как тези съобщения са форматирани и къде се отчитат. Нека да разгледаме всеки по ред.

Йерархия на категориите

Първото и най-важно предимство на всеки API за регистриране над обикновения се System.out.printlnкрие в способността му да деактивира определени операторски записи, като същевременно позволява на другите да печатат безпрепятствено. Тази възможност предполага, че пространството за регистриране, т.е. пространството на всички възможни оператори за регистриране, се категоризира според някои избрани от разработчика критерии.

В съответствие с това наблюдение, org.log4j.Categoryкласът фигурира в основата на пакета. Категориите са именувани обекти. В схема за именуване, позната на разработчиците на Java, категорията се казва родител на друга категория, ако нейното име, последвано от точка, е префикс на името на дъщерната категория. Например категорията с име com.fooе родител на категорията с име com.foo.Bar. По подобен начин javaе родител на java.utilи предшественик на java.util.Vector.

Основната категория, която се намира в горната част на йерархията на категориите, е изключителна по два начина:

  1. Винаги съществува
  2. Не може да бъде извлечен по име

В Categoryкласа извикването на статичния getRoot()метод извлича основната категория. Статичният getInstance()метод създава екземпляри за всички останали категории. getInstance()приема името на желаната категория като параметър. Някои от основните методи в Categoryкласа са изброени по-долу:

пакет org.log4j; клас на публична категория {// Методи за създаване и извличане: публична статична категория getRoot (); публична статична категория getInstance (име на низ); // методи за печат: публично отстраняване на грешки (съобщение в низ); публична информация за невалидни (съобщение на низ); публично предупреждение за невалидност (съобщение на низ); публична грешка в невалидността (съобщение на низ); // общ метод за печат: публичен дневник за невалидни (Приоритет p, низово съобщение); }

Категориите могат да получат приоритети от набора, определен от org.log4j.Priorityкласа. Въпреки че зададеният приоритет съвпада с този на системата Unix Syslog, log4j насърчава използването само на четири приоритета: ГРЕШКА, ПРЕДУПРЕЖДЕНИЕ, ИНФОРМАЦИЯ и ОТЛАГАНЕ, изброени в низходящ ред на приоритет. Обосновката зад този привидно ограничен набор е да се насърчава по-гъвкава йерархия на категориите, а не статичен (макар и голям) набор от приоритети. Можете обаче да определите собствените си приоритети, като подкласирате Priorityкласа. Ако дадена категория няма присвоен приоритет, тя наследява такъв от най-близкия си предшественик с присвоен приоритет. Като такава, за да се гарантира, че всички категории в крайна сметка могат да наследят приоритет, основната категория винаги има присвоен приоритет.

За да направите заявки за регистриране, извикайте един от методите за печат на екземпляр на категория. Тези методи за печат са:

  • error()
  • warn()
  • info()
  • debug()
  • log()

По дефиниция методът на печат определя приоритета на заявка за регистриране. Например, ако cе екземпляр на категория, тогава изявлението c.info("..")е заявка за регистриране на приоритет INFO.

Казва се, че заявка за регистриране е разрешена, ако нейният приоритет е по-висок или равен на приоритета на нейната категория. В противен случай се казва, че заявката е деактивирана. . Категория без зададен приоритет ще наследи такава от йерархията.

По-долу ще намерите пример за това правило:

// получаваме екземпляр на категория с име "com.foo" Категория cat = Category.getInstance ( "com.foo" ); // Сега задайте неговия приоритет. cat .setPriority ( Priority.INFO ); Категория barcat = Category.getInstance ( "com.foo.Bar" ); // Тази заявка е активирана, защото ПРЕДУПРЕЖДЕНИЕ > = ИНФОРМАЦИЯ . котка предупреждение ("Ниско ниво на горивото."); // Тази заявка е деактивирана, тъй като DEBUG < INFO . котка отстраняване на грешки („Започване на търсене на най-близката бензиностанция.“); // Екземплярът на категорията barcat, наречен "com.foo.Bar", // ще наследи приоритета си от категорията, наречена // "com.foo" По този начин,следната заявка е разрешена // защото INFO> = ИНФОРМАЦИЯ . баркат. информация („Намира се най-близката бензиностанция.“); // Тази заявка е деактивирана, тъй като DEBUG < INFO . баркат. отстраняване на грешки ("Излизане от търсенето на бензиностанция");

Извикването на getInstance()метода със същото име винаги ще връща препратка към точно същия обект от категорията. По този начин е възможно да конфигурирате категория и след това да извлечете същия екземпляр някъде другаде в кода, без да предавате референции. Категориите могат да се създават и конфигурират във всякакъв ред. По-специално, родителска категория ще намери и ще се свърже със своите деца, дори ако е създадена след тях. Средата log4j обикновено се конфигурира при инициализация на приложението, за предпочитане чрез четене на конфигурационен файл, подход, който ще обсъдим скоро.

Log4j улеснява наименуването на категории по софтуерен компонент. Това може да се постигне чрез статично създаване на инстанция на категория във всеки клас, като името на категорията е равно на напълно квалифицираното име на класа - полезен и ясен метод за дефиниране на категории. Тъй като изходът на регистрационния файл носи името на генериращата категория, такава стратегия за именуване улеснява идентифицирането на произхода на съобщението в дневника. Това обаче е само една възможна, макар и обща стратегия за именуване на категории. Log4j не ограничава възможния набор от категории. Всъщност разработчикът е свободен да назовава категориите по желание.

Апендери и оформления

Възможността за селективно активиране или деактивиране на заявки за регистриране въз основа на тяхната категория е само част от картината. Log4j също така позволява записване на заявки за регистриране на множество изходни дестинации, наречени добавки в log4j говорят. Понастоящем съществуват приложения за конзолата, файловете, GUI компонентите, отдалечените сървъри за сокети, NT Event Loggers и отдалечените демони на UNIX Syslog.

Категорията може да се отнася до множество добавки. Всяка активирана заявка за регистриране за дадена категория ще бъде препратена до всички добавящи в тази категория, както и до по-високите в йерархията. С други думи, приложенията се наследяват адитивно от йерархията на категориите. Например, ако добавите добавяне на конзола към основната категория, всички активирани заявки за регистриране ще се отпечатват поне на конзолата. Ако в допълнение към категорията се добави добавяне на файл, да речем C , тогава активираните заявки за регистриране за децата на C и C ще се отпечатат във файл и на конзолата. Имайте предвид, че можете да замените това поведение по подразбиране, така че натрупването на добавки вече да не е добавка.

More often than not, users want to customize not only the output destination but also the output format, a feat accomplished by associating a layout with an appender. The layout formats the logging request according to the user's wishes, whereas an appender takes care of sending the formatted output to its destination. The PatternLayout, part of the standard log4j distribution, lets the user specify the output format according to conversion patterns similar to the C language printf function.

For example, the PatternLayout with the conversion pattern %r [%t]%-5p %c - %m%n will output something akin to:

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

In the output above:

  • The first field equals the number of milliseconds elapsed since the start of the program
  • The second field indicates the thread making the log request
  • The third field represents the priority of the log statement
  • The fourth field equals the name of the category associated with the log request

The text after the - indicates the statement's message.

Configuration

Inserting log requests into the application code requires a fair amount of planning and effort. Observation shows that code dedicated to logging represents approximately four percent of the application's total. Consequently, even moderately sized applications will have thousands of logging statements embedded within their code. Given their number, it becomes imperative to manage those log statements without the need to modify them manually.

The log4j environment can be fully configured programmatically. However, it is far more flexible to configure log4j by using configuration files. Currently, configuration files can be written in XML or in Java properties (key=value) format.

Let us give a taste of how that is done with the help of an imaginary application -- MyApp -- that uses log4j:

 import com.foo.Bar; // Import log4j classes. import org.log4j.Category; import org.log4j.BasicConfigurator; public class MyApp { // Define a static category variable so that it references the // Category instance named "MyApp". static Category cat = Category.getInstance(MyApp.class.getName()); public static void main(String[] args) { // Set up a simple configuration that logs on the console. BasicConfigurator.configure(); cat.info("Entering application."); Bar bar = new Bar(); bar.doIt(); cat.info("Exiting application."); } } 

As seen in the code above, MyApp begins by importing log4j related classes. It then defines a static category variable with the name MyApp, which happens to be the class' fully qualified name.

MyApp uses the Bar class defined in the package com.foo:

package com.foo; import org.log4j.Category; public class Bar { static Category cat = Category.getInstance(Bar.class.getName()); public void doIt() { cat.debug("Did it again!"); } } 

In MyApp, the invocation of the BasicConfigurator.configure() method creates a rather simple log4j setup. That method is hardwired to add to the root category a FileAppender printing on the console. The output will be formatted by using a PatternLayout set to the pattern %-4r [%t] %-5p %c %x - %m%n.

Note that by default, the root category is assigned to Priority.DEBUG.

The output of MyApp is:

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

Figure 1 depicts MyApp's object diagram immediately after it calls the BasicConfigurator.configure() method.

The MyApp class configures log4j by invoking BasicConfigurator.configure() method. Other classes need only import the org.log4j.Category class, retrieve the categories they want to use and log away.

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 runtime. Below, you'll see a slightly modified version:

 import com.foo.Bar; import org.log4j.Category; import org.log4j.PropertyConfigurator; public class MyApp { static Category cat = Category.getInstance(MyApp.class.getName()); public static void main(String[] args) { // BasicConfigurator replaced with PropertyConfigurator. PropertyConfigurator.configure(args[0]); cat.info("Entering application."); Bar bar = new Bar(); bar.doIt(); cat.info("Exiting application."); } } 

This version of MyApp instructs PropertyConfigurator to parse a configuration file and set up logging accordingly.

Let's look at a sample configuration file that results in exactly the same output as the previous BasicConfigurator-based example:

# Set root category priority to DEBUG and its only appender to A1. log4j.rootCategory=DEBUG, A1 # A1 is set to be a FileAppender which outputs to System.out. log4j.appender.A1=org.log4j.FileAppender log4j.appender.A1.File=System.out # A1 uses PatternLayout. log4j.appender.A1.layout=org.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n 

Suppose we no longer wish to see the output of any component belonging to the com.foo package. The following configuration file shows one possible way of achieving that:

log4j.rootCategory = DEBUG, A1 log4j.appender.A1 = org.log4j.FileAppender log4j.appender.A1.File = System.out log4j.appender.A1.layout = org.log4j.PatternLayout # Отпечатайте датата във формат ISO 8601 log4j.appender.A1.layout.ConversionPattern = % d [% t]% -5p% c -% m% n # Отпечатвайте само съобщения с приоритет WARN или по-горе в пакета com.foo. log4j.category.com.foo = ПРЕДУПРЕЖДЕНИЕ