سؤال

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

ستعيد هذه الطريقة مُسجلًا يعرف الفصل الذي يقوم بالتسجيل له.أي أفكار ضد ذلك؟

بعد سنوات عديدة: https://github.com/yanchenko/droidparts/blob/master/droidparts/src/org/droidparts/util/L.java

هل كانت مفيدة؟

المحلول

أعتقد أنه يضيف الكثير من النفقات العامة لكل فصل.يجب "البحث عن" كل فئة.يمكنك إنشاء كائنات Throwable جديدة للقيام بذلك...هذه الأشياء القابلة للرمي لا تأتي مجانًا.

نصائح أخرى

يعد إنشاء تتبع المكدس عملية بطيئة نسبيًا.يعرف المتصل بك بالفعل الفئة والطريقة التي ينتمي إليها، وبالتالي يضيع الجهد المبذول.هذا الجانب من الحل الخاص بك غير فعال.

حتى إذا كنت تستخدم معلومات فئة ثابتة، يجب ألا تقوم بإحضار المسجل مرة أخرى لكل رسالة. من المؤلف من Log4j، سيكي جولكو:

الخطأ الأكثر شيوعًا في فئات المجمّع هو استدعاء الأسلوب Logger.getLogger في كل طلب سجل.ويضمن هذا أن يعيث فسادًا في أداء تطبيقك.حقًا!!!

هذا هو المصطلح التقليدي والفعال للحصول على المسجل أثناء تهيئة الفصل:

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

لاحظ أن هذا يمنحك مسجلاً منفصلاً لكل نوع في التسلسل الهرمي.إذا توصلت إلى طريقة تستدعي getClass() على سبيل المثال، سترى الرسائل المسجلة بواسطة نوع أساسي تظهر ضمن مسجل النوع الفرعي.ربما يكون هذا أمرًا مرغوبًا فيه في بعض الحالات، لكنني أجده مربكًا (وأنا أميل إلى تفضيل التركيب على الميراث على أي حال).

من الواضح أن استخدام النوع الديناميكي عبر getClass() سوف يتطلب منك الحصول على المسجل مرة واحدة على الأقل لكل مثيل، بدلاً من مرة واحدة لكل فئة مثل المصطلح الموصى به باستخدام معلومات النوع الثابت.

ال MethodHandles تتضمن الفئة (اعتبارًا من Java 7) أ ابحث عن فئة يمكنها، من سياق ثابت، العثور على اسم الفئة الحالية وإرجاعه.خذ بعين الاعتبار المثال التالي:

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

عند تشغيل هذا ينتج:

Main

بالنسبة للمسجل، يمكنك استخدام:

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

لدينا في الواقع شيء مشابه تمامًا في فئة LogUtils.نعم، إنه أمر رديء نوعًا ما، لكن المزايا تستحق العناء بالنسبة لي.أردنا التأكد من أنه لم يكن لدينا أي حمل من استدعائه بشكل متكرر، لذلك يضمن لنا (بشكل متسلل إلى حد ما) أنه لا يمكن استدعاؤه إلا من سياق مُهيئ ثابت، على سبيل المثال:

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

سوف يفشل إذا تم استدعاؤه بطريقة عادية، أو من مُهيئ المثيل (على سبيل المثال.إذا تم ترك "الثابت" أعلاه) لتقليل مخاطر الحمل الزائد للأداء.الطريقة هي:

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

ومن يسأل ما فائدة هذا الأمر؟

= Logger.getLogger(MyClass.class);

ربما لم تضطر مطلقًا إلى التعامل مع شخص يقوم بنسخ هذا السطر ولصقه من مكان آخر وينسى تغيير اسم الفصل، مما يتركك تتعامل مع فصل يرسل كل عناصره إلى مسجل آخر.

بافتراض أنك تحتفظ بمراجع ثابتة للمسجلين، فإليك مفردة ثابتة مستقلة:

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

الاستخدام جميل ونظيف:

Logger logger = LoggerUtils.getLogger();

بالنسبة لكل فئة تستخدم هذا معها، سيتعين عليك البحث عن المسجل على أي حال، لذا يمكنك أيضًا استخدام مسجل ثابت في تلك الفئات.

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

ثم تقوم فقط بالرجوع إلى هذا المسجل عندما تحتاج إلى القيام برسائل السجل الخاصة بك.طريقتك تفعل نفس الشيء الذي يفعله Log4J Logger الثابت بالفعل، فلماذا تعيد اختراع العجلة؟

