it-swarm-vi.tech

Trình ghi nhật ký Java tự động xác định tên lớp của trình gọi

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

Phương thức này sẽ trả về một logger biết lớp mà nó đang đăng nhập . Có ý tưởng nào chống lại nó không?

Nhiều năm sau: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.Java

33
yanchenko

Tôi đoán nó bổ sung rất nhiều chi phí cho mỗi lớp. Mỗi lớp phải được 'nhìn lên'. Bạn tạo các đối tượng Có thể ném mới để làm điều đó ... Những vật thể ném này không miễn phí.

15
Daan

Tạo một dấu vết ngăn xếp là một hoạt động tương đối chậm. Người gọi của bạn đã biết nó thuộc lớp nào và phương thức nào, vì vậy nỗ lực bị lãng phí. Khía cạnh này của giải pháp của bạn là không hiệu quả.

Ngay cả khi bạn sử dụng thông tin lớp tĩnh, bạn không nên tìm lại Logger cho mỗi tin nhắn. Từ tác giả của Log4j, Ceki Gülcü: 

Lỗi phổ biến nhất trong các lớp trình bao bọc là việc gọi phương thức Logger.getLogger trên mỗi yêu cầu nhật ký. Điều này được đảm bảo sẽ tàn phá hiệu suất của ứng dụng của bạn. Có thật không!!! 

Đây là thành ngữ thông thường, hiệu quả để nhận Logger là trong quá trình khởi tạo lớp:

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

Lưu ý rằng điều này cung cấp cho bạn một Logger riêng cho từng loại trong hệ thống phân cấp. Nếu bạn đưa ra một phương thức gọi getClass() trên một ví dụ, bạn sẽ thấy các thông báo được ghi lại bởi một loại cơ sở hiển thị dưới bộ ghi nhật ký của kiểu con. Có thể điều này là mong muốn trong một số trường hợp, nhưng tôi thấy nó khó hiểu (và tôi có xu hướng ủng hộ sáng tác hơn là thừa kế). 

Rõ ràng, sử dụng loại động thông qua getClass() sẽ yêu cầu bạn phải có được trình ghi nhật ký ít nhất một lần cho mỗi phiên bản, thay vì một lần cho mỗi lớp như thành ngữ được đề xuất sử dụng thông tin loại tĩnh.

22
erickson

Phương thức xử lý lớp (kể từ Java 7) bao gồm Tra cứu lớp, từ ngữ cảnh tĩnh, có thể tìm và trả về tên của lớp hiện tại. Hãy xem xét ví dụ sau:

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

Khi chạy nó tạo ra:

Main

Đối với một logger, bạn có thể sử dụng:

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

Chúng tôi thực sự có một cái gì đó khá giống nhau trong một lớp LogUtils. Vâng, đó là loại icky, nhưng những lợi thế là đáng giá như tôi quan tâm. Mặc dù vậy, chúng tôi muốn đảm bảo rằng chúng tôi không có bất kỳ chi phí nào từ nó được gọi liên tục, vì vậy chúng tôi (hơi khó tính) đảm bảo rằng nó CHỈ có thể được gọi từ ngữ cảnh khởi tạo tĩnh, la:

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

Nó sẽ thất bại nếu nó được gọi từ một phương thức bình thường hoặc từ một trình khởi tạo cá thể (tức là nếu 'tĩnh' bị bỏ lại ở trên) để giảm rủi ro về chi phí hoạt động. Phương pháp là:

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

Bất cứ ai hỏi lợi thế này có lợi thế gì 

= Logger.getLogger(MyClass.class);

có lẽ chưa bao giờ phải đối phó với ai đó sao chép và dán dòng đó từ nơi khác và quên thay đổi tên lớp, khiến bạn phải xử lý một lớp gửi tất cả nội dung của nó cho một logger khác.

17
Cowan

Giả sử bạn đang giữ các ref tĩnh cho logger, đây là một singleton tĩnh độc lập:

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

Cách sử dụng rất hay và sạch sẽ:

Logger logger = LoggerUtils.getLogger();
8
EGB

Đối với mỗi lớp mà bạn sử dụng cái này, dù sao bạn cũng sẽ phải tìm kiếm Logger, vì vậy bạn cũng có thể chỉ sử dụng một Logger tĩnh trong các lớp đó.

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

Sau đó, bạn chỉ cần tham khảo logger đó khi bạn cần làm thông điệp tường trình của bạn. Phương pháp của bạn thực hiện điều tương tự như Log4J Logger tĩnh đã làm vậy tại sao lại phát minh lại bánh xe?

4
18Rabbit

Sau đó, điều tốt nhất là kết hợp của hai. 

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

Và sau đó trong mỗi lớp học:

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

trong mã :

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

Bạn nhận được logger tĩnh mà bạn chỉ có thể sao chép và dán vào mỗi lớp và không có chi phí ...

Alaa

3
Alaa Murad

