2011-12-13 4 views
5

Mamy starą, starą bibliotekę Java, która jest tworzona z wielu różnych aplikacji. W tym przypadku każda aplikacja jest aplikacją internetową, w której wszystkie żyją w tym samym kontenerze tomcat.oddzielny rejestrator dla każdej instancji biblioteki

Każda aplikacja loguje się do własnego pliku dziennika, używając własnego programu rejestrującego. Chcemy, aby dzienniki generowane przez bibliotekę, które są istotne dla konkretnej aplikacji, również trafiały do ​​tych aplikacji osobny plik dziennika.

W tym jednym ze sposobów jest, aby umożliwić stosowanie przejść w rejestratorze do biblioteki:

library = new library(Logger applicationsVeryOwnLogger); 

a następnie użyć tego rejestratora, aby rejestrować wszystkie wypowiedzi w bibliotece. Oznacza to jednak, że rejestrator jest teraz zmienną klasy w bibliotece, a każda klasa w bibliotece potrzebuje odwołania do biblioteki, aby użyć odpowiedniego rejestratora.

Czy są na to lepsze sposoby?

Odpowiedz

2

Mamy podobną potrzebę w jednej z naszych starszych aplikacji. Rozwiązaniem, które wymyśliliśmy, był ResourceManager, który pobierał zasoby (Logger, Config files etc) według (context) ClassLoader.

Zwykle każda aplikacja wdrożona jako EAR otrzymuje własną klasę ClassLoader, a biblioteka może wtedy wywołać ResourceManager.getLogger() w celu uzyskania Loggera powiązanego z bieżącym wątkiem/aplikacją. W ten sposób nie musisz go przekazywać przy każdym wywołaniu metody w bibliotece (wymaga to zmiany biblioteki).

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); 
    }  
} 

Rejestracja Rejestrator w fazie init EJB/aplikacji sieci Web (musi być rejestrowane przed każdym wywołaniu getLogger):

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

Retrieve rejestratora w bibliotece (metoda Narzędzie)

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

Spowoduje to zwrócenie programu rejestrującego dla aplikacji (EAR) powiązanej z bieżącym wątkiem.

Nie ogranicza się tylko do Loggers działa również dla innych zasobów, które chcesz udostępnić.

Ograniczenia:

  • przyzwyczajenie praca, jeśli pakiet wielu aplikacji/EJBs za wdrożonej EAR

  • ResourceManager i biblioteki rejestrowania muszą być na tym samym lub wyższym ClassLoader niż biblioteki i aplikacji. Jeśli istnieje opcja łączenia, to podejście Alexandersa jest czystsze. (używamy java.util.logging, który jest domyślnie na poziomie serwera, więc jego podejście nie działa)

+0

Na marginesie tego rozwiązania można również używać do dzielenia się wszelkimi zasobami, a nie tylko rejestratorami. – Stefan

+0

możesz wkleić odpowiednie sekcje kodu w swojej odpowiedzi, na wypadek gdyby link umarł w przyszłości. – rouble

+0

Inną rzeczą, na którą warto zwrócić uwagę na temat tej metody, jest to, że ResourceManager.registerLogger() musi zostać wywołany przed utworzeniem instancji biblioteki. W przeciwnym razie rejestratory klas zostaną ustawione przy użyciu domyślnego programu rejestrującego. Może to być przełom w przypadku niektórych implementacji. – rouble

3

Oznaczono pytanie znacznikiem log4j, więc zakładam, że właśnie tego używasz.

Mam nadzieję, że Twoja biblioteka używa unikalnej nazwy pakietu.

Jeśli tak jest, możesz po prostu skonfigurować rejestrator dla tego pakietu.

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

Może to komunikaty będą się logować z biblioteki zarówno libFileAppender i rootFileAppender.

Jeśli nie chcesz, aby wiadomości od biblioteki do stawienia się w rootFileAppender można wyłączyć tego rejestratora addytywności, tak:

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

z tym, tylko zobaczy wiadomość w libFileAppender

+0

Nie sądzę, że to spełnia wymagania. Dzięki temu każdy dziennik z biblioteki może zostać przekierowany do jednego miejsca. Potrzebujemy jednak każdego dziennika z biblioteki generowanej przez określoną aplikację, aby przejść do jednego miejsca (który jest plikiem dziennika aplikacji). – rouble

+0

@prmatta. Przerzuć ten plik na WEB-INF/classes i program instalacyjny, który jest specyficzny dla tej aplikacji, a każda aplikacja będzie rejestrować połączenia biblioteki do tego aplikanta. Nie rozumiem, jak to nie spełnia twoich wymagań. –

+0

Arnt the Loggers/Appenders dla log4j zarządzane na poziomie JVM? A jeśli użyjesz tej samej nazwy dla wszystkich rejestratorów (musisz, ponieważ biblioteka potrzebuje nazwy), to rejestrujesz ten sam rejestrator. – Stefan