การตั้งค่าระดับบันทึกของข้อความที่รันไทม์ใน slf4j


102

เมื่อใช้ log4j Logger.log(Priority p, Object message)วิธีนี้จะพร้อมใช้งานและสามารถใช้เพื่อบันทึกข้อความที่ระดับบันทึกที่กำหนดที่รันไทม์ เรากำลังใช้ข้อเท็จจริงนี้และเคล็ดลับนี้เพื่อเปลี่ยนเส้นทาง stderr ไปยังคนตัดไม้ที่ระดับบันทึกเฉพาะ

slf4j ไม่มีlog()วิธีการทั่วไปที่ฉันสามารถหาได้ หมายความว่าไม่มีวิธีดำเนินการข้างต้นหรือไม่?


4
ดูเหมือนว่ามีการพูดคุยเกี่ยวกับการเพิ่มสิ่งนี้ใน slf4j 2.0 ในรายชื่ออีเมลสำหรับนักพัฒนา
Edward Dale

1
ดูที่ Marker นี่คือข้อมูลแบบกำหนดเองที่คุณสามารถส่งผ่านไปยังล็อกเชนได้
tuxSlayer

1
@tuxSlayer คุณช่วยอธิบายวิธีใช้ Marker ในกรณีนี้อย่างละเอียดได้ไหม
ตัวแปรที่น่าสังเวช

อาจไม่ใช่แนวคิดที่ดีที่สุดสำหรับ "การบันทึก" แต่คุณสามารถใช้เครื่องหมายหลายตัวสำหรับรายการบันทึก "ลำดับความสำคัญ" (สูง | ต่ำ | ปกติ, ข้อมูล | เตือน | ถึงแก่ชีวิต) และใช้การกรองในการบันทึกข้อมูลหรือโปรแกรมเสริมที่กำหนดเองเพื่อใช้เครื่องหมายและรายการบันทึกของไดรฟ์ แยกเป็นช่องทาง (ข้อมูลบันทึกอีเมลร้ายแรง ฯลฯ ) อย่างไรก็ตามวิธีที่ตรงกว่าคือการมีส่วนหน้าสำหรับสิ่งนี้ตามที่ระบุไว้ในคำตอบด้านล่าง
tuxSlayer

2
slf4j 2.0คุณลักษณะนี้ควรจะเป็นส่วนหนึ่งของ jira.qos.ch/browse/SLF4J-124ดูคำตอบของฉันสำหรับรายละเอียดและสำหรับ - วิธีslf4j 1.xแก้ปัญหาที่เป็นไปได้
slartidan

คำตอบ:


47

slf4jไม่มีทางที่จะทำเช่นนี้กับไม่มี

ฉันคิดว่าเหตุผลที่ฟังก์ชันนี้ขาดหายไปนั้นเป็นไปไม่ได้ที่จะสร้างLevelประเภทslf4jที่สามารถแมปอย่างมีประสิทธิภาพกับประเภทLevel(หรือเทียบเท่า) ที่ใช้ในการใช้งานการบันทึกทั้งหมดที่เป็นไปได้ที่อยู่ด้านหลังส่วนหน้า อีกวิธีหนึ่งคือนักออกแบบตัดสินใจว่ากรณีการใช้งานของคุณผิดปกติเกินไปที่จะปรับค่าใช้จ่ายในการรองรับ

เกี่ยวกับกรณีการใช้งานของ@ ripper234 (การทดสอบหน่วย) ฉันคิดว่าวิธีแก้ปัญหาในทางปฏิบัติคือการปรับเปลี่ยนการทดสอบหน่วยเป็นความรู้แบบฮาร์ดไวร์เกี่ยวกับระบบบันทึกที่อยู่ด้านหลังซุ้ม slf4j ... เมื่อเรียกใช้การทดสอบหน่วย


10
ไม่จำเป็นต้องทำแผนที่ มีห้าระดับที่กำหนดโดยนัยแล้วโดยวิธีการในorg.slf4j.Logger: debug, error, info, trace, warn
Edward Dale

1
และปัญหาถูกปิดเป็นไม่ถูกต้อง เท่าที่ฉันเข้าใจนี่เป็นการเลือกออกแบบโดยเจตนา
ripper234

