venerdì 4 marzo 2011

Apache Logging Services: A Log 4 All Languages


Molto spesso, durante lo sviluppo dei nostri progetti, ci viene richiesto di fornire dei file di log. Apache Software Foundation  fornisce quattro librerie dedicate al logging per Java, .NET,C++,PHP. Tali librerie hanno tutte in comune gli stessi concetti. L’obiettivo di tali librerie è gestire il log in tutti i suoi aspetti in modo facile, efficiente e flessibile . Inoltre il formato prodotto dei messaggi è visualizzabile tramite dei tool già esistenti. Tali librerie sono inoltre state ottimizzate per ridurre al minimo l’overhead sulle prestazioni delle nostre applicazioni.
Una delle caratteristiche distintive di tali librerie è il concetto di ereditarietà : per ciascuna classe del nostro progetto è possibile definire un “logger” con una sua granularità e uscita che può essere  un file, un OutputStream, un Writer, un server remoto ecc. E’possibile avere numerosi livelli di tracing: la seguente tabella definisce i livelli di log dei messaggi, in ordine decrescente di gravità. La colonna di sinistra è la denominazione del livello, la colonna di destra fornisce una breve descrizione di ogni livello di log.
Livello
Descrizione
OFF
Il grado più alto possibile. Disattiva l’output.
FATAL
errori gravi che causano cessazione anticipata. 
ERRORE
Altri errori di runtime o condizioni inaspettate. 
WARN
Messaggi a cui prestare particolare attenzione
INFO
Interessanti eventi di runtime (avvio / arresto).
DEBUG
Informazioni dettagliate
TRACE
Informazioni più dettagliate possibile

Ci sono due modi per configurare la libreria: uno è con un file di proprietà (chiave=valore) e l'altro è con un file XML . All'interno di entrambi è possibile definire tre componenti principali:  Loggers, Appenders e Layouts.  
  • Loggers: possiamo definire una gerarchia di nomi di logger secondo un principio di ereditarietà: un logger child eredita le proprietà dal logger parent. Ogni logger è configurabile in modo indipendente. 
  • Appenders: configurano gli output. Ci sono numerosi appenders supportati, con nomi descrittivi, come ad esempio FileAppender, ConsoleAppender, SocketAppender, SyslogAppender, NTEventLogAppender e  SMTPAppender. Appenders multipli possono essere collegati a qualsiasi Logger, quindi è possibile registrare le stesse informazioni su più uscite, per esempio su un file locale e su un socket listener su un altro computer. 
  • Layouts: sono usati per definire il formato dei messaggi. Un layout noto (one-line-at-a-time) è il PatternLayout. Per definire un layout si utilizza una “stringa modello”, molto simile alla funzione printf  del C / C + + . Ci sono anche gli XMLLayout per formattare i messaggi in XML/HTML.

Facciamo adesso degli esempi concreti di utilizzo delle librerie con i linguaggi relativi.

Java e log4j
La libreria che si occupa di produrre log in Java (usabile su qualsiasi tipologia di progetto) è log4j. Vediamo un esempio semplice di utilizzo all’interno di un progetto Java creato con eclipse.
·         Dopo aver scaricato lo zip/tar.gz dal sito, estrarre il contenuto e copiare il log4j*.jar che si trova nella radice della cartella unzippata, nella directory del nostro progetto (p.es. log4j-1.2.16.jar). Fare un Refresh sul Package Explorer di eclipse in modo che la libreria venga rilevata. Potremmo mettere il jar anche in un’altra cartella (es. lib) , ma ciò richiederà di aggiungere tale cartella al build path.
·         Dalle proprietà del progetto (tx destro) scegliere Java Build Path ->Libraries -> Add JARs, aggiungere log4j-1.2.16.jar
·         A questo punto il gioco è fatto, si possono usare liberamente le log4j. Ad esempio:

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
       

