2009-07-29 46 views
56

Tôi có một ứng dụng java sử dụng log4j.log4j chuyển hướng stdout sang DailyRollingFileAppender

Config:

log4j.rootLogger=info, file 

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender 
log4j.appender.file.File=${user.home}/logs/app.log 
log4j.appender.file.layout=org.apache.log4j.PatternLayout 
log4j.appender.file.layout.ConversionPattern=%d [%t] %c %p %m%n 

Vì vậy, tất cả những điều khoản đăng nhập được nối một cách chính xác để các tập tin, nhưng tôi đang mất stdout và stderr. Làm cách nào để chuyển hướng các dấu vết ngăn xếp ngoại lệ và sysouts vào tệp được cuộn hàng ngày?

Trả lời

88
// I set up a ConsoleAppender in Log4J to format Stdout/Stderr 
log4j.rootLogger=DEBUG, CONSOLE 
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 
log4j.appender.CONSOLE.layout.ConversionPattern=[%t] %-5p %c - %m%n 


// And I call this StdOutErrLog.tieSystemOutAndErrToLog() on startup 

public class StdOutErrLog { 

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

    public static void tieSystemOutAndErrToLog() { 
     System.setOut(createLoggingProxy(System.out)); 
     System.setErr(createLoggingProxy(System.err)); 
    } 

    public static PrintStream createLoggingProxy(final PrintStream realPrintStream) { 
     return new PrintStream(realPrintStream) { 
      public void print(final String string) { 
       realPrintStream.print(string); 
       logger.info(string); 
      } 
     }; 
    } 
} 
+7

điều này làm việc với các ngoại lệ, nhưng nó không nắm bắt tất cả các đầu ra được ghi vào stdout. Ví dụ nếu bạn gọi System.out.print (5); nó sẽ bỏ qua luồng in proxy. là tốt, ngay cả với trường hợp ngoại lệ này sẽ kết thúc in thêm dòng trống để stderr nếu bạn làm someException.printStackTrace() – sbridges

+0

hi..where tập tin đăng nhập sẽ tạo ra? cảm ơn bạn –

+3

@sbridges bạn chỉ có thể ghi đè lên nhiều phương thức nếu bạn yêu cầu. Ví dụ, 'printStackTrace' gọi phương thức' println (Object o) 'mà bạn cũng có thể ghi đè để loại bỏ các dòng trống khó chịu đó. – tysonjh

2

Tôi đoán bạn đang ghi nhật ký stacktraces qua e.printStackTrace()? Bạn có thể vượt qua một đối tượng ngoại lệ vào các phương pháp khai thác gỗ Log4j và những người sẽ xuất hiện trong nhật ký của mình (xem Logger.error(Object obj, Throwable t))

Lưu ý rằng bạn có thể thay đổi System.out và System.err khác PrintStream đó chuyển hướng đến Log4j. Đó sẽ là một thay đổi đơn giản và giúp bạn chuyển đổi tất cả các câu lệnh System.out.println() của mình.

1

Đầu ra tiêu chuẩn và luồng lỗi được quản lý từ vùng chứa của bạn. Ví dụ Tomcat sử dụng JULI để ghi nhật ký đầu ra và các luồng lỗi.

Đề xuất của tôi là để nguyên như vậy. Tránh sử dụng System.out.print trong ứng dụng của bạn. Xem here để biết dấu vết ngăn xếp.

+0

Làm thế nào về các ứng dụng java độc lập không sử dụng vùng chứa? Chắc chắn có một cách để bắt stdout và đăng nhập vào log4j? –

+0

Theo đề xuất của Brian Agnew bạn có thể chuyển hướng luồng tới log4j. Tuy nhiên tôi khuyên bạn nên chống lại nó. Giải pháp không được in ở đầu ra tiêu chuẩn, nhưng thay vào đó hãy sử dụng log4j. Không có một thư viện java phong nha nào sử dụng đầu ra tiêu chuẩn. Nếu bạn có mã của bên thứ ba thực hiện điều này, hãy yêu cầu họ sử dụng log4j hoặc commons logging để thay thế. Việc chuyển hướng đầu ra tiêu chuẩn phải là phương sách cuối cùng. – kgiannakakis

+0

Không sử dụng System.out.println() để ghi nhật ký. Sử dụng log4j (hoặc một khung đăng nhập khác) trong mã của bạn, và chỉ cần cấu hình gửi đầu ra tới stdout. –

5

Nếu bạn đang sử dụng máy chủ ứng dụng, thùng chứa servlet hoặc thứ gì đó tương tự, hãy xem kgiannakakis's answer.

Đối với các ứng dụng độc lập, hãy xem this. Bạn có thể đánh giá lại stdin, stdoutstderr sử dụng lớp java.lang.System. Về cơ bản bạn tạo một lớp con mới của PrintStream và thiết lập cá thể đó thành System.out.

