Spring logger service inne podejście

W poprzednim wpisie pokazałem jak napisać własny procesor adnotacji do Springa. W komentarzach Leszek Gawron stwierdził, że można by było automatycznie rozpoznawać typ loggera na podstawie typu pola. Tyle słuszne co do końca niewykonalne.

Po pierwsze dlatego, że Logger nie zawsze jest klasą. W przypadku commons-logging logger jest interfejsem. Po drugie dlatego, że logger nie zawsze jest możliwość stworzenia loggera za pomocą operatora new. Musi być tworzony czy to przez metodę fabrykującą czy to przez oddzielną klasę – fabrykę. Po trzecie mechanizm adnotacji nie pozwala na swobodne wykorzystanie wszystkich wartości. Wartość adnotacji jest ograniczona do typów prostych, String, Class i w dodatku musi być znana w momencie kompilacji! Mamy zatem dwa wyjścia. Podanie jako String nazwy klasy fabrykującej… co jest głupie ponieważ właśnie tego chcemy uniknąć. Wskazanie klasy adaptera, która wykona za nas czarną robotę. Co nadal nie jest najlepszym rozwiązaniem, ale w przeciwieństwie do enumów umożliwia rozszerzanie naszej fabryki o dowolny logger.

Wersja 0.2 w repo. Adnotacja ma teraz dwie metody. Pierwsza pozwala na podanie nazwy klasy adaptera jako String druga podanie klasy w prost. Pierwsza jest o tyle dobra, że nie musimy mieć w bieżącym classpath adaptera. Druga jest pewniejsza. Nie ma jeszcze walidacji, ale to dopiszę w wolnej chwili.

7 myśli na temat “Spring logger service inne podejście

  1. Jak juz sie bawimy tak na „ostro” to czemu nie zrobic tak:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    @Documented
    @LogService(loggerFactoryAdapterClass = Log4jAdapter.class)
    public @interface Log4jLogger {

    }

    public class Log4jLogBeanSimple {
    @Log4jLogger
    private Logger logger;

    public Logger getLogger() {
    return logger;
    }
    }

    Oto patch, ktory implementuje @Log4jLogger: http://pastebin.com/K3iAfMbJ reszty mi sie nie chcialo 🙂

    BTW wg mnie @LogService moglby wygladac po prostu tak:
    public @interface LogService {
    Class value();
    }

    gdyby projekt podzielic na moduly.

  2. podejscie nr 2: poprzedni patch mozesz pominac – ten zawiera wiecej: http://pastebin.com/GLrtjgqW

    @Log4jLogger, @JavaSeLogger, @CommonsLogger, @Slf4Logger, a dodatkowo @AutoLogger.

    Jak widac wykonalne. Dziala super – no prawie:

    Kazdy adapter rejestruje sie w AutoDetectRegistry:

    public final class Log4jAdapter implements LoggerFactoryAdapter {
    static {
    AutoDetectRegistry.registerAdapter( Logger.class,
    new Log4jAdapter() );
    }

    public org.apache.log4j.Logger getLogger( Class fieldType, Object… factoryArguments ) {
    org.apache.log4j.Logger logger = null;
    if ( factoryArguments.length <= 0 )
    logger = org.apache.log4j.Logger.getLogger( Object.class );
    if ( factoryArguments[ 0 ] instanceof Class )
    logger = org.apache.log4j.Logger.getLogger( (Class) factoryArguments[ 0 ] );
    if ( factoryArguments[ 0 ] instanceof String )
    logger = org.apache.log4j.Logger.getLogger( factoryArguments[ 0 ].toString() );

    return logger;
    }
    }

    a wiec mamy tylko delikatny problem: classloader musi zaladowac klasy adapterow zanim zaczniemy autodetekcje:

    @Test
    public void testAuto() {
    // TODO jak to scierwo automatycznie zaladowac?
    try {
    Class.forName( Log4jAdapter.class.getName() );
    Class.forName( CommonsAdapter.class.getName() );
    Class.forName( Slf4jAdapter.class.getName() );
    Class.forName( JavaSeAdapter.class.getName() );
    } catch ( ClassNotFoundException e ) {
    throw new RuntimeException( „e” );
    }

    AutoBean bean = new AutoBean();
    logServicePostProcessor.postProcessBeforeInitialization(bean, „”);
    assertNotNull(bean.getLoggerLog4j());
    assertNotNull(bean.getLoggerCommons());
    assertNotNull(bean.getLoggerSlf4j());
    }

    To nie jest jednak wielki problem: zalozmy, ze dzielimy projekt na moduly. Kazdy w META-INF ma jakis sobie plik ktory listuje udostepniane adaptery. PostProcessor podczas inicjalizacji szuka wszystkich dostepnych META-INF/logger-adapters i laduje wylistowane w nich klasy. Dzieki temu mozemy tez pozbyc sie niezbyt ladnego statycznego AutoDetectRegistry …

    and the story continues …

  3. wiecej sie nie bawie bez praw do commita 🙂 ( … i zmiany licencji na ASL)

  4. czy jest szansa na to by Twoj blog wysylal mi powiadomienie mailem jesli pojawiaja sie nowe komentarze w watkach, ktore sam komentuje?

Napisz odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax