2009-12-18 45 views
10

Tôi làm việc trong một dự án sử dụng Log4J. Một trong những yêu cầu là tạo một tệp nhật ký riêng biệt cho mỗi luồng; bản thân vấn đề này là một vấn đề kỳ lạ, được sắp xếp một phần bằng cách tạo một FileAppender mới trên bay và đính kèm nó vào cá thể Logger.Log4J - Chức năng SiftingAppender giống như

Logger logger = Logger.getLogger(<thread dependent string>); 
FileAppender appender = new FileAppender(); 
appender.setFile(fileName); 
appender.setLayout(new PatternLayout(lp.getPattern())); 
appender.setName(<thread dependent string>); 
appender.setThreshold(Level.DEBUG); 
appender.activateOptions(); 
logger.addAppender(appender); 

Tất cả mọi thứ diễn ra tốt đẹp cho đến khi chúng ta nhận ra rằng một thư viện chúng tôi sử dụng - Spring Framework v3.0.0 (mà sử dụng Commons Logging) - không chơi bóng với các kỹ thuật nói trên - các dữ liệu Xuân khai thác gỗ được “nhìn thấy” chỉ bởi appenders khởi từ tệp log4.configuration nhưng không phải do trình tạo thời gian chạy đã tạo. Vì vậy, hãy quay lại hình vuông.

Sau khi điều tra, tôi phát hiện ra rằng LogBack mới và được cải tiến có một appender - SiftingAppender - thực hiện chính xác những gì chúng tôi cần, tức là ghi nhật ký cấp luồng trên các tệp độc lập.

Hiện tại, việc chuyển sang LogBack không phải là một tùy chọn, vì vậy, bị mắc kẹt với Log4J, làm thế nào tôi có thể đạt được chức năng giống như SiftingAppender và giữ cho Spring luôn vui vẻ?

Lưu ý: Mùa xuân chỉ được sử dụng cho chức năng JdbcTemplate, không có IOC; để “móc” Commons Spring Logging để Log4J tôi đã thêm dòng này trong file log4j.properties:

log4j.logger.org.springframework = DEBUG

theo hướng dẫn here.

+1

Nếu bạn chỉ sử dụng chức năng Spring cho jdbc, thì bạn có thể muốn xem xét sử dụng một cái gì đó như apache commons-dbutils thay thế. –

Trả lời

3

Đăng nhập được truy cập qua slf4j api. Có một thư viện bộ điều hợp được gọi là jcl-over-sjf4j để lộ giao diện ghi nhật ký commons nhưng làm cho tất cả việc ghi nhật ký vào API slf4j, đi trực tiếp đến triển khai - LogBack. Nếu bạn đang sử dụng maven, đây là sự phụ thuộc:

<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>slf4j-api</artifactId> 
    <version>1.5.8</version> 
</dependency> 
<dependency> 
    <groupId>org.slf4j</groupId> 
    <artifactId>jcl-over-slf4j</artifactId> 
    <version>1.5.8</version> 
</dependency> 
<dependency> 
    <groupId>ch.qos.logback</groupId> 
    <artifactId>logback-core</artifactId> 
    <version>0.9.18</version> 
</dependency> 

(và thêm commons-logging vào danh sách loại trừ, xem here)

0

tôi muốn bao gồm tất cả các mặt tiền slf4j/tái router/whateveryoucallthem. Cũng lưu ý các "cung cấp" hack, giữ phụ thuộc từ kéo trong commons đăng nhập; trước đây tôi đã sử dụng một thư viện trống rỗng đăng nhập commons được gọi là phiên bản-99.0-không-không tồn tại.

Xem thêm http://blog.springsource.com/2009/12/04/logging-dependencies-in-spring/