Điều gì đó dọc theo các dòng này khi bắt đầu ứng dụng của bạn (chưa được kiểm tra).

// PrintStream object that prints stuff to log4j logger 
public class Log4jStream extends PrintStream { 
     public void write(byte buf[], int off, int len) { 
     try { 
      // write stuff to Log4J logger 
     } catch (Exception e) { 
     } 
    } 
} 

// reassign standard output to go to log4j 
System.setOut(new Log4jStream()); 
7

Tôi nhặt ý tưởng từ Michael S., nhưng cũng giống như đề cập trong một bình luận, nó có một số vấn đề: nó không chụp tất cả mọi thứ, và nó in một số dòng sản phẩm nào.

Ngoài ra tôi muốn tách System.outSystem.err, do đó System.out sẽ được ghi lại với mức log 'INFO'System.err được đăng nhập với 'ERROR' (hoặc 'WARN' nếu bạn thích).

Vì vậy, đây là giải pháp của tôi: Đầu tiên một lớp mở rộng OutputStream (dễ dàng ghi đè tất cả các phương thức cho OutputStream hơn cho PrintStream). Nó ghi nhật ký với mức nhật ký được chỉ định và cũng sao chép mọi thứ sang một số khác OutputStream. Và nó cũng phát hiện chuỗi "trống rỗng" (chỉ chứa khoảng trắng) và không ghi lại chúng.

import java.io.IOException; 
import java.io.OutputStream; 

import org.apache.log4j.Level; 
import org.apache.log4j.Logger; 

public class LoggerStream extends OutputStream 
{ 
private final Logger logger; 
private final Level logLevel; 
private final OutputStream outputStream; 

public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream) 
{ 
    super(); 

    this.logger = logger; 
    this.logLevel = logLevel; 
    this.outputStream = outputStream; 
} 

@Override 
public void write(byte[] b) throws IOException 
{ 
    outputStream.write(b); 
    String string = new String(b); 
    if (!string.trim().isEmpty()) 
     logger.log(logLevel, string); 
} 

@Override 
public void write(byte[] b, int off, int len) throws IOException 
{ 
    outputStream.write(b, off, len); 
    String string = new String(b, off, len); 
    if (!string.trim().isEmpty()) 
     logger.log(logLevel, string); 
} 

@Override 
public void write(int b) throws IOException 
{ 
    outputStream.write(b); 
    String string = String.valueOf((char) b); 
    if (!string.trim().isEmpty()) 
     logger.log(logLevel, string); 
} 
} 

Và sau đó là một lớp tiện ích rất đơn giản để thiết outerr:

import java.io.PrintStream; 

import org.apache.log4j.Level; 
import org.apache.log4j.Logger; 

public class OutErrLogger 
{ 
public static void setOutAndErrToLog() 
{ 
    setOutToLog(); 
    setErrToLog(); 
} 

public static void setOutToLog() 
{ 
    System.setOut(new PrintStream(new LoggerStream(Logger.getLogger("out"), Level.INFO, System.out))); 
} 

public static void setErrToLog() 
{ 
    System.setErr(new PrintStream(new LoggerStream(Logger.getLogger("err"), Level.ERROR, System.err))); 
} 

} 
+0

Nơi để thêm đường dẫn vào tệp nhật ký ?? – Aniket

+0

bạn có thể cho tôi một ý tưởng về cách log4j.properties hoặc tệp log4j.xml sẽ trông giống như để làm việc này? – Aniket

+0