9
@ ripper234 - ฉันไม่คิดว่าข้อบกพร่องของคุณแก้ไขปัญหาเดียวกันกับคำถามเดิมของ scompt.com คุณถามเกี่ยวกับการกำหนดค่าระดับของระบบบันทึกพื้นฐานผ่าน SLF4J API สิ่งที่ scompt.com ตามมาคือเมธอด 'บันทึก' ทั่วไปใน SLF4J API ซึ่งใช้ระดับการบันทึกของข้อความเป็นพารามิเตอร์
Richard Fearn

1
1 @RichardFearn และหนึ่งไม่สามารถยกเลิกการแสดงความคิดเห็น upvote หลังจาก 60 วินาทีMeh คำขอคุณลักษณะมีอยู่แล้วในขณะเดียวกัน: bugzilla.slf4j.org/show_bug.cgi?id=133
ม.ค.

3
ลิงก์ RFE ไม่สามารถแก้ไขได้อีกต่อไป ลิงค์ที่เกี่ยวข้องตอนนี้: jira.qos.ch/browse/SLF4J-124และjira.qos.ch/browse/SLF4J-197 ... และทั้งสองถูกปิดแล้ว อ่านความคิดเห็นสำหรับเหตุผล
Stephen C

28

Richard Fearn มีความคิดที่ถูกต้องดังนั้นฉันจึงเขียนคลาสเต็มตามรหัสโครงกระดูกของเขา หวังว่าจะสั้นพอที่จะโพสต์ที่นี่ คัดลอกและวางเพื่อความเพลิดเพลิน ฉันควรจะเพิ่มคาถาวิเศษด้วย: "รหัสนี้เผยแพร่สู่สาธารณสมบัติ"

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

สิ่งนี้จะง่ายกว่าเมื่อใช้กับพารามิเตอร์ args ตัวแปร (Object ... )
Anonymoose

"org.slf4j.Logger" มีลายเซ็นวิธีการบันทึกค่อนข้างน้อยที่ไม่ได้รับการจัดการในคลาสข้างต้นดังนั้นส่วนขยายจึงได้รับการรับประกัน: slf4j.org/api/org/slf4j/Logger.html
David Tonhofer

1
ฉันคิดว่าการใช้งานนี้จะเพิ่มการเปลี่ยนแปลงที่ไม่ต้องการ เมื่อคุณใช้ call logger.info (... ) คนตัดไม้สามารถเข้าถึงคลาสและเมธอดของผู้โทรและสามารถเพิ่มลงในรายการบันทึกโดยอัตโนมัติ ตอนนี้ด้วยการใช้งานนี้บันทึกการโทร (คนตัดไม้ระดับ txt) จะสร้างรายการบันทึกซึ่งจะมีผู้โทรคนเดียวกันเสมอ: Loglevel.log ฉันถูกไหม?
Domin

@Domin สวัสดีคุณหมายถึงคนตัดไม้สามารถตรวจสอบ call stack ปัจจุบันจากนั้นแยกรายการสุดท้ายสำหรับการบันทึกอัตโนมัติซึ่งไม่ใช่กรณีนี้ ตามหลักการใช่ แต่ในความเป็นจริงสแต็กจะเพิ่มขึ้นอีกเล็กน้อยหลังจากนี้จนกว่าข้อความจริงจะถูกเขียนออกมา (โดยเฉพาะอย่างยิ่งการบันทึกย้อนกลับจะต้องถูกเรียกในบางจุด ฉันคิดว่ามันควรเป็นบทบาทของ appender ในการทิ้งสแต็กไลน์ที่ไม่น่าสนใจออกไปดังนั้นคุณสามารถปรับให้มันทิ้งทุกอย่างไปและรวมถึงการเรียกคลาส Loglevel นี้
David Tonhofer

@ เดวิดใช่คุณพูดถูก :-) ฉันไม่แน่ใจว่ามันเป็นงานของ appender เพราะในกรณีนั้นคุณกำลังกำหนดการพึ่งพาอย่างหนักระหว่าง appender และคนตัดไม้ ... แต่ ... มันเป็นวิธีแก้ปัญหา ขอบคุณ David
Domin

12

คุณสามารถใช้งานได้โดยใช้ 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);
    }
}