public class EsempioLog4j {
      static final Logger logger = Logger.getLogger(EsempioLog4j.class);

      public static void main(String[] args) {
            BasicConfigurator.configure();
            logger.debug("Hello World!");
      }

}

Mandando in esecuzione avremo il seguente output su consolle:

2011-03-03 23:30:00,709 [main] DEBUG EsempioLog4j - debug
0 [main] DEBUG EsempioLog4j  - debug
2011-03-03 23:30:00,711 [main] INFO  EsempioLog4j - info
2 [main] INFO EsempioLog4j  - info
2011-03-03 23:30:00,711 [main] WARN  EsempioLog4j - warning
2 [main] WARN EsempioLog4j  - warning
2011-03-03 23:30:00,711 [main] ERROR EsempioLog4j - error
2 [main] ERROR EsempioLog4j  - error
2011-03-03 23:30:00,711 [main] FATAL EsempioLog4j - fatal
2 [main] FATAL EsempioLog4j  - fatal

Il caso mostrato utilizza una configurazione di base. Tale configurazione può essere anche personalizzata mediante un file di configurazione XML o un file equivalente di Properties. Per esempio possiamo creare in eclipse un file xml log4j.xml nella cartella src del progetto e copiare ed incollare questa configurazione:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <appender name="A1" class="org.apache.log4j.ConsoleAppender">
    <layout class="org.apache.log4j.PatternLayout">
      <!-- Print the date in ISO 8601 format -->
      <param name="ConversionPattern" value="%d [%t] %-5p %c - %m%n"/>
    </layout>
  </appender>
  <logger name="com.foo">
    <!-- Print only messages of level warn or above in the package com.foo -->
    <level value="warn"/>
  </logger>
  <root>
    <priority value ="debug" />
    <appender-ref ref="A1" />
  </root>
</log4j:configuration>


Nella classe precedente al posto della riga:

BasicConfigurator.configure();

mettere:

URL url = Loader.getResource("log4j.xml");
DOMConfigurator.configure(url);

importando le classi necessarie. L’output sarà il seguente:
2011-03-03 23:20:25,891 [main] DEBUG EsempioLog4j - debug
2011-03-03 23:20:25,892 [main] INFO  EsempioLog4j - info
2011-03-03 23:20:25,892 [main] WARN  EsempioLog4j - warning
2011-03-03 23:20:25,892 [main] ERROR EsempioLog4j - error
2011-03-03 23:20:25,892 [main] FATAL EsempioLog4j – fatal

Naturalmente è possibile customizzare il file di configurazione a piacere, mandando per esempio l’output su file anzichè su consolle, in questo caso dovremmo definire un apposito appender.

C/C++ e log4cxx
Una volta scaricata/compilata la libreria log4cxx, dobbiamo includerla all’interno del nostro progetto. Va detto che compilare la log4cxx non è così banale, soprattutto sotto windows (cygWin/Visual Studio), mentre su linux è tutto molto più semplice col gcc.
Sotto linux, installare le librerie apr e apr-util tramite p.es. apt-get, successivamente:

$ tar xzf apache-log4cxx-0.10.0.tar.gz
$ cd apache-log4cxx-0.10.0/
$ ./configure --prefix=/usr
$ make
$ make install

Questo dovrebbe generare le opportune librerie da includere nel nostro Makefile.

Vediamo il caso Windows con VS 2008/2010:

