2011-12-13 17 views
5

Abbiamo una semplice libreria java vecchia che viene istanziata da molte applicazioni diverse. In questo caso, ciascuna applicazione è un'applicazione Web che vive nello stesso contenitore Tomcat.logger separato per ogni istanza di una libreria

Ogni applicazione registra nel proprio file di registro, utilizzando il proprio registratore. Vogliamo che i log generati dalla libreria, che sono pertinenti a un'applicazione specifica, possano anche andare al file di registro separato di tali applicazioni.

Per questo, in un modo è quello di consentire l'applicazione di passare nel suo registratore alla libreria:

library = new library(Logger applicationsVeryOwnLogger); 

e quindi utilizzare tale logger, per registrare tutte le dichiarazioni nella libreria. Tuttavia, ciò significa che il logger è ora una variabile di classe nella libreria e ogni classe nella libreria richiede un riferimento alla libreria per utilizzare il logger corretto.

Ci sono modi migliori per farlo?

risposta

2

Abbiamo avuto un'esigenza simile in una delle nostre applicazioni precedenti. La soluzione che abbiamo trovato era un ResourceManager che recuperava risorse (Logger, file di configurazione, ecc.) Da (contesto) ClassLoader.

In genere ogni applicazione distribuita come EAR ottiene il proprio ClassLoader e la libreria può quindi chiamare ResourceManager.getLogger() per ottenere il Logger associato all'attuale Thread/Applicazione. In questo modo non hai bisogno di passarlo con ogni chiamata di metodo nella libreria (richiede che tu possa cambiare la libreria).

import java.util.*; 
import java.util.logging.*; 

public class ResourceManager 
{ 
    private static final Map<ClassLoader, Map<String, Object>> resources = 
     Collections.synchronizedMap(new WeakHashMap<ClassLoader, Map<String, Object>>()); 
    public static final String LOGGER = Logger.class.getName(); 

    static 
    { 
     // adjust for log4j or other frameworks 
     final Logger logger = Logger.getLogger("logging.default"); 
     logger.setLevel(Level.ALL); 
     logger.addHandler(new ConsoleHandler() 
     { 
      { 
       setOutputStream(System.out); 
       setLevel(Level.ALL); 
      } 
     }); 
     registerResource(null, LOGGER, logger); 
    } 

    private static ClassLoader getApplicationScope() 
    { 
     return Thread.currentThread().getContextClassLoader(); 
    } 

    public static void registerResource(final String name, final Object resource) 
    { 
     registerResource(getApplicationScope(), name, resource); 
    } 

    public static synchronized void registerResource(final ClassLoader scope, final String name, final Object resource) 
    { 
     Map<String, Object> hm = null; 
     hm = resources.get(scope); 
     if (hm == null) 
     { 
      hm = Collections.synchronizedMap(new HashMap<String, Object>()); 
      resources.put(scope, hm); 
     } 
     hm.put(name, resource); 
    } 

    public static Object getResource(final String name) 
    { 
     for(ClassLoader scope = getApplicationScope();;scope = scope.getParent()) 
     { 
      final Map<String, Object> hm = resources.get(scope); 
      if ((hm != null) && hm.containsKey(name)) 
      { 
       return hm.get(name); 
      } 
      if (scope == null) break; 
     } 
     return null; 
    } 

    public static void registerLogger(final Logger logger) 
    { 
     registerResource(LOGGER, logger); 
    } 

    public static Logger getLogger() 
    { 
     return (Logger)getResource(LOGGER); 
    }  
} 

Registrati Logger nella fase di init di EJB/WebApp (deve essere registrato prima di ogni chiamata a getLogger):

Logger logger = Logger.getLogger([Application Logger Name]); 
ResourceManager.registerLogger(logger); 

Recupera Logger in libreria (metodo di utilità):

private Logger getLogger() 
    { 
     return ResourceManager.getLogger();  
    } 

Ciò restituirà il registratore per l'applicazione (EAR) associato al thread corrente.

Non limitato ai logger, funziona anche per altre risorse che si desidera condividere.

Limitazioni:

  • non funzionerà se si comprime più applicazioni/EJB per orecchio schierato

  • ResourceManager e biblioteca registrazione bisogno di essere sullo stesso o un ClassLoader superiore alla biblioteca e applicazione. Se esiste un'opzione per il raggruppamento, allora l'approccio di Alexanders è più pulito. (usiamo java.util.logging che è di default a livello del server quindi il suo approccio non funziona)

+0

Come nota a margine, questa soluzione può anche essere utilizzata per condividere tutti i tipi di risorse, non solo i logger. – Stefan

+0

potresti voler incollare sezioni di codice rilevanti nella tua risposta, nel caso in cui il link dovesse morire in futuro. – rouble

+0

Un'altra cosa da notare su questo metodo è che ResourceManager.registerLogger() deve essere chiamato prima dell'istanza della libreria. In caso contrario, i logger delle classi verranno impostati utilizzando il logger predefinito. Questo può essere un compromesso per alcune implementazioni. – rouble

3

Hai contrassegnato la tua domanda con il tag log4j, quindi presumo che sia quello che stai utilizzando.

Spero che la tua libreria stia utilizzando un nome di pacchetto univoco.

Se questo è il caso, in realtà è possibile impostare un logger per quel pacchetto.

log4j.category.my.lib.package = INFO, libFileAppender 
log4j.rootLogger = INFO, rootFileAppender 

Così facendo registrare i messaggi dalla libreria sia libFileAppender e rootFileAppender.

Se non si desidera i messaggi dalla libreria a comparire in rootFileAppender è possibile disattivare che logger additività, in questo modo:

log4j.category.my.lib.package = INFO, libFileAppender 
log4j.additivity.my.lib.package = false 
log4j.rootLogger = INFO, rootFileAppender 

Con questo, si vedrà solo messaggio nella libFileAppender

+0

Non penso che questo soddisfi i requisiti. Con questo ogni registro dalla libreria può essere reindirizzato in un unico posto. Tuttavia, abbiamo bisogno di ogni registro dalla libreria che viene generato da un'applicazione specifica per andare in un posto (che è il file di registro dell'applicazione). – rouble

+0

@prmatta. Getta questo file in WEB-INF/classi e imposta appender specifico per quella applicazione e ogni applicazione registra le chiamate della libreria a quell'appender. Non vedo come non soddisfi le tue esigenze. –

+0

Arnt i logger/appendici per log4j gestiti su un livello JVM? E se si utilizza lo stesso nome per tutti i logger (che è necessario perché la libreria ha bisogno del nome), si continua a registrare lo stesso registratore. – Stefan

Problemi correlati