อืม ... แต่ตอนนี้คุณต้องแก้ไข codebase ของคุณเพื่อใช้ API นี้เช่นเดียวกับหรือแทน slf4j หากคุณใช้แทน slf4j 1) อาจต้องมีความสมบูรณ์มากขึ้น 2) ต้องเปลี่ยนการนำเข้า (อย่างน้อย) จำนวนมากและ 3) เลเยอร์ใหม่นี้ด้านหน้า slf4j จะเพิ่มค่าใช้จ่ายในการบันทึกเพิ่มเติม
Stephen C

4
โปรดทราบด้วยว่าเมื่อคุณใช้วิธีแก้ปัญหานี้คลาสที่ทำการบันทึกจริงจะไม่ถูกบันทึก (เนื่องจากตัวบันทึกถูกกำหนดค่าเริ่มต้นด้วยLevelLogger) ซึ่งไม่ใช่เรื่องดีเพราะโดยทั่วไปแล้วข้อมูลที่มีประโยชน์มาก
Dormouse

12

ลองเปลี่ยนเป็น Logback แล้วใช้

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

ฉันเชื่อว่านี่จะเป็นการโทรเข้าสู่ระบบเพียงครั้งเดียวและรหัสที่เหลือของคุณจะยังคงไม่เปลี่ยนแปลง Logback ใช้ SLF4J และการย้ายข้อมูลจะไม่เจ็บปวดเพียงแค่ต้องเปลี่ยนไฟล์ config xml

อย่าลืมตั้งค่าระดับการบันทึกกลับหลังจากทำเสร็จแล้ว


ฉันใช้ slf4j ที่ได้รับการสนับสนุนจาก Logback อยู่แล้วและสิ่งนี้ทำให้ฉันสามารถล้างการทดสอบหน่วยของฉันได้ทันที ขอบคุณ!
Lambart

2
นี่เป็น -1 แรกของฉันขอบคุณ ฉันเชื่อว่าคุณคิดผิด Logback ใช้ SLF4J ดังนั้นคำตอบจึงเกี่ยวข้อง
Αλέκος

4
@AlexandrosGelbessis คุณควรอ่านคำถามซ้ำ ระบบถูกถามถึงวิธีการที่สามารถบันทึกข้อความบันทึกโดยใช้โปรแกรมหนึ่งข้อความในระดับใดก็ได้ คุณกำลังเปลี่ยนระดับของ root logger สำหรับข้อความทั้งหมดไม่ใช่เฉพาะสำหรับข้อความเดียว
ม.ค.

7

สามารถทำได้ด้วยวิธีenumและตัวช่วย:

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", ...);

คุณสามารถเพิ่มตัวแปรอื่น ๆ ได้logเช่นหากคุณต้องการค่าเทียบเท่าทั่วไปของ 1 พารามิเตอร์หรือ 2 พารามิเตอร์ของ SLF4J warn/ error/ etc วิธีการ


3
จริง แต่วัตถุประสงค์ของ slf4j คือไม่ต้องเขียน log wrapper
djjeck

5
วัตถุประสงค์ของ SLF4J คือเพื่อให้เป็นนามธรรมสำหรับกรอบการบันทึกที่แตกต่างกัน หากสิ่งที่เป็นนามธรรมนั้นไม่ตรงกับสิ่งที่คุณต้องการคุณก็ไม่มีทางเลือกอื่นนอกจากเขียนวิธีการช่วยเหลือ ทางเลือกเดียวคือการมีส่วนร่วมในวิธีการเช่นเดียวกับคำตอบของฉันสำหรับโครงการ SLF4J
Richard Fearn

ฉันเห็นด้วย แต่ในกรณีนี้มีข้อแม้เช่นคุณจะไม่สามารถระบุไฟล์และหมายเลขบรรทัดได้อีกต่อไปเว้นแต่คุณจะใช้วิธีแก้ปัญหาอื่นสำหรับสิ่งนั้น ในกรณีนี้ฉันจะติด log4j จนกว่าเฟรมเวิร์กจะรองรับคุณสมบัติซึ่งในที่สุดก็เกิดขึ้นผ่านส่วนขยายดูคำตอบล่าสุดของ Robert Elliot
djjeck


3

ฉันแค่ต้องการอะไรแบบนั้นและมาพร้อมกับ:

@RequiredArgsConstructor //lombok annotation
public enum LogLevel{

    TRACE(l -> l::trace),
    INFO (l -> l::info),
    WARN (l -> l::warn),
    ERROR(l -> l::error);

