Domanda Perché non utilizzare java.util.logging?


Per la prima volta nella mia vita mi trovo in una posizione in cui sto scrivendo un'API Java che sarà open source. Spero di essere incluso in molti altri progetti.

Per la registrazione I (e in effetti le persone con cui lavoro) hanno sempre utilizzato JUL (java.util.logging) e non hanno mai avuto problemi con esso. Tuttavia ora ho bisogno di capire in modo più dettagliato cosa dovrei fare per il mio sviluppo dell'API. Ho fatto qualche ricerca su questo e con le informazioni che ho ottenuto sono solo più confuso. Quindi questo post.

Da quando vengo da luglio sono prevenuto su questo. La mia conoscenza del resto non è così grande.

Dalla ricerca che ho fatto ho trovato questi motivi per cui le persone non amano il LUG:

  1. "Ho iniziato a sviluppare in Java molto tempo prima che Sun pubblicasse JUL e per me è stato più semplice continuare con il logging-framework-X piuttosto che imparare qualcosa di nuovo". Hmm. Non sto scherzando, questo è in realtà ciò che la gente dice. Con questo argomento potremmo fare tutti COBOL. (tuttavia posso certamente riferire a questo essere un pigro me stesso)

  2. "Non mi piacciono i nomi dei livelli di registrazione in JUL". Ok, seriamente, questa non è una ragione sufficiente per introdurre una nuova dipendenza.

  3. "Non mi piace il formato standard dell'output di JUL". Hmm. Questa è solo la configurazione. Non devi nemmeno fare nulla in termini di codice. (Vero, ai vecchi tempi potresti aver dovuto creare la tua classe di formattazione per farlo bene).

  4. "Uso altre librerie che usano anche logging-framework-X, quindi ho pensato che fosse più semplice usare quello". Questa è una discussione ciclica, no? Perché "tutti" usano il logging-framework-X e non il JUL?

  5. "Tutti gli altri usano logging-framework-X". Questo per me è solo un caso speciale di quanto sopra. La maggioranza non ha sempre ragione.

Quindi la vera grande domanda è perché non luglio?. Cos'è che ho perso? La ragion d'essere delle facciate di logging (SLF4J, JCL) è che storicamente esistono implementazioni di logging e la ragione di ciò risale davvero all'era prima di luglio come la vedo io. Se JUL fosse perfetto, allora non esisterebbero facciate di logging o cosa? Piuttosto che abbracciarli, non dovremmo chiederci perché erano necessari in primo luogo? (e vedi se queste ragioni esistono ancora)

Ok, la mia ricerca finora ha portato a un paio di cose che posso vedere problemi reali con JUL:

  1. Prestazione. Alcuni dicono che le prestazioni in SLF4J sono superiori al resto. Questo mi sembra un caso di ottimizzazione prematura. Se devi registrare centinaia di megabyte al secondo, non sono sicuro che tu sia sulla strada giusta. Anche JUL si è evoluto e i test che hai fatto su Java 1.4 potrebbero non essere più veri. Puoi leggere a riguardo Qui e questa correzione è stata introdotta in Java 7. Molti parlano anche del sovraccarico della concatenazione delle stringhe nei metodi di registrazione. Tuttavia la registrazione basata su template evita questo costo ed esiste anche in JUL. Personalmente non scrivo mai il logging basato su template. Troppo pigro per quello. Ad esempio se lo faccio con JUL:

    log.finest("Lookup request from username=" + username 
       + ", valueX=" + valueX
       + ", valueY=" + valueY));
    

    il mio IDE mi avviserà e chiederà il permesso di cambiarlo in:

    log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", 
       new Object[]{username, valueX, valueY});
    

    .. che naturalmente accetterò. Permesso accordato ! Grazie per l'aiuto.

    Quindi in realtà non scrivo tali affermazioni da solo, che viene fatto dall'IDE.

    In conclusione sulla questione della performance non ho trovato nulla che possa suggerire che la prestazione del JUL non sia ok rispetto alla concorrenza.

  2. Configurazione da classpath. JUL pronto per l'uso non può caricare un file di configurazione dal classpath. È un poche righe di codice per farlo farlo. Posso capire perché questo può essere fastidioso, ma la soluzione è breve e semplice.

  3. Disponibilità di gestori di output. JUL è dotato di 5 gestori di uscita pronti all'uso: console, flusso di file, socket e memoria. Questi possono essere estesi o nuovi possono essere scritti. Ad esempio, potrebbe scrivere su UNIX / Linux Syslog e Windows Event Log. Personalmente non ho mai avuto questo requisito né l'ho visto usato, ma posso certamente riguardare il motivo per cui potrebbe essere una caratteristica utile. Logback viene fornito con un appender per Syslog, ad esempio. Ancora lo direi

    1. Il 99,5% delle esigenze per le destinazioni di output è coperto da ciò che è in JUL out-of-the-box.
    2. Le esigenze speciali potrebbero essere soddisfatte dai gestori personalizzati in cima a JUL piuttosto che sopra qualcos'altro. Non c'è niente per me che suggerisca che ci vuole più tempo per scrivere un gestore di output Syslog per JUL piuttosto che per un altro framework di registrazione.

