it-swarm-id.com

Java logger yang secara otomatis menentukan nama kelas pemanggil

public static Logger getLogger() {
    final Throwable t = new Throwable();
    final StackTraceElement methodCaller = t.getStackTrace()[1];
    final Logger logger = Logger.getLogger(methodCaller.getClassName());
    logger.setLevel(ResourceManager.LOGLEVEL);
    return logger;
}

Metode ini akan mengembalikan logger yang tahu kelas untuk apa logikanya. Ada ide yang menentangnya?

Beberapa tahun kemudian: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Saya kira itu menambah banyak overhead untuk setiap kelas. Setiap kelas harus 'dilihat'. Anda membuat objek Throwable baru untuk melakukan itu ... Throwable ini tidak gratis.

15
Daan

Membuat jejak stack adalah operasi yang relatif lambat. Penelepon Anda sudah tahu kelas dan metode apa yang digunakan, sehingga usahanya sia-sia. Aspek solusi Anda ini tidak efisien.

Bahkan jika Anda menggunakan informasi kelas statis, Anda tidak harus mengambil Logger lagi untuk setiap pesan. Dari penulis dari Log4j, Ceki Gülcü: 

Kesalahan paling umum dalam kelas wrapper adalah permohonan metode Logger.getLogger pada setiap permintaan log. Ini dijamin akan mendatangkan malapetaka pada kinerja aplikasi Anda. Sangat!!! 

Ini adalah idiom konvensional dan efisien untuk mendapatkan Logger selama inisialisasi kelas:

private static final Logger log = Logger.getLogger(MyClass.class);

Perhatikan bahwa ini memberi Anda Logger terpisah untuk setiap jenis dalam hierarki. Jika Anda datang dengan metode yang memanggil getClass() pada contoh, Anda akan melihat pesan yang dicatat oleh tipe dasar muncul di bawah pencatat subtipe. Mungkin ini diinginkan dalam beberapa kasus, tetapi saya merasa membingungkan (dan saya cenderung lebih menyukai komposisi daripada warisan). 

Jelas, menggunakan tipe dinamis melalui getClass() akan mengharuskan Anda untuk mendapatkan logger setidaknya sekali per instance, daripada sekali per kelas seperti idiom yang disarankan menggunakan informasi tipe statis.

22
erickson

Kelas MethodHandles (pada Java 7) termasuk kelas Cari yang, dari konteks statis, dapat menemukan dan mengembalikan nama kelas saat ini. Perhatikan contoh berikut:

import Java.lang.invoke.MethodHandles;

public class Main {
  private static final Class clazz = MethodHandles.lookup().lookupClass();
  private static final String CLASSNAME = clazz.getSimpleName();

  public static void main( String args[] ) {
    System.out.println( CLASSNAME );
  }
}

Saat dijalankan ini menghasilkan:

Main

Untuk logger, Anda dapat menggunakan:

private static Logger LOGGER = 
  Logger.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
19
Neeraj

Kami sebenarnya memiliki sesuatu yang sangat mirip di kelas LogUtils. Ya, ini agak menjengkelkan, tetapi keuntungannya sepadan dengan yang saya ketahui. Kami ingin memastikan bahwa kami tidak memiliki overhead dari yang berulang kali dipanggil, jadi milik kami (agak peretasan) memastikan bahwa HANYA dapat dipanggil dari konteks penginisialisasi statis, ala:

private static final Logger LOG = LogUtils.loggerForThisClass();

Ini akan gagal jika itu dipanggil dari metode normal, atau dari instance initializer (mis. Jika 'statis' ditinggalkan di atas) untuk mengurangi risiko overhead kinerja. Metodenya adalah:

public static Logger loggerForThisClass() {
    // We use the third stack element; second is this method, first is .getStackTrace()
    StackTraceElement myCaller = Thread.currentThread().getStackTrace()[2];
    Assert.equal("<clinit>", myCaller.getMethodName());
    return Logger.getLogger(myCaller.getClassName());
}

Siapa pun yang menanyakan keuntungan apa yang didapat dari hal ini 

= Logger.getLogger(MyClass.class);

mungkin tidak pernah harus berurusan dengan seseorang yang menyalin dan menempelkan garis itu dari tempat lain dan lupa untuk mengubah nama kelas, membuat Anda berurusan dengan kelas yang mengirimkan semua barangnya ke logger lain.

17
Cowan

Dengan asumsi Anda menyimpan referensi statis untuk para penebang, inilah singleton statis mandiri:

public class LoggerUtils extends SecurityManager
{
    public static Logger getLogger()
    {
        String className = new LoggerUtils().getClassName();
        Logger logger = Logger.getLogger(className);
        return logger;
    }