1.       Andare nel repository di APR di Apache (p.es. http://download.nextag.com/apache/apr/)
2.       Fare il download della libreria APR ed estrarla. Rinominare la cartella apr.
3.       Fare il download libreria APR-Util ed estrarla. Rinominare la cartella apr-util.
4.       Fare il download dal sito “Apache Logging Services”, di Log4CXX ed estrarla. Al termine di queste operazioni dobbiamo avere una cartella apache-log4cxx-0.10.0 con le sottocartelle log4cxx, apr e apr-util
5.       Download di GNU Sed (setup.exe per Win) e installarlo (mettere la cartella di sed.exe nella variabile di ambiente “Path” di Windows)
6.       Andare nella directory apache-log4cxx-0.10.0
7.       Esegui  configure.bat
8.       Esegui configure-aprutil.bat
9.       Apri la solution log4cxx.dsw e convertirla ad una sln VS 2008/2010
10.   Selezionare i progetti apr, apr-util e il progetto log4cxx e compilarli in ordine.

Al termine della procedura dovremmo ottenere la libreria statica log4cxx.lib e quella dinamica log4cxx.dll.
A questo punto il gioco è fatto. Basta includere nel nostro progetto la libreria log4cxx.lib e la cartella include di \apache-log4cxx-0.10.0\src\main\log4cxx nelle opzioni del Linker e in quelle di inclusione dalle Proprietà del progetto. Inoltre aggiungiamo anche log4cxx.dll nella cartella del nostro progetto. Facciamo un esempio di codice C++:

#include <log4cxx/logger.h>
#include <log4cxx/xml/domconfigurator.h>
#include <iostream>

using namespace log4cxx;
using namespace log4cxx::xml;
using namespace log4cxx::helpers;
using namespace std;


// Define static logger variable
LoggerPtr loggerMyMain(Logger::getLogger( "main"));
LoggerPtr loggerFunctionA(Logger::getLogger( "functionA"));

void functionA()
{
    LOG4CXX_INFO(loggerFunctionA, "Executing functionA.");
}

int main(int argc, const char* argv[] )
{
    // Load configuration file
    DOMConfigurator::configure("Log4cxxConfig.xml");
    LOG4CXX_TRACE(loggerMyMain, "debug message (detailed)");
    LOG4CXX_DEBUG(loggerMyMain, "debug message");
    LOG4CXX_INFO (loggerMyMain, "info message");
    LOG4CXX_WARN (loggerMyMain, "warn message");
    LOG4CXX_ERROR(loggerMyMain, "error message");
    LOG4CXX_FATAL(loggerMyMain, "fatal message!!!");
      functionA();
}

Il file log4cxxConfig.xml deve essere creato nella cartella principale del nostro progetto e rappresenta la configurazione del log. Per esempio:

<?xml version="1.0" encoding="UTF-8" ?>
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
  <!-- Output the log message to system console.-->
  <appender name="appxConsoleAppender" class="org.apache.log4j.ConsoleAppender">
    <param name="Target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%-5p %c{1} - %m%n"/>
    </layout>
  </appender>

  <!-- Output the log message to log file-->
  <appender name="appxNormalAppender" class="org.apache.log4j.FileAppender">
    <param name="file" value="appxLogFile.log" />
    <param name="append" value="true" />
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%d %-5p [%t:%x] %C{2} (%F:%L) - %m%n" />
    </layout>
  </appender>

  <root>
    <priority value="all" />
    <appender-ref ref="appxNormalAppender"/>
    <appender-ref ref="appxConsoleAppender"/>
  </root>

  <!-- Specify the level for some specific categories -->
  <category name="functionA" >
    <priority value ="info" />
    <appender-ref ref="appxNormalAppender"/>
    <appender-ref ref="appxConsoleAppender"/>
  </category>

</log4j:configuration>
L’output del programma sarà il seguente:
TRACE main - this is a debug message for detailed code discovery.
DEBUG main - this is a debug message.
INFO  main - this is a info message, ignore.
WARN  main - this is a warn message, not too bad.
ERROR main - this is a error message, something serious is happening.
FATAL main - this is a fatal message!!!
INFO  functionA - Executing functionA.

Il logger oltrechè su consolle scriverà anche su file (appxLogFile.log)

.NET (C#/VB.NET/ASP.NET) e log4net
Vediamo un esempio di utilizzo di log4net dentro un normale progetto .NET (Console Application), usando Visual Studio 2010.
Per inserire log4net dentro un progetto .NET (C#,VB.NET,ASP.NET) è sufficiente effettuare i seguenti passi:
  • Una volta scaricata/compilata log4net.dll, copiarla nella directory di progetto. 
  • In Visual Studio cliccare col tasto destro e aggiungere un nuovo riferimento (aggiungi Riferimento/Reference dal menù contestuale) a log4net.dll: scegliere sfoglia e selezionare log4net.dll.
  • Aggiungere un file App.config al progetto (tasto destro sul progetto in VS -> Aggiungi Nuovo Elemento->General->Application Configuration File)
  •  Copiare ed incollare in App.config  il seguente contenuto xml che rappresenta la configurazione di log4net:

File App.config 
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net"
      type="log4net.Config.Log4NetConfigurationSectionHandler,
            log4net"/>
  </configSections>

  <log4net>
    <root>
      <level value="ALL" />
      <appender-ref ref="LogFileAppender" />
      <appender-ref ref="ColoredConsoleAppender" />
    </root>

    <appender name="LogFileAppender" type="log4net.Appender.FileAppender" >
      <param name="File" value="MyLog.txt" />
      <param name="AppendToFile" value="true" />
      <layout type="log4net.Layout.PatternLayout">
        <param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"  />
      </layout>
    </appender>

    <appender name="ColoredConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
      <mapping>
        <level value="ERROR" />
        <foreColor value="Red" />
        <!-- <backColor value="Red, HighIntensity" /> -->
      </mapping>
      <mapping>
        <level value="DEBUG" />
        <backColor value="Blue" />
        <foreColor value="Green" />
      </mapping>
      <mapping>
        <level value="WARN" />
        <foreColor value="Yellow" />
      </mapping>
      <mapping>
        <level value="INFO" />
        <foreColor value="White" />
      </mapping>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger  - %message%newline" />
      </layout>
    </appender>
  </log4net>
</configuration>

Come si vede in questo file decidiamo il formato dei messaggi, i loro colori ecc.

·         Fare attenzione al profilo scelto per il progetto. Non usare un Client Profile che di solito è quello di default nel caso di Console Application in VS 2010. Ciò non è possiblie a causa della dipendenza di log4net dall’assembly System.Web. Scegliere come Target Profile per esempio .NET Framework 4 (dalle proprietà del progetto). Usando il Client Profile si possono avere problemi di riconoscimento della libreria log4net col seguente errore : “The type or namespace name 'log4net' could not be found (are you missing a using directive or an assembly reference?)”             
·         Utilizzare ora liberamente log4net,  ad esempio:

using System;
using log4net;
using log4net.Config;

namespace EsempiodiUsoLog4Net
{
    class Program
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(Program));

        static void Main(string[] args)
        {
            ILog log = LogManager.GetLogger("MyLogger");
            XmlConfigurator.Configure();
            log.Error("Error");
            log.Warn("Warning");
            log.Info("Info.");
            log.Debug("debug..");
            Console.ReadLine();
        }
    }
}

L’output sarà:




.NET  (C++/CLI) e log4net
Utilizzare log4net all’interno di un progetto C++/CLI è leggermente più oneroso rispetto al caso C#/VB.NET/ASP.NET. 
·         Una volta scaricata/compilata log4net.dll, copiarla nella directory di progetto.
·         In Visual Studio cliccare col tasto destro e aggiungere un nuovo riferimento (aggiungi Riferimento/Reference dal menù contestuale) a log4net.dll: scegliere sfoglia e selezionare log4net.dll. 
·         Aggiungere un file app.config al progetto (tasto destro sul progetto in VS -> Aggiungi Nuovo Elemento -> Utilità->File di configurazione in VS 2008/ Application Configuration File in VS 2010). Copiare ed incollare il contenuto del file App.config visto nel caso C#/VB.NET/ASP.NET .
·         Copiare l’app.config nella directory di output aggiungendo la riga di comando “copy app.config "$(TargetPath).config” come evento di post-compilazione del progetto. Ciò creerà un file <nome_mia_app>.config nella directory di output del progetto.
·         Aggiungere /SUBSYSTEM:CONSOLE tra le opzioni del Linker (Linker->Sistema->Sottosistema) nel caso si voglia l’output a consolle.
·         Leggere le considerazioni sul Target Profile fatte nel caso C#/VB.NET/ASP.NET.
·         Ora possiamo usare log4net all’interno del progetto C++/CLI.  Facciamo un esempio:

File Log.cpp

#include "StdAfx.h"
#include "Log.h"

using namespace log4net;
using namespace log4net::Config;

Log::Log(void)
{
      log4net::ILog^ log = LogManager::GetLogger("MyLogger");
      XmlConfigurator::Configure();
      log->Error("Error");
      log->Warn("Warning");
      log->Info("Info.");
      log->Debug("debug..");
}

L’output sarà lo stesso visto nel caso C#.

PHP e log4php
Log4php è la libreria di log dedicata agli sviluppatori PHP. In pratica ha le stesse proprietà comuni alle altre librerie e la stessa struttura. Supporta i seguenti appenders:  File, RollingFile, DailyFile, Echo, Console, Mail, PDO, PHP error, Syslog or NT events e Socket; nonchè i seguenti layouts: Simple, TTCC, Pattern, Html e Xml.
L’installazione consiste nei seguenti semplici punti:
  • Scaricare la libreria e unzipparla
  • Copiare la cartella src/main/php in quella dell’applicazione, p.es. $YOURAPP/log4php
  • Includere (require) la classe log4php/Logger.php nell’applicazione
L’esempio d’uso è quasi banale:

require_once dirname(__FILE__).'/../../main/php/Logger.php';
Logger::configure(dirname(__FILE__).'/../resources/layout_simple.xml');
$logger = Logger::getRootLogger();
$logger->info("Hello World!");

Proprietà dinamiche
Qualcuno si chiederà come sia possibile dare proprietà dinamiche agli appenders: per esempio dare un nome a un log file dinamico. Le librerie supportano le proprietà dinamiche settabili via codice. Per esempio se volessimo un nome di file dinamico possiamo mettere nel file di configurazione (nella sezione appender):

<file type="log4net.Util.PatternString" value="C:\logs\%property{LogName}" />

Tale proprietà potrà essere settata via codice. Per esempio in C#:

log4net.GlobalContext.Properties["LogName"] = "file1.log";


Ricordarsi di settare le proprietà del GlobalContext prima di richiamare il logger, ossia prima della riga

ILog log = LogManager.GetLogger("MyLogger");

Insieme alle proprietà dinamiche possiamo definire anche dei PropertyFilter. Per esempio definendo nel file di configurazione:

<filter type="log4net.Filter.PropertyFilter">
  <Key value="Version" />
  <StringToMatch value="1" />
</filter>

Possiamo scegliere via codice la versione del file di log sul quale mandare i messaggi:

log4net.ThreadContext.Properties["Version"] = "1";
log.Warn("Warning on the version 1 of the log file");


ci sono ancora molte proprietà che contraddistinguono le librerie di log di Apache, ma elencarle tutte sarebbe troppo lungo in questo articolo. Si rimanda quindi alla relativa documentazione sul sito.

Tools di visualizzazione

Uno dei vantaggi di utilizzare una libreria di Apache Logging Services è che esistono una serie di tool di visualizzazione dei log prodotti da tali librerie. Uno è per esempio fornito da Apache stesso:  Chainsaw.
Chainsaw è stato pensato per log4j ma è possibile interfacciarlo anche con le altre librerie della famiglia per mezzo dell’UdpAppender. Si rimanda alla relativa documentazione per il suo utilizzo e configurazione.



0 commenti:

Posta un commento

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | cna certification