Từ việc đọc qua tất cả các phản hồi khác trên trang web này, tôi đã tạo ra những điều sau đây để sử dụng với 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);
}

}

Bây giờ trong mã của bạn, tất cả những gì bạn cần là

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

hoặc là

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

Nếu bạn cần tiếp xúc nhiều hơn với các phương thức log4j, chỉ cần ủy quyền chúng từ lớp Logger được liệt kê ở trên.

3
Ed Sarrazin

Tôi thích tạo một Logger (tĩnh) cho mỗi lớp (với tên lớp rõ ràng). Tôi hơn là sử dụng logger như là.

2
Philip Helger

Tất nhiên bạn có thể chỉ cần sử dụng Log4J với bố cục mẫu thích hợp:

Ví dụ: đối với tên lớp "org.Apache.xyz.SomeClass", mẫu% C {1} sẽ xuất ra "someClass". 

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

2
Ian

Bạn không cần phải tạo một đối tượng Có thể ném mới. Bạn chỉ có thể gọi Thread.currentThread().getStackTrace()[1]

2
ykaganovich

TRƯỜNG OLD đơn giản và tầm thường :

Chỉ cần tạo lớp của riêng bạn và chuyển tên lớp, tên phương thức + nhận xét (nếu lớp/phương thức thay đổi, chúng sẽ được tái cấu trúc tự động Shift + F6)

public class MyLogs {    
    public static void LOG(String theClass, String theMethod, String theComment) {
        Log.d("MY_TAG", "class: " + theClass + " meth : " + theMethod + " comm : " + theComment);
    }
}

và chỉ sử dụng nó ở bất cứ đâu trong ứng dụng (không yêu cầu ngữ cảnh, không khởi tạo, không thêm lib và không tra cứu) - có thể được sử dụng cho bất kỳ ngôn ngữ lập trình nào!

MyLogs.LOG("MainActivity", "onCreate", "Hello world");

điều này sẽ in trong bảng điều khiển của bạn:

Lớp MYiah: MainActivity meth: onCreate comm: Xin chào thế giới

0
Choletski

Cơ chế này đặt rất nhiều nỗ lực vào thời gian chạy.

Nếu bạn sử dụng Eclipse làm IDE, hãy cân nhắc sử dụng Log4e . Plugin tiện dụng này sẽ tạo ra các khai báo logger cho bạn bằng cách sử dụng khung ghi nhật ký yêu thích của bạn. Một phần nỗ lực nhiều hơn vào thời gian mã hóa, nhưng much làm việc ít hơn trong thời gian chạy.

0
Bill Michell

Một cách khác là sử dụng (một trong) các chú thích nhật ký lombok: https://projectlombok.org/features/Log.html

Nó tạo ra câu lệnh log tương ứng với lớp hiện tại.

0
user2189998

Một cách hay để làm điều này từ Java 7 trở đi:

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

Trình ghi nhật ký có thể là static và điều đó tốt . Ở đây, nó sử dụng API SLF4J

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

Nhưng trong nguyên tắc có thể được sử dụng với bất kỳ khung đăng nhập. Nếu trình ghi nhật ký cần một đối số chuỗi, hãy thêm toString()

0
James Mudd

Tôi chỉ có dòng sau khi bắt đầu hầu hết các lớp học của tôi.

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

vâng, có một số chi phí ngay lần đầu tiên một đối tượng của lớp đó được tạo ra, nhưng tôi làm việc chủ yếu trong các ứng dụng web, vì vậy việc thêm micro giây vào khởi động 20 giây thực sự không thành vấn đề.

0
muttonUp

Trừ khi bạn thực sự cần Logger của bạn ở trạng thái tĩnh, bạn có thể sử dụng

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

API ghi nhật ký Google Flogger hỗ trợ ví dụ này.

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

Xem https://github.com/google/flogger để biết thêm chi tiết.

0
James Mudd

Hãy xem Logger class từ jcabi-log . Nó thực hiện chính xác những gì bạn đang tìm kiếm, cung cấp một tập hợp các phương thức tĩnh. Bạn không cần phải nhúng loggers vào các lớp nữa:

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

Logger gửi tất cả nhật ký tới SLF4J, bạn có thể chuyển hướng đến bất kỳ cơ sở ghi nhật ký nào khác, trong thời gian chạy.

0
yegor256

Tại sao không?

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

Và sau đó khi bạn cần một logger cho một lớp:

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

Vui lòng xem triển khai getLogger () tĩnh của tôi (sử dụng phép thuật "Sun. *" tương tự trên JDK 7 làm doit Java Logger mặc định)

  • lưu ý các phương pháp ghi nhật ký tĩnh (có nhập tĩnh) mà không có thuộc tính nhật ký xấu ...

    nhập tĩnh my.pakg.Logger. *;

Và tốc độ của chúng tương đương với việc triển khai Java nguyên gốc (được kiểm tra với 1 triệu dấu vết nhật ký)

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