    private String getClassName()
    {
        return getClassContext()[2].getName();
    }
}

Penggunaan Bagus dan bersih:

Logger logger = LoggerUtils.getLogger();
8
EGB

Untuk setiap kelas yang Anda gunakan dengan ini, Anda harus mencari Logger, jadi Anda mungkin juga menggunakan Logger statis di kelas-kelas tersebut.

private static final Logger logger = Logger.getLogger(MyClass.class.getName());

Kemudian Anda hanya referensi logger itu ketika Anda perlu melakukan pesan log Anda. Metode Anda melakukan hal yang sama dengan Log4J Logger statis sudah jadi mengapa menciptakan kembali roda?

4
18Rabbit

Maka yang terbaik adalah campuran dua. 

public class LoggerUtil {

    public static Level level=Level.ALL;

    public static Java.util.logging.Logger getLogger() {
        final Throwable t = new Throwable();
        final StackTraceElement methodCaller = t.getStackTrace()[1];
        final Java.util.logging.Logger logger = Java.util.logging.Logger.getLogger(methodCaller.getClassName());
        logger.setLevel(level);

        return logger;
    }
}

Dan kemudian di setiap kelas:

private static final Logger LOG = LoggerUtil.getLogger();

dalam kode :

LOG.fine("debug that !...");

Anda mendapatkan logger statis yang bisa Anda salin & tempel di setiap kelas dan tanpa biaya overhead ...

Alaa

3
Alaa Murad

Dari membaca semua umpan balik lain di situs ini, saya membuat yang berikut untuk digunakan dengan Log4j:

package com.edsdev.testapp.util;

import Java.util.concurrent.ConcurrentHashMap;

import org.Apache.log4j.Level;
import org.Apache.log4j.Priority;

public class Logger extends SecurityManager {

private static ConcurrentHashMap<String, org.Apache.log4j.Logger> loggerMap = new ConcurrentHashMap<String, org.Apache.log4j.Logger>();

public static org.Apache.log4j.Logger getLog() {
    String className = new Logger().getClassName();
    if (!loggerMap.containsKey(className)) {
        loggerMap.put(className, org.Apache.log4j.Logger.getLogger(className));
    }
    return loggerMap.get(className);
}
public String getClassName() {
    return getClassContext()[3].getName();
}
public static void trace(Object message) {
    getLog().trace(message);
}
public static void trace(Object message, Throwable t) {
    getLog().trace(message, t);
}
public static boolean isTraceEnabled() {
    return getLog().isTraceEnabled();
}
public static void debug(Object message) {
    getLog().debug(message);
}
public static void debug(Object message, Throwable t) {
    getLog().debug(message, t);
}
public static void error(Object message) {
    getLog().error(message);
}
public static void error(Object message, Throwable t) {
    getLog().error(message, t);
}
public static void fatal(Object message) {
    getLog().fatal(message);
}
public static void fatal(Object message, Throwable t) {
    getLog().fatal(message, t);
}
public static void info(Object message) {
    getLog().info(message);
}
public static void info(Object message, Throwable t) {
    getLog().info(message, t);
}
public static boolean isDebugEnabled() {
    return getLog().isDebugEnabled();
}
public static boolean isEnabledFor(Priority level) {
    return getLog().isEnabledFor(level);
}
public static boolean isInfoEnabled() {
    return getLog().isInfoEnabled();
}
public static void setLevel(Level level) {
    getLog().setLevel(level);
}
public static void warn(Object message) {
    getLog().warn(message);
}
public static void warn(Object message, Throwable t) {
    getLog().warn(message, t);
}

}

Sekarang dalam kode Anda semua yang Anda butuhkan adalah

Logger.debug("This is a test");

atau

Logger.error("Look what happened Ma!", e);

Jika Anda membutuhkan lebih banyak paparan metode log4j, cukup delegasikan mereka dari kelas Logger yang tercantum di atas.

3
Ed Sarrazin

Saya lebih suka membuat Logger (statis) untuk setiap kelas (dengan itu nama kelas eksplisit). Saya daripada menggunakan logger apa adanya.

2
Philip Helger

Anda tentu saja bisa menggunakan Log4J dengan tata letak pola yang sesuai:

Misalnya, untuk nama kelas "org.Apache.xyz.SomeClass", pola% C {1} akan menampilkan "SomeClass". 

http://logging.Apache.org/log4j/1.2/apidocs/org/Apache/log4j/PatternLayout.html

2
Ian

Anda tidak perlu membuat objek Throwable baru. Anda bisa memanggil Thread.currentThread().getStackTrace()[1]

