Java Language Registrazione di messaggi complessi (in modo efficiente)

Esempio

Diamo un'occhiata ad un campione di registrazione che puoi vedere in molti programmi:

public class LoggingComplex {
    
    private static final Logger logger = 
        Logger.getLogger(LoggingComplex.class.getName());

    private int total = 50, orders = 20;
    private String username = "Bob";

    public void takeOrder() {
        // (...) making some stuff
        logger.fine(String.format("User %s ordered %d things (%d in total)", 
                                  username, orders, total));
        // (...) some other stuff
    }

    // some other methods and calculations
}

L'esempio sopra sembra perfettamente a posto, ma molti programmatori dimenticano che Java VM è uno stack machine. Ciò significa che tutti i parametri del metodo vengono calcolati prima dell'esecuzione del metodo.

Questo fatto è cruciale per la registrazione in Java, specialmente per la registrazione di qualcosa in livelli bassi come FINE , FINER , FINEST che sono disabilitati di default. Diamo un'occhiata al bytecode Java per il metodo takeOrder() .

Il risultato per javap -c LoggingComplex.class è qualcosa del genere:

public void takeOrder();
    Code:
       0: getstatic     #27 // Field logger:Ljava/util/logging/Logger;
       3: ldc           #45 // String User %s ordered %d things (%d in total)
       5: iconst_3
       6: anewarray     #3  // class java/lang/Object
       9: dup
      10: iconst_0
      11: aload_0
      12: getfield      #40 // Field username:Ljava/lang/String;
      15: aastore
      16: dup
      17: iconst_1
      18: aload_0
      19: getfield      #36 // Field orders:I
      22: invokestatic  #47 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      25: aastore
      26: dup
      27: iconst_2
      28: aload_0
      29: getfield      #34 // Field total:I
      32: invokestatic  #47 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      35: aastore
      36: invokestatic  #53 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
      39: invokevirtual #59 // Method java/util/logging/Logger.fine:(Ljava/lang/String;)V
      42: return

La riga 39 esegue la registrazione effettiva. Tutto il lavoro precedente (caricamento di variabili, creazione di nuovi oggetti, concatenazione di stringhe nel metodo di format ) può essere inutile se il livello di registrazione è impostato su un valore superiore a FINE (e di default è). Tale registrazione può essere molto inefficiente, consumando risorse di memoria e processore non necessarie.

Ecco perché dovresti chiedere se il livello che vuoi utilizzare è abilitato.

La strada giusta dovrebbe essere:

public void takeOrder() {
    // making some stuff
    if (logger.isLoggable(Level.FINE)) {
        // no action taken when there's no need for it
        logger.fine(String.format("User %s ordered %d things (%d in total)",
                                  username, orders, total));
    }
    // some other stuff
}

Da quando Java 8:

La classe Logger ha metodi aggiuntivi che prendono come parametro un parametro Supplier<String> , che può essere semplicemente fornito da un lambda:

public void takeOrder() {
    // making some stuff
    logger.fine(() -> String.format("User %s ordered %d things (%d in total)",
            username, orders, total));
    // some other stuff
}

Il metodo get() fornitori - in questo caso il lambda - viene chiamato solo quando il livello corrispondente è abilitato e quindi la costruzione if non è più necessaria.