@acoolguy Xem [Log4j XML Configuration Primer] (http: //wiki.apache.org/logging-log4j/Log4jXmlFormat), sử dụng [FileAppender] (http://wiki.apache.org/logging-log4j/FileAppender). –

1

Các Anser của @ Michael là một điểm tốt. Nhưng mở rộng PrintStream không phải là rất tốt đẹp, bởi vì nó sử dụng một phương pháp nội bộ void write(String) để viết tất cả mọi thứ vào một OutputStream.

Tôi thích sử dụng LoggingOutputStream Class từ gói Log4J Contrib.

Sau đó, tôi chuyển hướng hệ thống suối như thế này:

public class SysStreamsLogger { 
    private static Logger sysOutLogger = Logger.getLogger("SYSOUT"); 
    private static Logger sysErrLogger = Logger.getLogger("SYSERR"); 

    public static final PrintStream sysout = System.out; 
    public static final PrintStream syserr = System.err; 

    public static void bindSystemStreams() { 
     // Enable autoflush 
     System.setOut(new PrintStream(new LoggingOutputStream(sysOutLogger, LogLevel.INFO), true)); 
     System.setErr(new PrintStream(new LoggingOutputStream(sysErrLogger, LogLevel.ERROR), true)); 
    } 

    public static void unbindSystemStreams() { 
     System.setOut(sysout); 
     System.setErr(syserr); 
    } 
} 
+0

LoggingOutputStream dường như không tồn tại nữa trong bản phát hành bản ghi log4j mới nhất (1.0.4) –

+0

chỉ cần lấy nó từ https://svn.apache.org/viewvc/logging /log4j/trunk/contribs/JimMoore/LoggingOutputStream.java?view=co! nó khá độc lập. – itshorty

10

Trong mã Skaffman: Để loại bỏ các dòng trống trong nhật ký log4j, chỉ cần thêm phương pháp "println" để PrintStream của createLoggingProxy

public static PrintStream createLoggingProxy(final PrintStream realPrintStream) { 
    return new PrintStream(realPrintStream) { 
     public void print(final String string) { 
      logger.warn(string); 
     } 
     public void println(final String string) { 
      logger.warn(string); 
     } 
    }; 
} 
2

Các câu trả lời ở trên cung cấp một ý tưởng tuyệt vời để sử dụng proxy để ghi nhật ký STDOUT/ERR. Tuy nhiên các ví dụ triển khai được cung cấp không hoạt động tốt cho mọi trường hợp. Ví dụ: hãy thử

System.out.printf ("Testing% s \ n", "ABC");

Mã từ ví dụ trên sẽ cắt đầu ra thành các phần riêng biệt trên bảng điều khiển và trong nhiều mục Log4j không thể đọc được.

Giải pháp là để đệm đầu ra cho đến khi trình kích hoạt '\ n' được tìm thấy ở cuối bộ đệm. Đôi khi bộ đệm kết thúc bằng '\ r \ n'. Lớp bên dưới giải quyết vấn đề này. Nó có đầy đủ chức năng. Gọi phương thức static bind() để kích hoạt nó.

import java.io.IOException; 
import java.io.OutputStream; 
import java.io.PrintStream; 

import org.apache.log4j.Level; 
import org.apache.log4j.Logger; 

// Based on 
// http://stackoverflow.com/questions/1200175/log4j-redirect-stdout-to-dailyrollingfileappender 
public class Log4jStdOutErrProxy { 

    public static void bind() { 
    bind(Logger.getLogger("STDOUT"), Logger.getLogger("STDERR")); 
    } 

    @SuppressWarnings("resource") 
    public static void bind(Logger loggerOut, Logger loggerErr) { 
    System.setOut(new PrintStream(new LoggerStream(loggerOut, Level.INFO, System.out), true)); 
    System.setErr(new PrintStream(new LoggerStream(loggerErr, Level.ERROR, System.err), true)); 
    } 

    private static class LoggerStream extends OutputStream { 
    private final Logger logger; 
    private final Level logLevel; 
    private final OutputStream outputStream; 
    private StringBuilder sbBuffer; 

    public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream) { 
     this.logger = logger; 
     this.logLevel = logLevel; 
     this.outputStream = outputStream; 
     sbBuffer = new StringBuilder(); 
    } 

    @Override 
    public void write(byte[] b) throws IOException { 
     doWrite(new String(b)); 
    } 

    @Override 
    public void write(byte[] b, int off, int len) throws IOException { 
     doWrite(new String(b, off, len)); 
    } 

    @Override 
    public void write(int b) throws IOException { 
     doWrite(String.valueOf((char)b)); 
    } 

    private void doWrite(String str) throws IOException { 
     sbBuffer.append(str); 
     if (sbBuffer.charAt(sbBuffer.length() - 1) == '\n') { 
     // The output is ready 
     sbBuffer.setLength(sbBuffer.length() - 1); // remove '\n' 
     if (sbBuffer.charAt(sbBuffer.length() - 1) == '\r') { 
      sbBuffer.setLength(sbBuffer.length() - 1); // remove '\r' 
     } 
     String buf = sbBuffer.toString(); 
     sbBuffer.setLength(0); 
     outputStream.write(buf.getBytes()); 
     outputStream.write('\n'); 
     logger.log(logLevel, buf); 
     } 
    } 
    } // inner class LoggerStream 

} 
0

Trước khi sử dụng phương thức System.setOut và System.setErr, chúng ta nên đặt lại đối tượng java.util.logging.LogManager bằng phương thức reset().

public static void tieSystemOutAndErrToLog() { 

    try{ 

     // initialize logging to go to rolling log file 
     LogManager logManager = LogManager.getLogManager(); 
     logManager.reset(); 

     // and output on the original stdout 
     System.out.println("Hello on old stdout"); 
     System.setOut(createLoggingProxy(System.out)); 
     System.setErr(createLoggingProxy(System.err)); 

     //Following is to make sure whether system.out and system.err is redirecting to the stdlog.log file 
     System.out.println("Hello world!"); 

     try { 
      throw new RuntimeException("Test"); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    }catch(Exception e){ 
     logger.error("Caught exception at StdOutErrLog ",e); 
     e.printStackTrace(); 
    } 
} 
Các vấn đề liên quan