2
ykaganovich

Mekanisme ini memberikan banyak upaya ekstra saat runtime.

Jika Anda menggunakan Eclipse sebagai IDE Anda, pertimbangkan untuk menggunakan Log4e . Plugin praktis ini akan menghasilkan deklarasi logger untuk Anda menggunakan kerangka logging favorit Anda. Sebagian kecil lebih banyak upaya pada waktu pengkodean, tetapi banyak kurang bekerja saat runtime.

0
Bill Michell

Alternatif yang baik adalah menggunakan (salah satu) anotasi log lombok: https://projectlombok.org/features/Log.html

Ini menghasilkan pernyataan log yang sesuai dengan kelas saat ini.

0
user2189998

Cara yang bagus untuk melakukan ini dari Java 7 dan seterusnya:

private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

Logger bisa static dan itu bagus. Di sini menggunakan API SLF4J

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

Namun pada prinsipnya dapat digunakan dengan kerangka kerja logging. Jika logger membutuhkan argumen string, tambahkan toString()

0
James Mudd

Saya hanya memiliki baris berikut di awal sebagian besar kelas saya.

  private static final Logger log = 
     LoggerFactory.getLogger(new Throwable().getStackTrace()[0].getClassName());

ya ada beberapa overhead pertama kali objek kelas itu dibuat, tapi saya bekerja sebagian besar di webapps, jadi menambahkan microseconds ke startup 20 detik tidak terlalu masalah.

0
muttonUp

Kecuali Anda benar-benar perlu Logger Anda menjadi statis, Anda dapat menggunakannya

final Logger logger = LoggerFactory.getLogger(getClass());
0
Asgeir S. Nilsen

API pencatatan Flogger Google mendukung hal ini, mis.

private static final FluentLogger logger = FluentLogger.forEnclosingClass();

Lihat https://github.com/google/flogger untuk rincian lebih lanjut.

0
James Mudd

Lihatlah Logger class from jcabi-log . Itu tidak persis apa yang Anda cari, menyediakan koleksi metode statis. Anda tidak perlu lagi menanamkan logger ke dalam kelas:

import com.jcabi.log.Logger;
class Foo {
  public void bar() {
    Logger.info(this, "doing something...");
  }
}

Logger mengirim semua log ke SLF4J, yang dapat Anda alihkan ke fasilitas logging lainnya, dalam runtime.

0
yegor256

Kenapa tidak?

public static Logger getLogger(Object o) {
  final Logger logger = Logger.getLogger(o.getClass());
  logger.setLevel(ResourceManager.LOGLEVEL);
  return logger;
}

Dan kemudian ketika Anda membutuhkan logger untuk sebuah kelas:

getLogger(this).debug("Some log message")
0
Mario Ortegón

Silakan lihat implementasi getLogger statis saya () (gunakan sihir "Sun. *" yang sama pada JDK 7 sebagai doit Java Logger default)

  • perhatikan metode logging statis (dengan impor statis) tanpa properti log jelek ...

    impor my.pakg.Logger statis. *;

Dan kecepatannya setara dengan implementasi Java asli (diperiksa dengan 1 juta jejak log)

package my.pkg;

import Java.text.MessageFormat;
import Java.util.Arrays;
import Java.util.IllegalFormatException;
import Java.util.logging.Level;
import Java.util.logging.LogRecord;

import Sun.misc.JavaLangAccess;
import Sun.misc.SharedSecrets;


public class Logger {
static final int CLASS_NAME = 0;
static final int METHOD_NAME = 1;

// Private method to infer the caller's class and method names
protected static String[] getClassName() {
    JavaLangAccess access = SharedSecrets.getJavaLangAccess();
    Throwable throwable = new Throwable();
    int depth = access.getStackTraceDepth(throwable);

    boolean lookingForLogger = true;
    for (int i = 0; i < depth; i++) {
        // Calling getStackTraceElement directly prevents the VM
        // from paying the cost of building the entire stack frame.
        StackTraceElement frame = access.getStackTraceElement(throwable, i);
        String cname = frame.getClassName();
        boolean isLoggerImpl = isLoggerImplFrame(cname);
        if (lookingForLogger) {
            // Skip all frames until we have found the first logger frame.
            if (isLoggerImpl) {
                lookingForLogger = false;
            }
        } else {
            if (!isLoggerImpl) {
                // skip reflection call
                if (!cname.startsWith("Java.lang.reflect.") && !cname.startsWith("Sun.reflect.")) {
                    // We've found the relevant frame.
                    return new String[] {cname, frame.getMethodName()};
                }
            }
        }
    }
    return new String[] {};
    // We haven't found a suitable frame, so just punt.  This is
    // OK as we are only committed to making a "best effort" here.
}

protected static String[] getClassNameJDK5() {
    // Get the stack trace.
    StackTraceElement stack[] = (new Throwable()).getStackTrace();
    // First, search back to a method in the Logger class.
    int ix = 0;
    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (isLoggerImplFrame(cname)) {
            break;
        }
        ix++;
    }
    // Now search for the first frame before the "Logger" class.
    while (ix < stack.length) {
        StackTraceElement frame = stack[ix];
        String cname = frame.getClassName();
        if (isLoggerImplFrame(cname)) {
            // We've found the relevant frame.
            return new String[] {cname, frame.getMethodName()};
        }
        ix++;
    }
    return new String[] {};
    // We haven't found a suitable frame, so just punt.  This is
    // OK as we are only committed to making a "best effort" here.
}