Sono davvero preoccupato che ci sia qualcosa che ho trascurato. L'uso delle facciate di registrazione e delle implementazioni di registrazione diverse da JUL è così diffuso che devo arrivare alla conclusione che sono io che non capisco. Non sarebbe la prima volta, temo. :-)

Quindi cosa dovrei fare con la mia API? Voglio che abbia successo. Naturalmente posso solo "andare con il flusso" e implementare SLF4J (che sembra il più popolare in questi giorni) ma per il mio bene ho ancora bisogno di capire esattamente cosa c'è di sbagliato con il JUL di oggi che garantisce tutto il fuzz? Mi saboterò scegliendo JUL per la mia biblioteca?

Test delle prestazioni

(sezione aggiunta da nolan600 il 07-LUGLIO 2012)

Di seguito c'è un riferimento a Ceki sulla parametrizzazione di SLF4J che è 10 volte o più veloce di quella di JUL. Quindi ho iniziato a fare alcuni test semplici. A prima vista l'affermazione è certamente corretta. Ecco i risultati preliminari (ma continua a leggere!):

  • Tempo di esecuzione SLF4J, backend Logback: 1515
  • Tempo di esecuzione SLF4J, back-end JUL: 12938
  • Tempo di esecuzione JUL: 16911

I numeri sopra sono msec quindi meno è meglio. Quindi la differenza di prestazioni 10 volte è in primo luogo in realtà piuttosto vicino. La mia reazione iniziale: è molto!

Ecco il nucleo del test. Come si può vedere un numero intero e una stringa viene costruita in un ciclo che viene poi utilizzato nell'istruzione del registro:

    for (int i = 0; i < noOfExecutions; i++) {
        for (char x=32; x<88; x++) {
            String someString = Character.toString(x);
            // here we log 
        }
    }

(Volevo che l'istruzione del log avesse sia un tipo di dati primitivo (in questo caso un int) sia un tipo di dati più complesso (in questo caso una stringa). Non è sicuro che importi, ma ce l'hai.)

La dichiarazione del registro per SLF4J:

logger.info("Logging {} and {} ", i, someString);

La dichiarazione del registro per JUL:

logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});

La JVM è stata "scaldata" con lo stesso test eseguito una volta prima della misurazione effettiva. Java 1.7.03 è stato utilizzato su Windows 7. Sono state utilizzate le ultime versioni di SLF4J (v1.6.6) e Logback (v1.0.6). Stdout e stderr sono stati reindirizzati al dispositivo null.

Tuttavia, attento ora, risulta che il JUL sta spendendo la maggior parte del suo tempo dentro getSourceClassName() poiché JUL per impostazione predefinita stampa il nome della classe di origine nell'output, mentre Logback no. Quindi stiamo confrontando mele e arance. Devo ripetere il test e configurare le implementazioni di registrazione in un modo simile in modo che in realtà producano le stesse cose. Tuttavia, sospetto che SLF4J + Logback sarà ancora in cima ma lontano dai numeri iniziali come indicato sopra. Rimanete sintonizzati.

Btw: Il test è stato la prima volta che ho effettivamente lavorato con SLF4J o Logback. Un'esperienza piacevole. JUL è sicuramente molto meno accogliente quando si inizia.

Test delle prestazioni (parte 2)

(sezione aggiunta da nolan600 il 08-LUGLIO 2012)

A quanto pare, non importa per le prestazioni come si configura il modello in JUL, cioè se include o meno il nome sorgente o meno. Ho provato con un modello molto semplice:

java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"

e questo non ha cambiato affatto i tempi di cui sopra. Il mio profiler ha rivelato che il registratore ha dedicato molto tempo alle chiamate getSourceClassName() anche se questo non faceva parte del mio schema. Il modello non ha importanza.

Sto quindi concludendo sulla questione delle prestazioni che almeno per l'affermazione di log basata su template testata sembra essere all'incirca un fattore di 10 nella differenza di prestazioni reali tra JUL (lento) e SLF4J + Logback (veloce). Proprio come ha detto Ceki.

Posso anche vedere un'altra cosa, ovvero quella di SLF4J getLogger() la chiamata è molto più costosa della idem di JUL. (95 ms vs 0,3 ms se il mio profiler è accurato). Questo ha senso. SLF4J deve fare un po 'di tempo sull'associazione dell'implementazione di registrazione sottostante. Questo non mi spaventa. Queste chiamate dovrebbero essere piuttosto rare durante la vita di un'applicazione. La solidità dovrebbe essere nelle chiamate di registro effettive.

