giovedì 31 marzo 2011

Shallow Copy e Deep Copy in C++

In questo articolo cercheremo di capire cosa si intende per shallow copy e deep copy di un oggetto: tali concetti saranno validi per tutti i linguaggi di programmazione ad oggetti, ma nel linguaggio C++, che fa uso di puntatori e della copia implicita di oggetti,  rivestono un’importanza maggiore. Iniziamo a definire cosa è un costruttore di copia in C++: un costruttore di copia è un costruttore speciale, utilizzato per creare un nuovo oggetto-copia di un oggetto esistente. Ma perchè ne abbiamo bisogno? Se noi non definiamo un costruttore di copia sarà il compilatore a crearne uno per noi, ossia creerà un costruttore di copia di default. Con tale costruttore di copia, la copia avverrà “bit a bit”, brutto modo per dire che che si avrà una copia superficiale dell’ oggetto (shallow copy). Se noi vogliamo ottenere una copia dell’oggetto differente e in profondità (deep copy), dobbiamo crearcene uno.
Supponiamo inizialmente di avere la seguente classe C++:

File A.h
class A
{
public:
       A(int i) { a = i; }
       ~A(void) { }
       int get_a() { return a; }

private :
       int a;

};

Supponiamo che adesso si effettui una copia di un oggetto in un altro oggetto in un programma che usa tale classe:

#include "A.h"
#include <iostream>
using namespace std;

     
A x(10);
A y(20);
x = y; //shallow copy
cout << x.get_a()<<" "<< y.get_a() << endl;

Tutto è corretto: l’oggetto “y” viene copiato in “x” bit a bit, ossia il valore dell' attributo intero di y di valore 20 viene copiato in quello di x, cosicchè il programma stamperà 20 20.
Ma cosa succederebbe se al posto di un intero avessimo un puntatore? Si consideri ora la stessa classe A, dove abbiamo sostituito l'attributo intero con un puntatore ad un intero:

class A
{
public:
       A(int i)
{
                    a = new int;
*a = i;
}
       ~A(void) { delete a;}
       int get_a() { return *a; }

private :
       int* a;
};

Se provo a lanciare il programma esso stampa ancora 20 20, quindi sembra che tutto sia andato bene, ma non è così! Ciò che succede quando eseguo x = y, è che viene copiato il puntatore di y in quello di x:



Questo, oltre ad essere sbagliato concettualmente (non si effettua una reale copia dell’oggetto), dà vita anche ad un bel memory leak!
Si noti che nell’esempio con l’istruzione x = y, abbiamo chiamato l’operatore “=” effettuando una shallow copy, ma se per esempio avessimo usato una funzione f, che accetta in ingresso un oggetto A passato per valore, il discorso non cambierebbe perché in quel caso verrebbe creato automaticamente un oggetto A locale su cui verrà copiato l’oggetto passato:

A x(10);
f(x); //questa funzione "distrugge" l'intero puntato dal membro di x
cout << x.get_a()<< endl; 

...

void f(A y)
{
//qui y è una shallow copy di x
}
        
In questo caso verrà chiamato automaticamente il costruttore di copia di default della classe A (definito dal compilatore), che effettua anch’esso una copia bit a bit. Se proviamo a stampare il valore dell'intero puntato dal membro di x dopo la chiamata a f(x), vedremo che tale valore è stato "cancellato" dalla delete dentro il distruttore chiamato sull'oggetto locale y all'uscita dalla funzione.
Il problema si risolve dichiarando esplicitamente il costruttore di copia e l’operatore di uguaglianza e copiando “manualmente” i valori puntati dai membri puntatore. Nel nostro caso ci servirà definire dentro la classe A i due metodi:

A(const A& val); //costruttore di copia
A& operator=(const A& val); //sovraccaricamento operatore =

La classe A diventerà:

class A
{
public:
       A(int i)
{
                    a = new int;
*a = i;
}
       ~A(void) { delete a;}
       int get_a() { return *a;}

A(const A& val) //costruttore di copia
{
                     a = new int;
                    *a = *(val.a);
}
A& operator=(const A& val) //sovraccaricamento operatore =
{
                    *a = *(val.a);
                    return *this;
}

private :
       int* a;
};

Come si nota, sia il costruttore di copia che l’operatore “=” accettano entrambi una reference ad un altro oggetto A e copiano manualmente il valore intero, in modo da avere una copia effettiva:


Bisogna fare molta attenzione al distruttore della classe A: esso giustamente distrugge il puntatore “a” quando l’oggetto A viene distrutto: se si omette la delete dentro il distruttore, potremmo avere “piacevoli” sorprese: a volte si sa che in informatica c’è la regola “bug + bug = funziona tutto”! Facciamo un esempio, supponendo che nella classe A senza costruttore di copia e operatore =, manchi pure la delete e che vogliamo effettuare lo swap di due oggetti (ossia del contenuto dell’intero puntato da “a”):

A x(10);
A y(20);

cout << "x=" << x.get_a()<<" y="<<y.get_a() << endl;
A::swap(x,y);
cout << "x=" << x.get_a()<<" y="<<y.get_a() << endl;

dove abbiamo definito in A il metodo statico:

static void swap(A& x, A& y)//passaggio per reference, nessuna copia!
{
       A tmp = x; //questo chiama il costruttore di copia bit a bit
       x = y; //questo chiama l’operatore = bit a bit
       y = tmp; /questo chiama l’operatore = bit a bit
}

Con somma meraviglia notiamo che effettivamente la funzione swap effettua lo scambio delle variabili intere nonostante la shallow copy e il programma sopra stampa:

x=10 y=20
x=20 y=10

Ma vediamo graficamente cosa avviene. Dopo l’istruzione A tmp = x si avrà:


Naturalmente il fatto che swap funzioni è solo un caso e resta il fatto che le variabili intere puntate, in questo modo non verrebbero mai eliminate. Provando ad aggiungere nuovamente la delete al distruttore di A (ma senza aggiungere anche il costruttore di copia e l’operatore =), otterremmo dal programma precedente:

x=10 y=20
x=20 y= -17891602

Questo perchè all’uscita della funzione swap, l’intero puntato da tmp (oggetto locale alla funzione), viene cancellato dalla delete (in rosso in figura), lasciando “flottante” anche il puntatore contenuto dentro y, tale comportamento può considersi della categoria "effetti collaterali". Questo esempio è stato fatto solo per far capire che con i puntatori a volte anche se le cose sembrano “funzionare”, in realtà non è affatto così.
La regola che si deve tenere dovrebbe essere: “se una classe non alloca nei propri membri memoria in maniera dinamica (new, malloc ecc.), allora possiamo affidarci alla shallow copy, altrimenti è necessaria la deep copy”

In C++ la deep copy consiste in:
  • Creazione di un costruttore di copia che allochi la memoria e copi i valori delle variabili dell’oggetto da copiare.
  • Sovraccaricamento dell’operatore = che copi i valori delle variabili da un oggetto all’altro
  • Creazione di un distruttore che deallochi tutte le variabili allocate dinamicamente

Per approfondimenti sulla Shallow Copy e Deep Copy, anche in altri linguaggi, si legga qui.

martedì 8 marzo 2011

Parliamo un pò di SOA (Service Oriented Architecture)

Dopo aver visto un’introduzione alle EAI, veniamo al passo successivo che si è avuto nel panorama dell’integrazione dei sistemi informativi: le soluzioni SOA (Service Oriented Architecture). La grande diffusione del Web ha portato le architetture legate ad esso a grandi trasformazioni passando dalla costruzione di semplici siti web, ai portali aziendali fino alle soluzioni che integrassero le infrastrutture IT.
Agli inizi degli studi dei problemi di integrazione di applicazioni si avevano le cosiddette applicazioni distribuite (Object Oriented Architecture, OOA): componenti realizzati con tecnologie differenti potevano cooperare tra di loro dando vita ad un’applicazione unica secondo protocolli standard (es. CORBA, DCOM).  A questo livello agivano le EAI: far interagire tra loro applicazioni/componenti del tutto eterogenee.
Ci si è resi conto però dei vantaggi di un approccio differente, a servizi distribuiti (web services): si poteva accedere a tali servizi usufruendo dell’interfaccia che tali web services fornivano secondo un contratto (WSDL) e un protocollo (SOAP) standard. Eventualmente tali servizi potevano essere ricercati e scoperti (discovered) in una lista di servizi disponibili (UDDI). Il vantaggio di questo approccio (SOA) rispetto a quello precedente (OOA) consisteva nel fatto che veniva posto il focus sulle funzionalità e non sulle tecnologie.

In altre parole l’approccio SOA mette in evidenza un’entità nota maggiormente al management aziendale di quanto lo fossero le implementazioni tecnologiche, ossia metteva al centro dell’attenzione il processo di business.
Un’architettura SOA è qualcosa di più complesso di un’architettura a WS, nonostante possiamo affermare che essa si possa anche fondare sui concetti tecnologici dei WS. In realtà un’architettura SOA sta ad un livello di astrazione superiore di una struttura a WS: possiamo affermare che un’architettura SOA è

Un paradigma per l'organizzazione e l'utilizzazione delle risorse distribuite che possono essere sotto il controllo di domini di proprietà differenti. Fornisce un mezzo uniforme per offrire, scoprire, interagire ed usare le capacità di produrre gli effetti voluti consistentemente con presupposti e aspettative misurabili” - Reference Model for Serviced Oriented Architecture 1.0.”, OASIS,12 ottobre 2006 (Oasis è un consorzio mondiale fondato nel 1993 che regolamenta le convergenze degli sviluppi e l’adozione di standard di e-business).