ثم أفضل شيء هو مزيج من اثنين.

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

ثم في كل فصل:

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

في الكود :

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

يمكنك الحصول على مسجل ثابت يمكنك فقط نسخه ولصقه في كل فصل دراسي وبدون أي تكاليف إضافية ...

الاء

من خلال قراءة جميع التعليقات الأخرى على هذا الموقع، قمت بإنشاء ما يلي للاستخدام مع 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);
}

}

الآن في التعليمات البرمجية الخاصة بك كل ما تحتاجه هو

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

أو

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

إذا كنت بحاجة إلى مزيد من التعرض لأساليب log4j، فما عليك سوى تفويضها من فئة المسجل المذكورة أعلاه.

يمكنك بالطبع استخدام Log4J مع تخطيط النمط المناسب:

على سبيل المثال، بالنسبة لاسم الفئة "org.apache.xyz.SomeClass"، فإن النمط %C{1} سيخرج "SomeClass".

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

أفضل إنشاء مسجل (ثابت) لكل فئة (مع اسم الفئة الصريح).أنا من استخدام المسجل كما هو.

لا تحتاج إلى إنشاء كائن Throwable جديد.يمكنك فقط الاتصالThread.currentThread().getStackTrace()[1]

لدي فقط السطر التالي في بداية معظم فصولي.

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

نعم هناك بعض النفقات العامة في المرة الأولى التي يتم فيها إنشاء كائن من هذه الفئة، لكنني أعمل في الغالب في تطبيقات الويب، لذا فإن إضافة ميكروثانية إلى بدء تشغيل مدته 20 ثانية لا يمثل مشكلة حقًا.

تدعم واجهة برمجة تطبيقات تسجيل Google Flogger هذا على سبيل المثال.

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

يرى https://github.com/google/flogger لمزيد من التفاصيل.

ولم لا؟

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

وبعد ذلك عندما تحتاج إلى مسجل لفصل دراسي:

getLogger(this).debug("Some log message")

تبذل هذه الآلية الكثير من الجهد الإضافي في وقت التشغيل.

إذا كنت تستخدم Eclipse باعتباره IDE الخاص بك، ففكر في استخدامه Log4e.سيقوم هذا البرنامج المساعد المفيد بإنشاء إعلانات المسجل لك باستخدام إطار التسجيل المفضل لديك.بذل المزيد من الجهد في وقت البرمجة، ولكن كثيراً عمل أقل في وقت التشغيل.

إلا انت حقًا تحتاج إلى أن يكون المسجل الخاص بك ثابتًا، يمكنك استخدامه

final Logger logger = LoggerFactory.getLogger(getClass());

يرجى الاطلاع على تطبيق getLogger() الثابت الخاص بي (استخدم نفس السحر "sun.*" على JDK 7 باعتباره java Logger doit الافتراضي)

  • لاحظ طرق التسجيل الثابتة (مع الاستيراد الثابت) بدون خاصية السجل القبيحة ...

    import static my.pakg.Logger.*;

وسرعتها تعادل تنفيذ Java الأصلي (تم التحقق منها باستخدام مليون تتبع للسجل)

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

}

نلقي نظرة على Logger فئة من jcabi-log.إنه يفعل بالضبط ما تبحث عنه، حيث يوفر مجموعة من الأساليب الثابتة.لم تعد بحاجة إلى تضمين أدوات تسجيل الدخول في الفصول الدراسية بعد الآن:

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

Logger يرسل كافة السجلات إلى SLF4J، والتي يمكنك إعادة توجيهها إلى أي منشأة تسجيل أخرى، في وقت التشغيل.

البديل الجيد هو استخدام (أحد) التعليقات التوضيحية لسجلات lombok :https://projectlombok.org/features/Log.html

يقوم بإنشاء بيان السجل المقابل للفئة الحالية.

طريقة لطيفة للقيام بذلك بدءًا من Java 7 وما بعده:

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

المسجل يمكن أن يكون static وهذا جيد.هنا يستخدم SLF4J API

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

ولكن من حيث المبدأ يمكن استخدامها مع أي إطار عمل التسجيل.إذا كان المسجل يحتاج إلى إضافة وسيطة سلسلة toString()

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top