Conclusione finale

(sezione aggiunta da nolan600 il 08-LUGLIO 2012)

Grazie per tutte le tue risposte. Contrariamente a quanto pensavo inizialmente, ho deciso di utilizzare SLF4J per la mia API. Questo è basato su una serie di cose e il tuo contributo:

  1. Offre flessibilità per scegliere l'implementazione del log al momento dell'implementazione.

  2. Problemi con la mancanza di flessibilità della configurazione di JUL quando eseguita all'interno di un server delle applicazioni.

  3. SLF4J è sicuramente molto più veloce come dettagliato sopra in particolare se lo accoppi con Logback. Anche se si è trattato solo di un test approssimativo, ho motivo di ritenere che siano stati compiuti molti più sforzi per l'ottimizzazione su SLF4J + Logback che su JUL.

  4. Documentazione. La documentazione di SLF4J è semplicemente molto più completa e precisa.

  5. Flessibilità del modello Mentre eseguivo i test, ho deciso di fare in modo che JUL simulasse il pattern predefinito da Logback. Questo modello include il nome del thread. Si scopre che JUL non può farlo fuori dalla scatola. Ok, non mi sono perso fino ad ora, ma non penso che sia una cosa che dovrebbe mancare da un framework di log. Periodo!

  6. La maggior parte (o molti) progetti Java oggi usano Maven, quindi aggiungere una dipendenza non è una cosa particolarmente grande se la dipendenza è piuttosto stabile, cioè non cambia costantemente la sua API. Questo sembra essere vero per SLF4J. Anche il barattolo e gli amici SLF4J sono di piccole dimensioni.

Quindi la cosa strana che è successo è che mi sono davvero arrabbiata con JUL dopo aver lavorato un po 'con SLF4J. Mi dispiace ancora che debba essere così con JUL. LUGLIO è lungi dall'essere perfetto ma il tipo di lavoro lo fa. Semplicemente non abbastanza bene. Lo stesso si può dire Properties come esempio, ma non pensiamo all'astrazione, in modo che le persone possano inserire la propria libreria di configurazione e che cosa possiedi. Penso che la ragione sia questa Properties arriva appena sopra il bar mentre il contrario è vero per JUL di oggi ... e in passato è arrivato a zero perché non esisteva.


265
2017-07-06 09:07


origine


risposte:


disconoscimento: Sono il fondatore di log4j, SLF4J e progetti di logback.

Ci sono ragioni oggettive per preferire SLF4J. Per uno, garantisce all'utente finale la libertà di scegliere il framework di registrazione sottostante. Inoltre, gli utenti più esperti tendono a preferire logback che offre funzionalità oltre a log4jcon j.u.l che cade indietro. J.u.l potrebbe essere sufficiente per alcuni utenti, ma per molti altri non lo è. In poche parole, se la registrazione è importante per te, ti consigliamo di utilizzare SLF4J con il logback come implementazione sottostante. Se la registrazione non è importante, j.u.l va bene.

Tuttavia, come sviluppatore di oss, devi tenere conto delle preferenze dei tuoi utenti e non solo dei tuoi. Ne consegue che è necessario adottare SLF4J non perché tu sono convinto che SLF4J sia migliore di j.u.l, ma poiché la maggior parte degli sviluppatori Java attualmente (luglio 2012) preferisce SLF4J come API di registrazione. Se alla fine decidi di non preoccuparti dell'opinione pubblica, considera i seguenti fatti:

  1. quelli che preferiscono j.u.l lo fanno per convenienza perché j.u.l è in bundle con il JDK. A mia conoscenza non ci sono altri argomenti oggettivi a favore di j.u.l.
  2. la tua preferenza per j.u.l è proprio questo, una preferenza.

Quindi, tenere "i fatti concreti" al di sopra dell'opinione pubblica, anche se apparentemente coraggiosi, è un errore logico in questo caso.

Se non sei ancora convinto, JB Nizet rende un argomento aggiuntivo e potente:

Tranne che l'utente finale avrebbe potuto già fare questa personalizzazione per il suo   proprio codice o un'altra libreria che utilizza log4j o logback. j.u.l è   estendibile, ma che deve estendere logback, j.u.l, log4j e solo Dio   sa quale altro framework di logging perché usa quattro librerie   utilizzare quattro diversi quadri di registrazione è macchinoso. Usando SLF4J, tu   permettergli di configurare i quadri di registrazione che desidera, non quello   tu hai scelto. Ricorda che un tipico progetto usa miriadi di   biblioteche e non solo le tue.

Se per qualsiasi motivo odi l'API SLF4J e utilizzandolo, eliminerai il divertimento dal tuo lavoro, quindi con tutti i mezzi per j.u.l. Dopo tutto, ci sono mezzi per reindirizzare da j.u.l a SLF4J.

