Pertanyaan Mengatur tingkat log pesan saat runtime di slf4j


Saat menggunakan log4j, the Logger.log(Priority p, Object message) tersedia dan dapat digunakan untuk mencatat pesan pada tingkat log yang ditentukan saat runtime. Kami menggunakan fakta ini dan tip ini untuk mengalihkan stderr ke logger pada tingkat log tertentu.

slf4j tidak memiliki generik log() metode yang bisa saya temukan. Apakah itu berarti tidak ada cara untuk menerapkan hal di atas?


76
2018-04-12 11:39


asal


Jawaban:


Tidak ada cara untuk melakukan ini slf4j.

Saya membayangkan bahwa alasan bahwa fungsi ini hilang adalah bahwa tidak mungkin untuk membangun sebuah Level ketik untuk slf4j yang dapat dipetakan secara efisien ke Level (atau yang setara) jenis yang digunakan dalam semua implementasi logging yang mungkin di balik fasad. Atau, para desainer memutuskan itu use case Anda terlalu tidak biasa untuk membenarkan biaya overhead untuk mendukungnya.

Tentang @ ripper234's use-case (pengujian unit), saya pikir solusi pragmatis adalah memodifikasi tes unit (s) ke pengetahuan hard-wire tentang apa sistem pencatatan di balik fasad slf4j ... ketika menjalankan tes unit.


36
2018-04-12 12:36



Richard Fearn memiliki ide yang tepat, jadi saya menulis kelas penuh berdasarkan kode kerangkanya. Mudah-mudahan cukup singkat untuk memposting di sini. Salin & tempel untuk kesenangan. Saya mungkin harus menambahkan mantra sihir juga: "Kode ini dirilis ke domain publik"

import org.slf4j.Logger;

public class LogLevel {

    /**
     * Allowed levels, as an enum. Import using "import [package].LogLevel.Level"
     * Every logging implementation has something like this except SLF4J.
     */

    public static enum Level {
        TRACE, DEBUG, INFO, WARN, ERROR
    }

    /**
     * This class cannot be instantiated, why would you want to?
     */

    private LogLevel() {
        // Unreachable
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "txt" is null,
     * behaviour depends on the SLF4J implementation.
     */

    public static void log(Logger logger, Level level, String txt) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt);
                break;
            case DEBUG:
                logger.debug(txt);
                break;
            case INFO:
                logger.info(txt);
                break;
            case WARN:
                logger.warn(txt);
                break;
            case ERROR:
                logger.error(txt);
                break;
            }
        }
    }

    /**
     * Log at the specified level. If the "logger" is null, nothing is logged.
     * If the "level" is null, nothing is logged. If the "format" or the "argArray"
     * are null, behaviour depends on the SLF4J-backing implementation.
     */

    public static void log(Logger logger, Level level, String format, Object[] argArray) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(format, argArray);
                break;
            case DEBUG:
                logger.debug(format, argArray);
                break;
            case INFO:
                logger.info(format, argArray);
                break;
            case WARN:
                logger.warn(format, argArray);
                break;
            case ERROR:
                logger.error(format, argArray);
                break;
            }
        }
    }

    /**
     * Log at the specified level, with a Throwable on top. If the "logger" is null,
     * nothing is logged. If the "level" is null, nothing is logged. If the "format" or
     * the "argArray" or the "throwable" are null, behaviour depends on the SLF4J-backing
     * implementation.
     */

    public static void log(Logger logger, Level level, String txt, Throwable throwable) {
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                logger.trace(txt, throwable);
                break;
            case DEBUG:
                logger.debug(txt, throwable);
                break;
            case INFO:
                logger.info(txt, throwable);
                break;
            case WARN:
                logger.warn(txt, throwable);
                break;
            case ERROR:
                logger.error(txt, throwable);
                break;
            }
        }
    }

    /**
     * Check whether a SLF4J logger is enabled for a certain loglevel. 
     * If the "logger" or the "level" is null, false is returned.
     */

    public static boolean isEnabledFor(Logger logger, Level level) {
        boolean res = false;
        if (logger != null && level != null) {
            switch (level) {
            case TRACE:
                res = logger.isTraceEnabled();
                break;
            case DEBUG:
                res = logger.isDebugEnabled();
                break;
            case INFO:
                res = logger.isInfoEnabled();
                break;
            case WARN:
                res = logger.isWarnEnabled();
                break;
            case ERROR:
                res = logger.isErrorEnabled();
                break;
            }
        }
        return res;
    }
}