    private final Function<Logger, Consumer<String>> function;

    public void log(Logger logger, String message) {
        function.apply(logger).accept(message);
    }
}

การใช้งาน:

    LogLevel level = LogLevel.TRACE;
    level.log(logger, "message");

Logger จะถูกส่งต่อในระหว่างการเรียกใช้ดังนั้นข้อมูลคลาสควรจะโอเคและใช้งานได้ดีกับคำอธิบายประกอบ @ Slf4j lombok


ขอบคุณมากสำหรับแนวทางที่ยอดเยี่ยมนี้ - ฉันโพสต์คำตอบที่คล้ายกันตามความคิดของคุณ
slartidan

DEBUGหายไปเป็นค่าคงที่
slartidan

โซลูชันนี้จะบันทึกLogLevelเป็น class และlogas method เสมอซึ่งจะทำให้บันทึกมีความหมายน้อยลง
slartidan

3

มันเป็นไม่ได้เป็นไปเพื่อระบุระดับการบันทึกใน sjf4j 1.xออกจากกล่อง แต่มีความหวังสำหรับ slf4j 2.0เพื่อแก้ไขปัญหา ใน 2.0 อาจมีลักษณะดังนี้:

// POTENTIAL 2.0 SOLUTION
import org.slf4j.helpers.Util;
import static org.slf4j.spi.LocationAwareLogger.*;

// does not work with slf4j 1.x
Util.log(logger, DEBUG_INT, "hello world!");

ในขณะเดียวกันสำหรับ slf4j 1.x คุณสามารถใช้วิธีแก้ปัญหานี้:

คัดลอกคลาสนี้ลงใน classpath ของคุณ:

import org.slf4j.Logger;
import java.util.function.Function;

public enum LogLevel {

    TRACE(l -> l::trace, Logger::isTraceEnabled),
    DEBUG(l -> l::debug, Logger::isDebugEnabled),
    INFO(l -> l::info, Logger::isInfoEnabled),
    WARN(l -> l::warn, Logger::isWarnEnabled),
    ERROR(l -> l::error, Logger::isErrorEnabled);

    interface LogMethod {
        void log(String format, Object... arguments);
    }

    private final Function<Logger, LogMethod> logMethod;
    private final Function<Logger, Boolean> isEnabledMethod;

    LogLevel(Function<Logger, LogMethod> logMethod, Function<Logger, Boolean> isEnabledMethod) {
        this.logMethod = logMethod;
        this.isEnabledMethod = isEnabledMethod;
    }

    public LogMethod prepare(Logger logger) {
        return logMethod.apply(logger);
    }

    public boolean isEnabled(Logger logger) {
        return isEnabledMethod.apply(logger);
    }
}

จากนั้นคุณสามารถใช้งานได้ดังนี้:

Logger logger = LoggerFactory.getLogger(Application.class);

LogLevel level = LogLevel.ERROR;
level.prepare(logger).log("It works!"); // just message, without parameter
level.prepare(logger).log("Hello {}!", "world"); // with slf4j's parameter replacing

try {
    throw new RuntimeException("Oops");
} catch (Throwable t) {
    level.prepare(logger).log("Exception", t);
}

if (level.isEnabled(logger)) {
    level.prepare(logger).log("logging is enabled");
}

สิ่งนี้จะส่งออกบันทึกดังนี้:

[main] ERROR Application - It works!
[main] ERROR Application - Hello world!
[main] ERROR Application - Exception
java.lang.RuntimeException: Oops
    at Application.main(Application.java:14)
[main] ERROR Application - logging is enabled

คุ้มมั้ย?

  • Proจะเก็บตำแหน่งซอร์สโค้ดไว้ (ชื่อคลาสชื่อเมธอดหมายเลขบรรทัดจะชี้ไปที่โค้ดของคุณ )
  • Proคุณสามารถกำหนดตัวแปรพารามิเตอร์และประเภทผลตอบแทนเป็นไฟล์LogLevel
  • Proรหัสธุรกิจของคุณสั้นและอ่านง่ายและไม่จำเป็นต้องอ้างอิงเพิ่มเติม

รหัสที่มาเป็นตัวอย่างที่น้อยที่สุดจะเป็นเจ้าภาพใน GitHub