SOA è pertanto un paradigma che si traduce in un’architettura dinamica con la quale il progetto e lo sviluppo delle soluzioni sono portate a livelli di ragionamento più elevati. Tali ragionamenti permettono di  valutare in modo più completo i processi aziendali, considerandoli nel loro insieme e non solo singolarmente, e, quindi, a ricercare lo sviluppo della migliore soluzione possibile che realizzi le operazioni di business richieste .
Quindi SOA non è una tecnologia, ma un approccio architetturale costruito attorno alle tecnologie esistenti. Promuove un insieme di pratiche, discipline, modalità di disegno e linee-guida che possono essere applicate usando una o più tecnologie. SOA propone lo sviluppo di nuovi servizi basati su funzionalità già offerte da un’applicazione. Altre applicazioni che desiderano comunicare con questa applicazione, faranno uso di uno o più servizi per realizzare il compito desiderato.
Il paradigma SOA si basa su tre concetti basilari:
  • Visibilità:  capacità di trovare il servizio più idoneo alle proprie necessità
  • Interazione: capacità di richiesta di un servizio e conseguentemente di esaudizione della richiesta mediante scambio di messaggi.
  • Effetti reali: capacità di dare i risultati dell’interazione.
Si nota come tali concetti non definiscano altro che le proprietà di un servizio: un servizio viene erogato quando si svolgono attività per conto di un richiedente (service consumer). Il servente (service provider) garantisce la propria offerta di servizio, la propria capacità di svolgerlo e la descrizione delle specifiche con le quali si può invocare correttamente il servizio offerto. La descrizione del servente viene resa pubblica mediante una discovery agency (service broker), un repository o una directory.
SOA è uno degli ultimi paradigmi della Web Engineering: generazione di servizi e creazione delle condizioni per la meccanizzazione dei processi all’interno degli asset informativi aziendali attraverso delle composite applications. Scegliere una soluzione SOA rispetto ad una OOA significa assumere per l’architettura queste proprietà:

  • Effettivo riuso e migliore “granularità” del servizi
  •  Interoperabilità dei servizi e loro componentizzazione in servizi più semplici (composite application)
  • Assunzione di standard generici e/o specifici di certi domini
  • Identificazione e accessibilità ai servizi standardizzata
  • Monitoraggio della soluzione (IT governance)
  • Incapsulamento dei servizi
  • Accoppiamento debole tra i servizi (loose coupling) mediante messaggi XML standard
  • Regole di interazione tra i servizi mediante contratti definiti
  • Maggiore astrazione dalle logiche di implementazione
  • Possibilità di suddivisone dei servizi per macro-funzionalità
  • Autonomia di ciascun servizio nel definire la propria logica implementativa
  • Rintracciabilità dei servizi mediante costrutti noti e standard

Semplificando possiamo dire che SOA ricontestualizza il paradigma nato con le OOA alla rete e ad Internet. I nuovi livelli di astrazione introdotti permettono di ridurre notevolmente il gap presente tra le logiche dei processi di business e i sistemi IT.

Schema modulare di un'architettura SOA

lunedì 7 marzo 2011

Ready. Run Codemotion.

Sabato 5 Marzo si è tenuto a Roma, presso l’Università di Ingegneria Informatica Roma 3,  il Codemotion, che “rimpiazza” il classico Javaday. La manifestazione prevedeva una serie di talk che riguardano le ultime novità nel mondo della programmazione, dell’informatica e della tecnologia. Dai talk presenti mi è smbrato che il focus fosse incentrato maggiormente su due argomenti hot: l’HTML 5 e i Database NoSQL. La manifestazione ha avuto un gran successo e afflusso di persone. Oltre ai talk erano presenti anche i classici stand con le aziende ICT sponsor dell’evento tra i quali nomi grossi come Oracle, IBM, Sony, Samsung, Almaviva, Adobe. Ho notato con piacere che l’afflusso agli stand maggiore si è avuto in quello della O’Really , che per l’occasione dava uno sconto del 40% sui titoli presenti sul banco (in realtà i titoli non erano numerosissimi, ma qualcosa di interessante si poteva trovare): impazzita la povera signora di lingua inglese che vendeva i libri e che non riusciva a tenere a bada orde di nerd che volevano comprare libri di informatica a prezzo stracciato (tra i quali c’ero anche io). Mio giudizio personale sull’evento: in generale ottimi gli argomenti trattati, mi è piaciuta molto l’idea di generalizzare l’evento a tutti i linguaggi e tecnologie, molto buona l’organizzazione globale della manifestazione, buona anche l’idea di far incontrare addetti ai lavoratori/appassionati di tecnologia con il mondo del lavoro. Per quanto riguarda i talk direi buon alcuni, ma un pò troppo bassa la qualità di alcuni altri che ho seguito (c’è da dire a parziale giustificazione che il tempo a disposizione degli oratori era poco). Nel complesso è stata una bella giornata, esperienza sicuramente da ripetere. A questo link le foto dell’evento:  in una foto, guardando attentamente, potete pure verificare che “io c’ero” J... mentre se aguzzate la vista noterete che il mio nome compare tra gli autori dei “codicilli” stampati sulle colonne: che onore! J

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.



Recent Posts

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