21
2017-10-19 23:41



Anda dapat menerapkan ini menggunakan Java 8 lambdas.

import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

public class LevelLogger {
    private static final Logger LOGGER = LoggerFactory.getLogger(LevelLogger.class);
    private static final Map<Level, LoggingFunction> map;

    static {
        map = new HashMap<>();
        map.put(Level.TRACE, (o) -> LOGGER.trace(o));
        map.put(Level.DEBUG, (o) -> LOGGER.debug(o));
        map.put(Level.INFO, (o) -> LOGGER.info(o));
        map.put(Level.WARN, (o) -> LOGGER.warn(o));
        map.put(Level.ERROR, (o) -> LOGGER.error(o));
    }

    public static void log(Level level, String s) {
        map.get(level).log(s);
    }

    @FunctionalInterface
    private interface LoggingFunction {
        public void log(String arg);
    }
}

9
2018-03-10 17:01



Coba beralih ke Logback dan gunakan

ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(ch.qos.logback.classic.Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.toLevel("info"));

Saya percaya ini akan menjadi satu-satunya panggilan untuk Logback dan sisa kode Anda akan tetap tidak berubah. Logback menggunakan SLF4J dan migrasi tidak akan menimbulkan rasa sakit, hanya file konfigurasi xml yang harus diubah.

Ingatlah untuk mengatur tingkat log kembali setelah Anda selesai.


9
2018-01-18 13:20



Ini bisa dilakukan dengan enum dan metode pembantu:

enum LogLevel {
    TRACE,
    DEBUG,
    INFO,
    WARN,
    ERROR,
}

public static void log(Logger logger, LogLevel level, String format, Object[] argArray) {
    switch (level) {
        case TRACE:
            logger.trace(format, argArray);
            break;
        case DEBUG:
            logger.debug(format, argArray);
            break;
        case INFO:
            logger.info(format, argArray);
            break;
        case WARN:
            logger.warn(format, argArray);
            break;
        case ERROR:
            logger.error(format, argArray);
            break;
    }
}

// example usage:
private static final Logger logger = ...
final LogLevel level = ...
log(logger, level, "Something bad happened", ...);

Anda dapat menambahkan varian lain log, katakanlah jika Anda menginginkan persamaan generik dari SLF4J's 1-parameter atau 2-parameter warn/error/ etc. metode.


6
2018-05-25 21:46



Siapa pun yang menginginkan solusi lengkap yang kompatibel dengan SLF4J untuk masalah ini mungkin ingin memeriksanya Ekstensi Lidalia SLF4J - ada di Maven Central.


4
2018-03-28 13:14



Saya baru saja menemui kebutuhan yang sama. Dalam kasus saya, slf4j dikonfigurasi dengan adaptor logging java (yang jdk14 satu). Menggunakan potongan kode berikut ini saya telah berhasil mengubah tingkat debug saat runtime:

Logger logger = LoggerFactory.getLogger("testing");
java.util.logging.Logger julLogger = java.util.logging.Logger.getLogger("testing");
julLogger.setLevel(java.util.logging.Level.FINE);
logger.debug("hello world");

1
2018-06-11 03:33



Berdasarkan jawaban massimo virgilio, saya juga berhasil melakukannya dengan slf4j-log4j menggunakan introspeksi. HTH.

Logger LOG = LoggerFactory.getLogger(MyOwnClass.class);

org.apache.logging.slf4j.Log4jLogger LOGGER = (org.apache.logging.slf4j.Log4jLogger) LOG;

try {
    Class loggerIntrospected = LOGGER.getClass();
    Field fields[] = loggerIntrospected.getDeclaredFields();
    for (int i = 0; i < fields.length; i++) {
        String fieldName = fields[i].getName();
        if (fieldName.equals("logger")) {
            fields[i].setAccessible(true);
            org.apache.logging.log4j.core.Logger loggerImpl = (org.apache.logging.log4j.core.Logger) fields[i].get(LOGGER);
            loggerImpl.setLevel(Level.DEBUG);
        }
    }
} catch (Exception e) {
    System.out.println("ERROR :" + e.getMessage());
}

0
2018-02-24 12:39