หมายเหตุ: LogMethodอินเทอร์เฟซต้องเป็นแบบสาธารณะเพื่อให้สามารถทำงานกับคลาสนอกแพ็กเกจได้ นอกเหนือจากนั้นทำงานได้ตามที่ตั้งใจไว้ ขอบคุณ!
andrebrait

1

วิธีที่ฉันใช้คือการนำเข้าโมดูล ch.qos.logback จากนั้นพิมพ์ - ส่งอินสแตนซ์ slf4j Logger ไปยัง ch.qos.logback.classic.Logger อินสแตนซ์นี้มีเมธอด setLevel ()

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

Logger levelSet = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

// Now you can set the desired logging-level
levelSet.setLevel( Level.OFF );

ในการค้นหาระดับการบันทึกที่เป็นไปได้คุณสามารถระเบิดคลาส ch.qos.logback เพื่อดูค่าที่เป็นไปได้ทั้งหมดสำหรับระดับ :

prompt$ javap -cp logback-classic-1.2.3.jar ch.qos.logback.classic.Level

ผลลัพธ์มีดังต่อไปนี้:

{
   // ...skipping
   public static final ch.qos.logback.classic.Level OFF;
   public static final ch.qos.logback.classic.Level ERROR;
   public static final ch.qos.logback.classic.Level WARN;
   public static final ch.qos.logback.classic.Level INFO;
   public static final ch.qos.logback.classic.Level DEBUG;
   public static final ch.qos.logback.classic.Level TRACE;
   public static final ch.qos.logback.classic.Level ALL;
}

1

เป็นไปไม่ได้ด้วย slf4j API ที่จะเปลี่ยนระดับการบันทึกแบบไดนามิก แต่คุณสามารถกำหนดค่าการล็อกแบ็ก (ถ้าคุณใช้สิ่งนี้) ด้วยตัวคุณเอง ในกรณีนั้นให้สร้างคลาสโรงงานสำหรับคนตัดไม้ของคุณและใช้ root logger ด้วยการกำหนดค่าที่คุณต้องการ

LoggerContext loggerContext = new LoggerContext();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

// Configure appender
final TTLLLayout layout = new TTLLLayout();
layout.start(); // default layout of logging messages (the form that message displays 
// e.g. 10:26:49.113 [main] INFO com.yourpackage.YourClazz - log message

final LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setCharset(StandardCharsets.UTF_8);
encoder.setLayout(layout);

final ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>();
appender.setContext(loggerContext);
appender.setEncoder(encoder);
appender.setName("console");
appender.start();

root.addAppender(appender);

หลังจากที่คุณกำหนดค่า root logger (เพียงครั้งเดียวก็เพียงพอแล้ว) คุณสามารถมอบหมายการรับคนตัดไม้ใหม่ได้โดย

final ch.qos.logback.classic.Logger logger = loggerContext.getLogger(clazz);

loggerContextอย่าลืมใช้เดียวกัน

loggerContextการเปลี่ยนระดับการบันทึกเป็นเรื่องง่ายที่จะทำอย่างไรกับคนตัดไม้รากได้รับจาก

root.setLevel(Level.DEBUG);

1

ยืนยันคำตอบOndrej Skopek

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;

var rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(Level.TRACE);

คุณจะได้รับผล:

2020-05-14 14: 01: 16,644 TRACE [] [oakcmMetrics] ผู้ปฏิบัติงานทดสอบตัวชี้วัดที่ลงทะเบียนชื่อ MetricName [name = bufferpool-wait-time-total, group = producer-metrics, description = เวลาทั้งหมดที่ appender รอการจัดสรรพื้นที่ ., tags = {client-id = producer-2}]


0

ฉันเพิ่งพบความต้องการที่คล้ายกัน ในกรณีของฉัน slf4j ถูกกำหนดค่าด้วยอะแด็ปเตอร์การบันทึก java (jdk14 one) การใช้ข้อมูลโค้ดต่อไปนี้ฉันได้จัดการเพื่อเปลี่ยนระดับการดีบักที่รันไทม์:

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
เช่นเดียวกับคำตอบอื่น ๆ สิ่งนี้ไม่ได้ตอบคำถามเดิม แต่เป็นปัญหาที่แตกต่างออกไป
E-Riz

0

จากคำตอบของ massimo virgilio ฉันยังสามารถทำได้ด้วย slf4j-log4j โดยใช้วิปัสสนา 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

นี่เป็นโซลูชันแลมบ์ดาที่ไม่เป็นมิตรกับผู้ใช้เช่นเดียวกับ @Paul Croarkin ในทางเดียว (ระดับจะผ่านไปสองครั้งอย่างมีประสิทธิภาพ) แต่ฉันคิดว่า (ก) ผู้ใช้ควรผ่าน Logger; และ (b) AFAIU คำถามเดิมไม่ได้ถามถึงวิธีที่สะดวกสำหรับทุกที่ในแอปพลิเคชันเป็นเพียงสถานการณ์ที่มีการใช้งานเพียงเล็กน้อยภายในห้องสมุด

package test.lambda;
import java.util.function.*;
import org.slf4j.*;

public class LoggerLambda {
    private static final Logger LOG = LoggerFactory.getLogger(LoggerLambda.class);

    private LoggerLambda() {}

    public static void log(BiConsumer<? super String, ? super Object[]> logFunc, Supplier<Boolean> logEnabledPredicate, 
            String format, Object... args) {
        if (logEnabledPredicate.get()) {
            logFunc.accept(format, args);
        }
    }

    public static void main(String[] args) {
        int a = 1, b = 2, c = 3;
        Throwable e = new Exception("something went wrong", new IllegalArgumentException());
        log(LOG::info, LOG::isInfoEnabled, "a = {}, b = {}, c = {}", a, b, c);

        // warn(String, Object...) instead of warn(String, Throwable), but prints stacktrace nevertheless
        log(LOG::warn, LOG::isWarnEnabled, "error doing something: {}", e, e);
    }
}

เนื่องจาก slf4j อนุญาตให้ Throwable (ซึ่งควรบันทึกการติดตามสแต็ก) ภายในพารามิเตอร์ varargsฉันคิดว่าไม่จำเป็นต้องใช้logวิธีการช่วยเหลือสำหรับผู้บริโภครายอื่น(String, Object[])มากเกินไป


0

ฉันสามารถทำสิ่งนี้สำหรับการโยง JDK14 ได้โดยการขออินสแตนซ์ SLF4J Logger ก่อนจากนั้นตั้งค่าระดับบนการโยง - คุณสามารถลองสิ่งนี้สำหรับการโยง Log4J

private void setLevel(Class loggerClass, java.util.logging.Level level) {
  org.slf4j.LoggerFactory.getLogger(loggerClass);
  java.util.logging.Logger.getLogger(loggerClass.getName()).setLevel(level);
}

-2

โดยใช้ java introspection คุณสามารถทำได้ตัวอย่างเช่น:

private void changeRootLoggerLevel(int level) {

    if (logger instanceof org.slf4j.impl.Log4jLoggerAdapter) {
        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.log4j.Logger loggerImpl = (org.apache.log4j.Logger) fields[i]
                            .get(logger);

                    if (level == DIAGNOSTIC_LEVEL) {
                        loggerImpl.setLevel(Level.DEBUG);
                    } else {
                        loggerImpl.setLevel(org.apache.log4j.Logger.getRootLogger().getLevel());
                    }

                    // fields[i].setAccessible(false);
                }
            }
        } catch (Exception e) {
            org.apache.log4j.Logger.getLogger(LoggerSLF4JImpl.class).error("An error was thrown while changing the Logger level", e);
        }
    }

}

5
สิ่งนี้กล่าวถึง log4j อย่างชัดเจนและไม่ใช่ slf4j โดยทั่วไป
Thorbjørn Ravn Andersen

-6

ไม่มันมีหลายวิธี info (), debug (), warn () ฯลฯ (สิ่งนี้จะแทนที่ฟิลด์ลำดับความสำคัญ)

ดูที่http://www.slf4j.org/api/org/slf4j/Logger.htmlสำหรับ Logger api ฉบับเต็ม


ขอโทษฉันเห็นสิ่งที่คุณกำลังถามตอนนี้ ไม่ไม่มีวิธีทั่วไปในการเปลี่ยนระดับการบันทึกที่รันไทม์ แต่คุณสามารถใช้วิธีการช่วยเหลือได้อย่างง่ายดายด้วยสถิติการสลับ
chris

ใช่ แต่คุณต้องทำครั้งเดียวสำหรับเมธอด "บันทึก" แต่ละเวอร์ชันที่โอเวอร์โหลด
Andrew Swan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.