<dependencies> 
    <dependency> 
     <groupId>commons-logging</groupId> 
     <artifactId>commons-logging</artifactId> 

     <!-- use provided scope on real JCL instead --> 
     <!-- <version>99.0-does-not-exist</version> --> 

     <version>1.1.1</version> 

     <scope>provided</scope> 
    </dependency> 

    <dependency> 
     <groupId>commons-logging</groupId> 
     <artifactId>commons-logging-api</artifactId> 

     <!-- use provided scope on real JCL instead --> 
     <!-- <version>99.0-does-not-exist</version> --> 

     <version>1.1</version> 

     <scope>provided</scope> 
    </dependency> 

    <!-- the slf4j commons-logging replacement --> 
    <!-- if any package is using jakarta commons logging this will --> 
    <!-- re-route it through slf4j. --> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>jcl-over-slf4j</artifactId> 

     <version>${version.slf4j}</version> 
    </dependency> 

    <!-- the slf4j log4j replacement. --> 
    <!-- if any package is using log4j this will re-route --> 
    <!-- it through slf4j. --> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>log4j-over-slf4j</artifactId> 

     <version>${version.slf4j}</version> 
    </dependency> 

    <!-- the slf4j java.util.logging replacement. --> 
    <!-- if any package is using java.util.logging this will re-route --> 
    <!-- it through slf4j. --> 
    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>jul-to-slf4j</artifactId> 
     <version>${version.slf4j}</version> 
    </dependency> 

    <dependency> 
     <groupId>org.slf4j</groupId> 
     <artifactId>slf4j-api</artifactId> 

     <version>${version.slf4j}</version> 
    </dependency> 

    <dependency> 
     <groupId>ch.qos.logback</groupId> 
     <artifactId>logback-classic</artifactId> 

     <version>${version.logback}</version> 
    </dependency> 
</dependencies> 

<properties> 
    <version.logback>0.9.15</version.logback> 
    <version.slf4j>1.5.8</version.slf4j> 
</properties> 
0

bạn đã xem xét log4j.NDC và MDC? Điều này ít nhất cho phép bạn gắn thẻ dữ liệu chủ đề cụ thể vào nhật ký của bạn. Không chính xác những gì bạn đang yêu cầu, nhưng có thể hữu ích. Có một cuộc thảo luận here.

3

Tôi đã gặp khó khăn trong một lúc để tìm chức năng giống như SiftingAppender trong log4j (chúng tôi không thể chuyển sang logback vì một số phụ thuộc) và kết thúc bằng giải pháp lập trình hoạt động khá tốt, sử dụng MDC và logger tại runtime:

// this can be any thread-specific string 
String processID = request.getProcessID(); 

Logger logger = Logger.getRootLogger(); 

// append a new file logger if no logger exists for this tag 
if(logger.getAppender(processID) == null){ 

    try{ 
    String pattern = "%d{yy/MM/dd HH:mm:ss} %p %c{2}: %m%n"; 
    String logfile = "log/"+processID+".log"; 

    FileAppender fileAppender = new FileAppender(
     new PatternLayout(pattern), logfile, true); 
    fileAppender.setName(processID); 

    // add a filter so we can ignore any logs from other threads 
    fileAppender.addFilter(new ProcessIDFilter(processID)); 

    logger.addAppender(fileAppender); 
    }catch(Exception e){ 
    throw new RuntimeException(e); 
    } 
} 

// tag all child threads with this process-id so we can separate out log output 
MDC.put("process-id", processID); 

//whatever you want to do in the thread 
LOG.info("This message will only end up in "+processID+".log!"); 

MDC.remove("process-id"); 

Bộ lọc nối trên chỉ kiểm tra cho một quá trình id cụ thể:

public class RunIdFilter extends Filter { 

    private final String runId; 

    public RunIdFilter(String runId) { 
    this.runId = runId; 
    } 

    @Override 
    public int decide(LoggingEvent event) { 
    Object mdc = event.getMDC("run-id"); 

    if (runId.equals(mdc)) { 
     return Filter.ACCEPT; 
    } 

    return Filter.DENY; 
    } 
} 

Hope this helps một chút.

0

Trong Log4j2, bây giờ chúng ta có thể sử dụng RoutingAppender:

Các RoutingAppender đánh giá LogEvents và sau đó tuyến đường họ đến một appender cấp dưới. Appender đích có thể là một appender được cấu hình trước đó và có thể được tham chiếu bởi tên của nó hoặc Appender có thể được tạo động khi cần thiết.

Từ FAQ của họ:

Làm thế nào để tự động viết để tách các file log? Nhìn vào RoutingAppender. Bạn có thể định nghĩa nhiều tuyến trong cấu hình và đặt các giá trị trong bản đồ ThreadContext xác định tệp nhật ký nào các sự kiện tiếp theo trong chuỗi này được đăng nhập.

Các vấn đề liên quan