private static boolean isLoggerImplFrame(String cname) {
    // the log record could be created for a platform logger
    return (
            cname.equals("my.package.Logger") ||
            cname.equals("Java.util.logging.Logger") ||
            cname.startsWith("Java.util.logging.LoggingProxyImpl") ||
            cname.startsWith("Sun.util.logging."));
}

protected static Java.util.logging.Logger getLogger(String name) {
    return Java.util.logging.Logger.getLogger(name);
}

protected static boolean log(Level level, String msg, Object... args) {
    return log(level, null, msg, args);
}

protected static boolean log(Level level, Throwable thrown, String msg, Object... args) {
    String[] values = getClassName();
    Java.util.logging.Logger log = getLogger(values[CLASS_NAME]);
    if (level != null && log.isLoggable(level)) {
        if (msg != null) {
            log.log(getRecord(level, thrown, values[CLASS_NAME], values[METHOD_NAME], msg, args));
        }
        return true;
    }
    return false;
}

protected static LogRecord getRecord(Level level, Throwable thrown, String className, String methodName, String msg, Object... args) {
    LogRecord record = new LogRecord(level, format(msg, args));
    record.setSourceClassName(className);
    record.setSourceMethodName(methodName);
    if (thrown != null) {
        record.setThrown(thrown);
    }
    return record;
}

private static String format(String msg, Object... args) {
    if (msg == null || args == null || args.length == 0) {
        return msg;
    } else if (msg.indexOf('%') >= 0) {
        try {
            return String.format(msg, args);
        } catch (IllegalFormatException esc) {
            // none
        }
    } else if (msg.indexOf('{') >= 0) {
        try {
            return MessageFormat.format(msg, args);
        } catch (IllegalArgumentException exc) {
            // none
        }
    }
    if (args.length == 1) {
        Object param = args[0];
        if (param != null && param.getClass().isArray()) {
            return msg + Arrays.toString((Object[]) param);
        } else if (param instanceof Throwable){
            return msg;
        } else {
            return msg + param;
        }
    } else {
        return msg + Arrays.toString(args);
    }
}

public static void severe(String msg, Object... args) {
    log(Level.SEVERE, msg, args);
}

public static void warning(String msg, Object... args) {
    log(Level.WARNING, msg, args);
}

public static void info(Throwable thrown, String format, Object... args) {
    log(Level.INFO, thrown, format, args);
}

public static void warning(Throwable thrown, String format, Object... args) {
    log(Level.WARNING, thrown, format, args);
}

public static void warning(Throwable thrown) {
    log(Level.WARNING, thrown, thrown.getMessage());
}

public static void severe(Throwable thrown, String format, Object... args) {
    log(Level.SEVERE, thrown, format, args);
}

public static void severe(Throwable thrown) {
    log(Level.SEVERE, thrown, thrown.getMessage());
}

public static void info(String msg, Object... args) {
    log(Level.INFO, msg, args);
}

public static void fine(String msg, Object... args) {
    log(Level.FINE, msg, args);
}

public static void finer(String msg, Object... args) {
    log(Level.FINER, msg, args);
}

public static void finest(String msg, Object... args) {
    log(Level.FINEST, msg, args);
}

public static boolean isLoggableFinest() {
    return isLoggable(Level.FINEST);
}

public static boolean isLoggableFiner() {
    return isLoggable(Level.FINER);
}

public static boolean isLoggableFine() {
    return isLoggable(Level.FINE);
}

public static boolean isLoggableInfo() {
    return isLoggable(Level.INFO);
}

public static boolean isLoggableWarning() {
    return isLoggable(Level.WARNING);
}
public static boolean isLoggableSevere() {
    return isLoggable(Level.SEVERE);
}

private static boolean isLoggable(Level level) {
    return log(level, null);
}

}
0
joseaio