Canigó - Servei de Traces
SERVEI DE TRACES
IntroduccióPropósitEl Servei de Traces té com a missió poder detectar i localitzar amb més facilitat errors de l'aplicació, entrada de dades incorrectes segons la lògica del sistema, i inclús realitzar un seguiment de qui ha fet una determinada operació per a portar a terme una auditoria. Per a que això pugui ser portat a terme, el servei ofereix la possibilitat de:
Context i Escenaris d'ÚsEl Servei de Traces es troba dins dels serveis de Propósit General de canigo. El seu ús és necessari en cas de voler generar traces de la nostra aplicació. Tammateix, molts dels serveis proporcionats per canigo fan servir aquest servei per a generar les seves traces.
Versions i DependènciesLes dependències descrites a la següent url son requerides per tal de compilar i fer funcionar el projecte: A qui va dirigitAquest document va dirigit als següents perfils:
Documents i Fonts de Referència
GlossariLog4J Un dels framework de traces més extés. Es basa en l'ús de Appenders, Categories i Layouts. Veure l'annex per a més informació. Descripció DetalladaArquitectura i Componentscanigo ofereix la possibilitat d'utilitzar diferents implementacions de traces sense que afecti al servei de traces. Els components podem classificar-los en:
JavaDoc: http://canigo.ctti.gencat.net/confluence/canigodocs/site/canigo2_0/canigo-services-traces/apidocs/index.html Instal- lació i ConfiguracióInstal- lacióLa instal- lació del servei requereix de la utilització de la llibreria 'canigo-services-logging' i les dependències indicades a l'apartat 'Introducció-Versions i Dependències'. ConfiguracióLa configuració del Servei de Traces implica 3 pasos:
Definició del Configurador del Servei La definició del servei requereix definir un configurador amb un identificador (es recomana usar 'loggingConfigurator' ) Atributs:
Propietats:
Exemple:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework. org/dtd/spring-beans.dtd"> <beans> <bean id="loggingConfigurator" class="net.gencat.ctti.canigo.services. logging.log4j.xml.HostDOMConfigurator" init-method="init"> <property name="configFileName"> <value>classpath:log4j.xml</value> </property> </bean> ... </beans> Definició del Servei La definició del servei requereix configurar un bean amb un identificador (es recomana usar 'loggingService' ) i els següents atributs: Atributs:
També es poden configurar les següents propietats:
Exemple:
... <bean id="loggingService" class="net.gencat.ctti.canigo.services. logging.log4j.Log4JServiceImpl" init-method="init"> <!- propietat que referència al configurador -> <property name="configurator"> <ref local="loggingConfigurator"/> </property> </bean> <!- bean del configurador -> <bean id="loggingConfigurator" class="net.gencat.ctti.canigo.services. logging.log4j.xml.HostDOMConfigurator"> ... </bean> ... Cal destacar que en l'exemple el configurador fa ús d'un localitzador de fitxers depenent del host (HostDOMConfigurator). Això vol dir que si en aquest configurador incloem una referència a un fitxer de propietats anomenat Definició de les Propietats del Servei per Log4J Fitxer de configuració: log4j.xml En cas d'haver escollit com a implementació del servei de traces la classe ' net.gencat.ctti.canigo.services.logging.log4j.Log4JServiceImpl', la definició del fitxer de propietats es basa en l'ús de Log4J. En aquest apartat es mostren les configuracions més comuns de Log4J (mitjançant appenders) i algunes de més específiques que per la seva complexitat són necessàries d'explicar. En alguns casos, es tracten d'implementacions disponibles en la comunitat, en d'altres desenvolupats específicament per canigo. Es recomana una lectura prèvia del manual de log4j per conéixer en detall la configuració de log4j.
Classe: org.apache.log4j.ConsoleAppender S'afegeixen les traces traces al "System.out" o "System.err". Per a infornmació detallada sobre les propietats de l'appender consultar el manual de Log4J. Exemple:
<appender name="console" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="canigo Message: %-5p [%t] %c - %m%n"/> </layout> </appender>
Classe: org.apache.log4j.DailyRollingFileAppender Permet establir un tamany màxim de fitxer i cada vegada que el fitxer de log supera aquest tamany es crea un fitxer de traces nou. Aquest procès es fa diariament o segons la freqüència especificada. Per a informació detallada sobre la forma d'especificar les diferents freqüències i propietats de l'appender consultar el manual de Log4J. El format del fitxer (texte pla o XML) el defineix el layout associat a l'appender com veurem posteriorment.
Classe: org.apache.log4j.SMTPAppender Envia les traces per correu electrònic. Per a informació detallada sobre les propietats de l'appender consultar el manual de Log4J.
Classe: org.apache.log4j.ext.SNMPTrapAppender Envia les traces com a missatges TRAP pel protocol de control de xarxa SNMP a un administrador de xarxa. El manual de Log4J és molt suscint en definir el seu ús.Degut a la seva complexitat es fa esmena en aquest apartat de cóm utilitzar-lo. Es poden especificar els següents paràmetres:
Exemple:
<appender name="snmp" class="org.apache.log4j.ext.SNMPTrapAppender"> <param name="ImplementationClassName" value="org.apache. log4j.ext.WengsoftSNMPTrapSender"/> <param name="ManagementHost" value="127.0.0.1"/> <param name="ManagementHostTrapListenPort" value="8001"/> <param name="EnterpriseOID" value="1.3.6.1.4.1.24.0"/> <param name="LocalIPAddress" value="127.0.0.1"/> <param name="LocalTrapSendPort" value="161"/> <param name="GenericTrapType" value="6"/> <param name="SpecificTrapType" value="12345678"/> <param name="CommunityString" value="public"/> <param name="ForwardStackTraceWithTrap" value="true"/> <param name="Threshold" value="DEBUG"/> <param name="ApplicationTrapOID" value="1.3.6.1.4.1.24.12.10.22.64"/> <layout class="org.apache.log4j.ext.SnmpDelimitedConversionPatternLayout"> <param name="ValuePairDelim" value="/"/> <param name="VarDelim" value=";"/> <param name="ConversionPattern" value="%-5p;1.3.6.1.4.1.24.100.1/%t;1. 3.6.1.4.1.24.100.2/%c;1.3.6.1.4.1.24.100.3/%m;1.3.6.1.4.1.24.100.4" /> </layout> </appender>
Classe: org.apache.log4j.jdbcplus.JDBCAppender Instal- lació: Per a poder fer ús d'aquest appender cal descarregar una llibreria, disponible a la url 'http://www.dankomannhaupt.de/projects/index.html' Es poden definir els paràmetres:
Patrons del paràmetre sql:
Exemple:
<appender name="JDBCPooled" class="org.apache.log4j.jdbcplus.JDBCAppender"> <param name="url" value="jdbc:mysql://localhost/test" /> <param name="username" value="eulo" /> <param name="password" value="eulo" /> <param name="sql" value="INSERT INTO TEST (prio, cat, thread, msg, layout_msg, throwable, ndc, mdc, mdc2, info, addon, created_by) VALUES (' (PRIO)', ' (CAT)', ' (THREAD)', ' (MSG)', '@LAYOUT:1@', ' (THROWABLE)', ' (NDC)', '@MDC:MyMDC@', '@MDC:MyMDC2@', 'info timestamp: (TIMESTAMP)', 'addon', 'me')"/> <param name="buffer" value="1"/> <param name="commit" value="true"/> <param name="dbclass" value="com.mysql.jdbc.Driver"/> <param name="quoteReplace" value="true"/> <param name="throwableMaxChars" value="3000"/> <param name="layoutPartsDelimiter" value="#-#"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%t] %m####%d\{dd.MM.yyyy\} #-#%d\{HH:mm:ss\}"/> </layout> </appender> També podem indicar el nom de les columnes i el seu valor amb param name="column" i posant a value: nom_de_columna~patró~valor On patró correspon a un dels possibles patrons. <param name="table" value="logtest" /> <param name="column" value="id~ID~org.apache.log4j.jdbcplus. examples.MyIDHandler" /> <param name="column" value="prio~PRIO" /> <param name="column" value="cat~CAT" /> <param name="column" value="thread~THREAD" /> <param name="column" value="msg~MSG" /> <param name="column" value="layout_msg~LAYOUT" /> <param name="column" value="info~DYNAMIC~org.apache.log4j.jdbcplus. examples.MyColumnHandler" /> <param name="column" value="mdc~MDC~MyMDC" /> <param name="column" value="created_on~TIMESTAMP" /> <param name="column" value="created_by~STATIC~me" /> <param name="usePreparedStatements" value="true"/> <param name="layoutPartsDelimiter" value="#-#"/> <!- layout with conversion pattern -> <layout class="org.apache.log4j.PatternLayout"> <!- conversion pattern with 4 parts separated by ##, second part is empty --> <param name="ConversionPattern" value="[%t] %m####%d\{dd.MM.yyyy\}#-#% d\{HH:mm:ss\}"/> </layout>
Classe: org.apache.log4j.PatternLayout
El resultat de l'aplicació d'aquests patrons són missatges de texte pla uniformement formatejats.
Classe: [ org.apache.log4j.XMLLayout|http://logging.apache.org/log4j/docs/api/org/apache/log4j/xml/XMLLayout.html] <?xml version=1.0"?> <!DOCTYPE log4j*:*eventSet ** <!ENTITY* data SYSTEM "*file:///abc">]> <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"> &data; </log4j:eventSet> L'atribut version de l'element eventSet indica la versió de Log4J que es fa servir. 1.1 indica una versió de Log4J anterior a la 1.2 i un valor per l'atribut 1.2 indica una versió 1.2 o posterior. <log4j:event logger="net.gencat.ctti.canigo.services.logging.snmp. test.TrapReceiver" timestamp="1127743279027" level="DEBUG" thread="main"> <log4j:message><![CDATA[missatge...]]></log4j:message> </log4j:event> Qualsevol informació de tipus contextual (com ara l'userId) s'ha d'incloure dins la informació contextual del log i automàticament s'inclourà en la traça:
<log4j:event logger="net.gencat.ctti.canigo.services.logging.test. LoggingClient" timestamp="1127746730256" level="DEBUG" thread="main"> <log4j:message><![CDATA[Log property file: log4j-test.xml.es-c7pym1j]]> </log4j:message> <log4j:NDC><![CDATA[userId: me]]></log4j:NDC> </log4j:event>
Classe: net.gencat.ctti.canigo.services.logging.log4j.xml.PatternXMLLayout Es permet definir els següents paràmetres:
Exemple:
<appender name="file" class="org.apache.log4j.RollingFileAppender"> <param name="File" value="D:/logs/logging4.log.xml"/> <param name="Append" value="true"/> <param name="MaxFileSize" value="50KB"/> <param name="maxBackupIndex" value="3"/> <layout class="net.gencat.ctti.canigo.services.logging.log4j.xml.PatternXMLLayout"> <param name="NameSpace" value="log4j"/> <param name="NodeName" value="registre"/> <param name="AttrValuePairDelim" value="="/> <param name="AttrVarDelim" value=";"/> <param name="AttrConversionPattern" value="attr1=%-5p;attr2=%t;attr3=%c" /> <param name="NodesValuePairDelim" value="="/> <param name="NodesVarDelim" value=";"/> <param name="NodesConversionPattern" value="data=%d\{yyyy MM dd HH:mm:ss\}; errorType=0;aplicacio=%c;logger=%F;nivell=%p;classe=%c;metode=metode; pid=%t; missatge=%m;user_id=%X\{userId\};pagOrigen=%X\{pagOrigen\};" /> <param name="Throwable" value="textException"/> <param name="ExceptionMaxLength" value="256"/> </layout> </appender> Un exemple de sortida amb aquesta configuració és: <log4j:registre attr1="ERROR" attr2="SNMP Server" attr3="net.gencat.ctti.canigo.services.logging.snmp.test.TestListener"> <log4j: Data>2005 10 13 13:29:21</log4j: Data> <log4j:errorType>0</log4j:errorType> <log4j:aplicacio>net.gencat.ctti.canigo.services.logging.snmp.test.TestListener</log4j:aplicacio> <log4j:logger>Log4JLog.java</log4j:logger> <log4j:nivell>ERROR</log4j:nivell> <log4j:classe>net.gencat.ctti.canigo.services.logging.snmp.test.TestListener</log4j:classe> <log4j:metode>metode</log4j:metode> <log4j: Pid>SNMP Server</log4j: Pid> <log4j:textException><![CDATA[java.lang.Exception: Exception Message at net.gencat.ctti.canigo.services.logging.snmp.test.TestListener.snmpRequest(TestListener.java:38) at ca.wengsoft.snmp.Core.SnmpDispatcher.notifyEvent(SnmpDispatcher.java:164) at ca.wengsoft.snmp.Core....]]> </log4j:textException> </log4j:registre> En cas que no s'hagués defint el paràmetre NameSpace obtindrem aquest altre format: <registre attr1="ERROR" attr2="SNMP Server" attr3="net.gencat.ctti.canigo.services.logging.snmp.test.TestListener"> <data>2005 10 13 13:30:40</data> <errorType>0</errorType> <aplicacio>net.gencat.ctti.canigo.services.logging.snmp.test.TestListener</aplicacio> <logger>Log4JLog.java</logger> <nivell>ERROR</nivell> <classe>net.gencat.ctti.canigo.services.logging.snmp.test.TestListener</classe> <metode>metode</metode> <pid>SNMP Server</pid> <textException><![CDATA[java.lang.Exception: Exception Message at net.gencat.ctti.canigo.services.logging.snmp.test.TestListener.snmpRequest(TestListener.java:38) at ca.wengsoft.snmp.Core.SnmpDispatcher.notifyEvent(SnmpDispatcher.java:164) at ca.wengsoft.snmp.Core....]]> </textException> </registre> Incorporació de valors comuns a l'aplicació Utilitzant aquest layout podem crear logs amb valors provinents de variables ja especificades en qualsevol punt de l'aplicació. Exemple: Imaginem que el filtre d'autentificació obté l'usuari i volem mostrar l'usuari connectat a les nostres traces. Una possible opció és que la classe que generés la traça accedís prèviament a l'obtenció de l'usuari connectat (a la sessió, cridant a la classe d'autentificació, etc.) i afegís aquesta informació a la traça. Aquesta opció és costosa i poc mantenible. Si volem mostrar a totes les traces de la nostra aplicació l'usuari connectat haurem de realitzar aquest procediment. I si després ens demanen que s'afegeixi altra informació contextual a totes les traces de l'aplicació? Per evitar aquestes problemàtiques canigo proporciona la classe ' net.gencat.ctti.canigo.core.threadlocal.ThreadLocalProperties'. ThreadLocalProperties.put("userId","Usuari1"); Una vegada introduit en qualsevol punt de l'aplicació aquest atribut, podem configurar al layout que es mostri aquesta informació utilitzant el caràcter '$' seguit del nom del atribut. Exemple:
<param name="NodesConversionPattern" value="data=%d\{yyyy MM dd HH:mm:ss\};user_id=$userId;" /> Aquest paràmetre crearia un tag amb un node 'data' obtingut amb el patró de conversió indicat i un tag 'user_id' amb el valor del paràmetre userId obtingut de l'objecte 'ThreadLocalProperties':
<log4j:registre> <log4j: Data>2005 10 19 11:29:40</log4j: Data> <log4j:user_id>Usuari1</log4j:user_id> </log4j:registre>
Per lo general els missatges de traces són cadenes. Si volem generar informació adicional estructurada farem ús de l'objecte BaseLoggingObject per afegir la informació (veure apartat 'Utilització - Generar logs amb paràmetres adicionals a un missatge'). Per mostrar els atributs de l'objecte BaseLoggingObject s'usarà la sintaxi '@' seguida del nom del paràmetre. <param name="NodesConversionPattern" value="idTramit=@idTramit;" /> Utilització del ServeiGenerar MissatgesTal i com s'ha comentat a l'apartat 'Arquitectura i Components' les classes principals per a la generació de les traces es troben al package 'net.gencat.ctti.canigo.services.logging'. Per generar un missatge seguirem un patró com el mostrat en el següent exemple:
if(this.loggingService!=null){ String missatge = "Missatge"; Log log = this.loggingService.getLog(this.getClass()); log.info(missatge); } Realitzarem doncs els següents pasos:
Aquest missatge és per lo general un String, però també es permet l'ús de la classe 'BaseLoggingObject' per generar un missatge estructurat (veure apartat 'Generar missatges estructurats').
En el cas de fer ús de la implementació de log4j, el nom de la categoria ha de correspondre al nom especificat en l'atribut 'name' del tag 'category' del fitxer de propietats de log4j.
<category name="net.gencat.ctti"> <appender-ref ref="console"/> <appender-ref ref="file"/> </category>
log.info(), log.debug(),.... Evitar càlculs innecessàrisUna de les qüestions més importants del sistema de traces és el cost de computació. Si en un moment donat es desactiven determinats nivells de traces, tot i que aquesta traça no es generi a la sortida el missatge és avaluat (ja que es troba com un paràmetre de la crida). Si l'avaluació del missatge comporta càlculs estem afegint un cost innecessari. Per evitar afegir costos adicionals de cómput innecessari es recomana prèviament comprovar que es troba activat el nivell pel qual generarem el missatge. Així, per exemple, enlloc de:
log.debug("....");
Haurem sempre de realitzar:
if (log.isDebugEnabled()) log.debug("...."); Generar missatges estructuratsPer lo general les traces enviades són missatges purs (cadenes de tipus String). En cas de que volguem generar informació estructurada del missatge es pot fer ús de l'objecte 'net.gencat.ctti.canigo.services.logging.log4j.BaseLoggingObject' com si del missatge es tractés. La utilització d'aquesta classe és especialment útil en l'ús del layout 'PatternXMLLayout', el qual ens permet generar tags específics per estructurar la nostra informació. Per a utilitzar-ho passaram a cadascun dels mètodes de traça l'objecte creat de la classe 'BaseLoggingObject'. Aquesta classe permet 2 constructors:
Exemple:
log.debug(new BaseLoggingObject(new Object[]\{"idTramit","34","missatge","missatge del log"})); O bé:
Hashtable params = new Hashtable(); params.put("idTramit","34"); params.put("missatge","missatge del log"); log.debug(new BaseLoggingObject(params)); Veure la secció 'PatterXMLLayout - Configuració Incorporació de missatges estructurats a la traça' per configurar que se'ns generi la informació introduïda a l'objecte BaseLoggingObject. Generar Informació ContextualMoltes vegades no és suficient amb la informació estàndard que es mostra a les traces i és necessari complementar-la amb informació contextual cóm: usuari que fa la petició, temps d'inici i final, etc. Per afegir aquesta informació, és necessari inicialitzar-la en algun moment de la petició per què estigui disponible durant tot el processat de la mateixa. Per tant, el patró de treball a seguir és:
canigo suporta la inserció d'aquesta informació mitjançant el mètode public void putInContext(String key, Object object) de la interfície "net.gencat.ctti.canigo.services.logging.Log". Cal remarcar que el contexte manegat és una solució basada en "contexte per fil" (context per thread), per tant al finalitzar la petició el programador s'ha de preocupar de buidar el contexte del "thread" amb el mètode public void removeContext() Una possible solució per seguir el patró de treball abans esmentat, és fer servir els filtres de l'especificació de l'API Servlet. Així, podríem tenir un filtre que ens insertès, per exemple, el nom d'usuari, tal i com es mostra a continuació:
... public class RequestLoggingFilter implements Filter { ... private LoggingService loggingService = null; public void init(FilterConfig filterConfig) throws ServletException { // retrieves the loggingService ... } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain chain) throws IOException, ServletException \{* HttpServletRequest request = (HttpServletRequest) arg0; HttpServletResponse response = (HttpServletResponse) arg1; String userLogin = (String)request.getSession().getAttribute("userLogin"); if (userLogin!=null) \{ this.loggingService.getLog(this.getClass()).putInContext("userLoginId",userLoginId); } String remoteHost = request.getRemoteHost(); String remoteAddr = request.getRemoteAddr(); this.loggingService.getLog(this.getClass()).putInContext("remoteHost",remoteHost); this.loggingService.getLog(this.getClass()).putInContext("remoteAddr",remoteAddr); try { chain.doFilter(arg0,arg1); } finally { this.loggingService.getLog(this.getClass()).removeContext(); } } ... } Generar nous nivells de tracesEl servei de traces afegeix varis nivells de traces (debug, info, warn, error, fatal y audit). En cas de voler afegir un nou nivell es pot seguir el següent procediment:
Exemple :
package net.gencat.ctti.canigo.services.logging*;* ... public interface Log { ... public void audit(java.lang.Object message); public void audit(java.lang.Object message, java.lang.Throwable); public boolean isAuditEnabled(); ... }
NOTA: En aquest exemple es mostra un exemple d'aplicació que ja es troba incorporar a canigo Per últim, la referència al nou nivell des del fitxer de propietats es farà mitjançat l'ús del tag '<level>' indicant com a value el nou nivell:
... <category name="net.gencat.ctti.canigo.services.logging"> <level value="audit" class="net.gencat.ctti.canigo.services.logging.log4j.AuditLevel"/> <appender-ref ref="file"/> </category> ... Eines de SuportServidor de proves SNMPEn el cas de voler instal- lar un servidor SNMP local per a realitzar proves amb SNMP, canigo proporciona unes classes de test per a fer ús d'una implementació opensource de servidor.
Classe:'net.gencat.ctti.canigo.services.logging.snmp.test.TestListener' Definir les següents propietats:
Exemple:
<bean id="listener" class="net.gencat.ctti.canigo.services.logging.snmp.test.TestListener"> <property name="loggingService"><ref local="loggingService"/></property> </bean>
Classe: 'net.gencat.ctti.canigo.services.logging.snmp.test.Security' Definir les següents propietats:
Exemple:
<bean id="security" class="net.gencat.ctti.canigo.services.logging.snmp.test.Security"> <property name="loggingService"><ref local="loggingService"/></property> </bean> Per últim definir el servei que rebrà els missatges enviats per l'appender configurat Classe: 'net.gencat.ctti.canigo.services.logging.snmp.test.TrapReceiver'
Com a propietats configurarem:
Exemple:
<bean id="trapReceiver" class="net.gencat.ctti.canigo.services.logging. snmp.test.TrapReceiver" init-method="init"> <property name="loggingService"><ref local="loggingService"/></property> <property name="listener"><ref local="listener"/></property> <property name="security"><ref local="security"/></property> <property name="defaultPort"> <value>8001</value> </property> </bean> Visualització de LogsVisualització amb chainsaw:Per visualitzar els logs provinents de la nostra aplicació podem utilitzar una eina anomenada chainsaw, que trobem dins del paquet de log4j (org.apache.log4j.chainsaw). Aquesta eina ens permet visualitzar tant els events generats per l'aplicació com els provinents d'un fitxer xml generat per XMLLayout. Per visualitzar un fitxer xml només caldrà executar l'eina chainsaw i obrir el fitxer. Aquest fitxer no ha d'ésser un fitxer xml ben format. Ha de tenir l'estructura que presenta quan és generat per XMLLayout, és a dir, només haurà de tenir els nodes dels events, sense la capçalera xml, ja que chainsaw ja la introdueix abans de parsejar el fitxer. Així, aquest fitxer no podrà ser visualitzat pel chainsaw:
<?xml version=1.0"?> <!DOCTYPE log4j:eventSet <!ENTITY data SYSTEM "file:///abc"> ]> <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/"> <log4j:event logger="net.gencat.ctti.canigo.services.logging.test.LoggingClient" timestamp="1127746730256" level="DEBUG" thread="main"> <log4j:message><![CDATA[Log property file: log4j-test.xml.es-c7pym1j]]></log4j:message> <log4j:NDC><![CDATA[userId: me]]></log4j:NDC> </log4j:event> </log4j:eventSet> Però sí podrà visualitzar fitxers amb aquest format:
<log4j:event logger="net.gencat.ctti.canigo.services.logging.test.LoggingClient" timestamp="1127746730256" level="DEBUG" thread="main"> <log4j:message><![CDATA[Log property file: log4j-test.xml.es-c7pym1j]]></log4j:message> <log4j:NDC><![CDATA[userId: me]]></log4j:NDC> </log4j:event> Per visualitzar els events provinents d'una aplicació haurem de configurar l'arxiu log4j.xml.nom_del_host de la següent manera:
<appender name="chainsaw" class="org.apache.log4j.net.SocketAppender"> <param name="remoteHost" value="localhost"/> <param name="port" value="4445"/> <param name="locationInfo" value="true"/> </appender> Aquí creem un appender amb el nom chainsaw i amb els següents remoteHost = nom del host on executem el chainsaw Seguidament hem d'especificar quines classes enviaran els seus events cap a chainsaw, com fem amb els altres appenders:
<category name="net.gencat.ctti.canigo.services"> <appender-ref ref="chainsaw"/> <appender-ref ref="console"/> <appender-ref ref="file"/> <appender-ref ref="snmp"/> </category> Un cop configurat el fitxer tots els events que produeixin aquestes classes seran automàticament enviats cap al chainsaw. Visualització amb ValueList:Una segona manera de veure les traces és a través d'una ValueList dins d'una pàgina jsp. Per a poder usar aquest mètode el primer que s'ha de fer és generar una nova entrada al fitxer canigo-services-web-lists.xml, concretament a dins del <map> de la propietat <config.adapters>.
<entry key="tracesList"> <bean name="tracesListAdapter" class="net.gencat.ctti.canigo.services.logging.log4j.xml.impl. DailyRollingFileLogFinderAdapter"> <property name="logBuilder"> <bean class="net.gencat.ctti.canigo.services.logging.log4j.xml.impl. DailyRollingFileLogBuilder"> <property name="maxLogs" value="${tracesList.maxLogs}" /> <property name="fileDatePattern" value="${tracesList. fileDatePattern}" /> <property name="filterDatePattern" value="${tracesList. filterDatePattern}" /> <property name="logItemDatePattern" value="${tracesList. logItemDatePattern}" /> <property name="fileSystemService" ref="fileSystemService" /> <property name="logService" ref="loggingService" /> <property name="filtersConfig"> <map> <entry key="userId" value="EQUALS" /> <entry key="nivell" value="EQUALS" /> <entry key="missatge" value="LIKE" /> </map> </property> </bean> </property> <property name="logService" ref="loggingService" /> <property name="defaultNumberPerPage" value="${tracesList.defaultNumberPerPage}" /> </bean> </entry> I afegir la següent definició de bean al mateix fitxer:
<bean name="extendedValueListActionHelper" class="net.gencat.ctti.canigo.services.web.lists.impl.ExtendedValueListActionHelper"> <property name="valueListHelper"> <ref bean="valueListHelper" /> </property> <property name="maxExportRows" value="10000"></property> <property name="logService" ref="loggingService" /> </bean> Un cop ja es té la llista definida, el següent pas es generar una estructura amb una acció, unes pàgines jsp i els seus corresponents arxius de configuració i propietats (a la plantilla se'n pot veure un exemple). El més normal seria seguir una estructura de cerca, és a dir una pàntalla amb les condicions de cerca i posteriorment una pantalla amb la ValueList mostrant els resultats de la cerca. Cal destacar que a l'acció que realitza la cerca hi ha d'haver el codi següent, que és el que permet fer la cerca correctament:
//Posem la localització del fitxer de traces XML traza.setRuta(confService.getProperty("ruta.fitxerLogXml")); //Es fa la cerca i es carrega a la valueList this.valueListActionHelper.search(mapping,form,request,response); L'arxiu de configuració de l'acció tindrà una aparença com el que es mostra a continuació:
<bean lazy-init="true" name="/llistaTraces" class="net.gencat.ctti.canigo.samples.jpetstore.struts.action.TracesAction"> <property name="logService" ref="loggingService" /> <property name="pojoClass" value="net.gencat.ctti.canigo.services.logging. log4j.xml.impl.TrazaCtti" /> <property name="valueListActionHelper"> <bean parent="extendedValueListActionHelper"> <property name="exposedProperties"> <list> <value>ruta</value> </list> </property> <property name="listName"><value>tracesList</value></property> <property name="tableId" value="LOGS"/> </bean> </property> <property name="levels"> <list> <value>DEBUG</value> <value>INFO</value> <value>WARN</value> <value>ERROR</value> <value>FATAL</value> </list> </property> <property name="confService" ref="configurationService"/> ... En aquesta configuració es mostra com s'ha d'indicar el nom de la llista definida a l'arxiu canigo-services-web-lists.xml, el nom de l'atribut de la classe TrazaCtti que conté el nom del fitxer de traces, etc. I amb aquestes indicacions i l'exemple de la plantilla ja n'hi hauria d'haver prou per fer una petita acció amb una ValueList de les traces XML. Altres visualitzacions:Per visualitzar fitxers xml que no segueixin l'estructura dels que genera XMLLayout, com els de PatternLayout o PatternXMLLayout, no podran ser visualitzats pel *chainsaw*. Haurem d'utilitzar editors de text o bé d'xml, pel cas dels generats per _PatternXMLLayout_. ExemplesTests UnitarisUn exemple d'utilització del servei de traces són els tests unitaris, a on s'obté el bean del servei a partir del fitxer de definició (applicationContext.xml) i s'envia un log a les sortides especificades.
package net.gencat.ctti.canigo.services.mail.test; ... public class LoggingServiceTest extends TestCase \{ ... public void testLogging()\{ /** * Obtenim les definicions dels beans */ ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml"); /** * Obtenim el servei de logs */ BeanFactory factory = (BeanFactory) appContext; /** * Obtenim un client de test per llençar la traça */ LoggingService service=(LoggingService)factory.getBean("loggingService"); FileSystemResource resource = new FileSystemResource( client.getLoggingService().getConfigurator(). getConfigFileName() ); assertTrue("Log property file doesn't exist: "+resource.getFilename(),resource.exists()); if(resource.exists())\{ /** * Llencem la traça */ Log log = service.getLog(this.getClass()); if(log.isDebugEnabled()) log.debug("Log property file: "+resource.getFilename()); } } ... } La utilització del servei es independent de la configuració del mateix. Així, un possible fitxer de configuració del servei seria:
ge net.gencat.ctti.canigo.services.mail.test; ... public class LoggingServiceTest extends TestCase \{ ... public void testLogging()\{ /** * Obtenim les definicions dels beans */ ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext ("applicationContext.xml"); /** * Obtenim el servei de logs */ BeanFactory factory = (BeanFactory) appContext; /** * Obtenim un client de test per llençar la traça */ LoggingService service=(LoggingService)factory.getBean("loggingService"); FileSystemResource resource = new FileSystemResource( client.getLoggingService(). getConfigurator().getConfigFileName() ); assertTrue("Log property file doesn't exist: "+resource.getFilename(),resource.exists()); if(resource.exists())\{ /** * Llencem la traça */ Log log = service.getLog(this.getClass()); if(log.isDebugEnabled()) log.debug("Log property file: "+resource.getFilename()); } } ... } La utilització del servei es independent de la configuració del mateix. Així, un possible fitxer de configuració del servei seria: <?xml version="1.0"?> <!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J//EN" "http://logging.apache.org/log4j/ docs/api/org/apache/log4j/xml/log4j.dtd"> <log4j:configuration> <appender name="JDBCPooled" class="org.apache.log4j.jdbcplus.JDBCAppender"> <param name="connector" value="org.apache.log4j.jdbcplus.examples.FirebirdPoolConnectionHandler"/> <param name="sql" value="INSERT INTO LOGTEST (id, prio, cat, thread, msg, layout_msg, throwable, ndc, mdc, mdc2, info, addon, the_date, the_time, the_timestamp, created_by) VALUES ( (INC), ' (PRIO)', ' (CAT)', ' (THREAD)', ' (MSG)', '@LAYOUT:1@', ' (THROWABLE)', ' (NDC)', '@MDC:MyMDC@', '@MDC:MyMDC2@', 'info timestamp: (TIMESTAMP)', 'addon', cast ('@LAYOUT:3@' as date), cast ('@LAYOUT:4@' as time), cast ('@LAYOUT:3@ @LAYOUT:4@' as timestamp), 'me')"/> <param name="buffer" value="1"/> <param name="commit" value="true"/> <param name="dbclass" value="org.firebirdsql.jdbc.FBDriver"/> <param name="quoteReplace" value="true"/> <param name="throwableMaxChars" value="3000"/> <param name="layoutPartsDelimiter" value="#-#"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="[%t] %m####%d\{dd.MM.yyyy\}#-#%d\{HH:mm:ss\}"/> </layout> </appender> <appender name="SYSTEMOUT" class="org.apache.log4j.ConsoleAppender"> <param name="target" value="System.out"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value=" %d\{dd.MM.yyyy HH:mm:ss\} - %m\n"/> </layout> </appender> <appender name="SNMP" class="org.apache.log4j.ext.SNMPTrapAppender"> <param name="ImplementationClassName" value="org.apache.log4j.ext.WengsoftSNMPTrapSender"/> <param name="ManagementHost" value="127.0.0.1"/> <param name="ManagementHostTrapListenPort" value="8001"/> <param name="EnterpriseOID" value="1.3.6.1.4.1.24.0"/> <param name="LocalIPAddress" value="127.0.0.1"/> <param name="LocalTrapSendPort" value="161"/> <param name="GenericTrapType" value="6"/> <param name="SpecificTrapType" value="12345678"/> <param name="CommunityString" value="public"/> <param name="Threshold" value="DEBUG"/> <layout class="org.apache.log4j.ext.SnmpDelimitedConversionPatternLayout"> <param name="ValuePairDelim" value="/"/> <param name="VarDelim" value=";"/> <param name="ConversionPattern" value="%-5p;1.3.6.1.4.1.24.100.1/%t;1.3.6.1.4.1. 24.100.2/%c;1.3.6.1.4.1.24.100.3/%m;1.3.6.1.4.1.24.100.4" /> </layout> </appender> <appender name="MAIL" class="org.apache.log4j.net.SMTPAppender"> <param name="threshold" value="ERROR"/> <param name="SMTPHost" value="smtp.ctti.net"/> <param name="subject" value="Error!"/> <param name="to" value="xavi.xicota@ctti.net,jordi.negrete@ctti.net"/> <param name="from" value="xavi.xicota@ctti.net"/> </appender> <appender name="FILE" class="org.apache.log4j.DailyRollingFileAppender"> <param name="File" value="/var/logs/daily_application.log"/> <param name="DatePattern" value="'.'yyyy-MM-dd"/> <param name="Append" value="true"/> <param name="MaxFileSize" value="50KB"/> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="canigo Message: %-5p [%t] %c - %m%n"/> </layout> </appender> <root> <appender-ref ref="SYSTEMOUT" /> <appender-ref ref="JDBCPooled"/> <appender-ref ref="FILE"/> <appender-ref ref="MAIL"/> </root> </log4j:configuration> AnnexosIntroducció a Log4JEn aquest annex s'ofereix una breu introducció a conceptes de Log4J que cal entendre per utilitzar el Servei de Traces. A Log4J, existeixen varis elements clau:
|