A proposito, la parametrizzazione di j.u.l è almeno 10 volte più lenta di quella di SLF4J che finisce per fare una differenza notevole.


159
2017-07-06 10:32



  1. java.util.logging è stato introdotto in Java 1.4. Esistevano già degli usi per la registrazione, ecco perché esistono molte altre API di registrazione. Quelle API sono state utilizzate pesantemente prima di Java 1.4 e quindi hanno avuto una grande quota di mercato che non è passata a 0 quando 1.4 è stata rilasciata.

  2. JUL non è partito così bene, molte delle cose che hai menzionato erano molto peggiori in 1.4 e sono migliorate solo in 1.5 (e credo anche in 6, ma non ne sono troppo sicuro).

  3. JUL non è adatto per più applicazioni con diverse configurazioni nella stessa JVM (si pensi a più applicazioni Web che non dovrebbero interagire). Tomcat ha bisogno di saltare attraverso alcuni cerchi per farlo funzionare (efficacemente ri-implementando JUL se ho capito che correttamente).

  4. Non è sempre possibile influenzare il framework di registrazione utilizzato dalle tue librerie. Pertanto, l'utilizzo di SLF4J (che in realtà è solo uno strato API molto sottile sopra le altre librerie) aiuta a mantenere un'immagine coerente dell'intero mondo di registrazione (in modo da poter decidere il framework di registrazione sottostante pur mantenendo la registrazione delle librerie nello stesso sistema).

  5. Le biblioteche non possono facilmente cambiare. Se una versione precedente di una libreria utilizzava logging-library-X, non può facilmente passare a logging-library-Y (ad esempio JUL), anche se quest'ultimo è chiaramente superio: qualsiasi utente di quella libreria avrebbe bisogno di imparare il nuovo framework di registrazione e (almeno) riconfigurare la loro registrazione. Questo è un grande no-no, specialmente quando non porta alcun guadagno apparente alla maggior parte delle persone.

Detto tutto quello che penso sia il JUL almeno una valida alternativa ad altri framework di registrazione in questi giorni.


25
2017-07-06 09:17



IMHO, il vantaggio principale nell'uso di una facciata di logging come slf4j è che si consente all'utente finale della libreria di scegliere quale implementazione di registrazione concreta desidera, piuttosto che imporre la propria scelta all'utente finale.

Forse ha investito tempo e denaro in Log4j o LogBack (formattatori speciali, appendici, ecc.) E preferisce continuare a utilizzare Log4j o LogBack, piuttosto che configurare jul. Nessun problema: lo consente slf4j. È una scelta saggia usare Log4j su luglio? Forse sì forse no. Ma non ti interessa. Lascia che l'utente finale scelga ciò che preferisce.


23
2017-07-06 09:19



Ho iniziato, come sospettavo, usando il JUL perché era il modo più semplice per iniziare immediatamente. Nel corso degli anni, tuttavia, sono giunto a desiderare di aver trascorso un po 'più di tempo a scegliere.

Il mio problema principale ora è che abbiamo una notevole quantità di codice "libreria" che viene utilizzato in molte applicazioni e tutti usano il JUL. Ogni volta che uso questi strumenti in un'app di tipo di servizio web, la registrazione scompare o va da qualche parte imprevedibile o strana.

La nostra soluzione consisteva nell'aggiungere una facciata al codice della libreria, il che significava che le chiamate al registro della libreria non erano cambiate ma venivano reindirizzate dinamicamente a qualsiasi meccanismo di registrazione disponibile. Quando sono inclusi in uno strumento POJO, vengono indirizzati a JUL, ma quando vengono distribuiti come app web vengono reindirizzati a LogBack.

Il nostro rammarico, ovviamente, è che il codice della libreria non utilizza la registrazione parametrizzata, ma ora può essere riadattato come e quando necessario.

Abbiamo usato slf4j per costruire la facciata.


4
2017-07-06 13:42



Ho eseguito jul su slf4j-1.7.21 su logback-1.1.7, output su SSD, Java 1.8, Win64

jul correva 48449 ms, logback 27185 ms per un ciclo 1M.

Ancora, un po 'più veloce e un po' più bello API non vale la pena di 3 librerie e 800K per me.

package log;

import java.util.logging.Level;
import java.util.logging.Logger;

public class LogJUL
{
    final static Logger logger = Logger.getLogger(LogJUL.class.getSimpleName());

    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            Object[] o = { lc };

            logger.log(Level.INFO,"Epoch time {0}", o);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }
}

e

package log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogSLF
{
    static Logger logger = LoggerFactory.getLogger(LogSLF.class);


    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            logger.info("Epoch time {}", lc);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }

}

1
2017-11-25 13:24