2011-10-13 2 views
13

보안상의 이유로 로그 파일로 가기 전에 내 응용 프로그램에서 기록 된 모든 메시지를보고 수정해야합니다. 나는 사용자 정의 appender (DailyRollingFileAppender를 확장)를 작성하고 subAppend (LoggingEvent 이벤트)를 대체 할 수 있다고 생각했습니다. 문제는 LoggingEvent에 메시지 텍스트에 대한 설정자가없고 메시지가 개인 속성이라는 것입니다. 수정 된 메시지로 새 LoggingEvent를 만들 수 있지만 API가 원래 LoggingEvent의 나머지 부분을 쉽게 복사 할 수는 없습니다. 이 모든 것은 사용자 정의 appender에서 메시지에 간섭하는 것을 막기 위해 설계된 것 같습니다.LOG4J : 사용자 지정 appender를 사용하여 기록 된 메시지 수정

내가 볼 수있는 유일한 다른 옵션은 텍스트를 먼저 수정할 수있는 새로운 전역 메서드를 호출하고 Log4J 호출을 만들기 위해 수백 개의 로깅 문을 수정하는 것입니다. 나는 오히려하지 않을 것이다!

다른 누구도 사용자 정의 appender에서 기록 된 메시지를 수정해야합니까?

답변

0

Logger에 대한 대리자 클래스를 만들고 모든 가져 오기를 org.apache.log4j.Logger에서 your.own.Logger으로 변경하려고합니다. 코드에서 로거 호출을 변경하는 것을 고려하기 때문에 간단하고 자동으로 처리 할 수 ​​있습니다. — 소스 코드에 대한 완전한 액세스 권한이 있습니다. 델리게이트에서 log4j Logger의 메소드를 정확히 호출 할 것이지만 문자열을 먼저 마음의 내용에 간섭하십시오.

코드를 탐색하고 버전 1.2.15 이전에 새 라이브러리를 만들 때 라이브러리의 절반을 파기하지 않고 기존 LoggingEvent을 새로 만들 수는 없습니다. 1.2.15부터는 아무런 문제가 없습니다.

11

새로운 LoggingEvent을 만드는 것이 왜 까다로운 지 잘 모르겠습니다.

package test.logging; 

import org.apache.log4j.DailyRollingFileAppender; 
import org.apache.log4j.spi.LoggingEvent; 

public class MyDailyRollingFileAppender extends DailyRollingFileAppender { 

    @Override 
    protected void subAppend(LoggingEvent event) { 
     String modifiedMessage = String.format("**** Message modified by MyDailyRollingFileAppender ****\n\n%s\n\n**** Finished modified message ****", event.getMessage()); 
     LoggingEvent modifiedEvent = new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), event.getTimeStamp(), event.getLevel(), modifiedMessage, 
                 event.getThreadName(), event.getThrowableInformation(), event.getNDC(), event.getLocationInformation(), 
                 event.getProperties()); 

     super.subAppend(modifiedEvent); 
    } 

} 

이 테스트로 :

package test; 

import org.apache.log4j.Logger; 

public class TestLogging { 

    public static void main(String[] args) { 
     Logger log = Logger.getLogger(TestLogging.class); 
     log.info("I am testing my logging"); 
     log.info("Here is an exception", new Exception()); 
    } 

} 

이 구성 :

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> 

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 

    <appender name="MyDailyRollingFileAppender" class="test.logging.MyDailyRollingFileAppender"> 
     <param name="Append" value="true"/> 
     <param name="datePattern" value="'.'yyyy-MM-dd"/> 
     <param name="File" value="mine.log"/> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <root> 
     <priority value="debug"/> 
     <appender-ref ref="MyDailyRollingFileAppender"/> 
    </root> 

</log4j:configuration> 

나는 다음과 같은 출력을 얻고있다 :

2011-10-14 10:09:09,322 INFO () [main] TestLogging - **** Message modified by MyDailyRollingFileAppender **** 

I am testing my logging 

**** Finished modified message **** 
2011-10-14 10:09:09,333 INFO () [main] TestLogging - **** Message modified by MyDailyRollingFileAppender **** 

Here is an exception 

**** Finished modified message **** 
java.lang.Exception 
    at test.TestLogging.main(TestLogging.java:10) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 

를 이것은 나를 위해 작동하는 것 같다 내가 simila 뭔가를했지만 r에 약간 다른 접근 방식을 사용했습니다. Appender의 각 유형의 하위 클래스를 작성하는 대신 다른 Appender 오브젝트를 래핑하는 Appender 오브젝트를 작성하고 랩핑 된 Appender으로 송신하기 전에 메시지를 수정합니다. 이런 식으로 뭔가 :

package test.logging; 

import org.apache.log4j.Appender; 
import org.apache.log4j.AppenderSkeleton; 
import org.apache.log4j.spi.AppenderAttachable; 
import org.apache.log4j.spi.LoggingEvent; 

import java.util.ArrayList; 
import java.util.Collections; 
import java.util.Enumeration; 
import java.util.Iterator; 
import java.util.List; 

public class MyAppenderWrapper extends AppenderSkeleton implements AppenderAttachable { 

    private final List<Appender> appenders = new ArrayList<Appender>(); 

    public void close() { 
     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       appender.close(); 
      } 
     } 
    } 

    public boolean requiresLayout() { 
     return false; 
    } 

    public void addAppender(Appender appender) { 
     synchronized (appenders) { 
      appenders.add(appender); 
     } 
    } 

    public Enumeration getAllAppenders() { 
     return Collections.enumeration(appenders); 
    } 

    public Appender getAppender(String name) { 
     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       if (appender.getName().equals(name)) { 
        return appender; 
       } 
      } 
     } 
     return null; 
    } 

    public boolean isAttached(Appender appender) { 
     synchronized (appenders) { 
      for (Appender wrapped : appenders) { 
       if (wrapped.equals(appender)) { 
        return true; 
       } 
      } 
      return false; 
     } 
    } 

    public void removeAllAppenders() { 
     synchronized (appenders) { 
      appenders.clear(); 
     } 
    } 

    public void removeAppender(Appender appender) { 
     synchronized (appenders) { 
      for (Iterator<Appender> i = appenders.iterator(); i.hasNext();) { 
       if (i.next().equals(appender)) { 
        i.remove(); 
       } 
      } 
     } 
    } 

    public void removeAppender(String name) { 
     synchronized (appenders) { 
      for (Iterator<Appender> i = appenders.iterator(); i.hasNext();) { 
       if (i.next().getName().equals(name)) { 
        i.remove(); 
       } 
      } 
     } 
    } 

    @Override 
    protected void append(LoggingEvent event) { 
     String modifiedMessage = String.format("**** Message modified by MyAppenderWrapper ****\n\n%s\n\n**** Finished modified message ****", event.getMessage()); 
     LoggingEvent modifiedEvent = new LoggingEvent(event.getFQNOfLoggerClass(), event.getLogger(), event.getTimeStamp(), event.getLevel(), modifiedMessage, 
                 event.getThreadName(), event.getThrowableInformation(), event.getNDC(), event.getLocationInformation(), 
                 event.getProperties()); 

     synchronized (appenders) { 
      for (Appender appender : appenders) { 
       appender.doAppend(modifiedEvent); 
      } 
     } 
    } 

} 

당신처럼 구성 할 수 있습니다 :

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> 

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> 

    <appender name="StdOut" class="org.apache.log4j.ConsoleAppender"> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <appender name="FileAppender" class="org.apache.log4j.DailyRollingFileAppender"> 
     <param name="Append" value="true"/> 
     <param name="datePattern" value="'.'yyyy-MM-dd"/> 
     <param name="File" value="mine.log"/> 
     <layout class="org.apache.log4j.PatternLayout"> 
      <param name="ConversionPattern" value="%d %-5p (%x) [%t] %c{1} - %m%n" /> 
     </layout> 
    </appender> 

    <appender name="AppenderWrapper" class="test.logging.MyAppenderWrapper"> 
     <appender-ref ref="StdOut"/> 
     <appender-ref ref="FileAppender"/> 
    </appender> 

    <root> 
     <priority value="debug"/> 
     <appender-ref ref="AppenderWrapper"/> 
    </root> 

</log4j:configuration> 

메시지가 여전히 원래 펜더에 있지만 수정 된 메시지와 함께 전송됩니다 그런 식으로.

+2

버전 1.2.15 이상을 사용하고 있기 때문에 효과가 있습니다. 이전 log4j 버전에는 예제에서 사용하는 메소드가 없으므로 작성자의 문제입니다. 오랫동안 최신 버전이었던 많은 프로젝트는 1.2.14 버전을 사용합니다. – MaDa

+1

1.2.16으로 업그레이드하여 예제를 사용하여 새 LoggingEvent를 만들 수있었습니다. 도와 주셔서 감사합니다! – user993719

4

또 다른 옵션은 appender에서 사용하는 레이아웃을 사용자 정의하는 것입니다. 레이아웃은 로그 이벤트를 문자열로 직렬화하는 책임이 있기 때문에 레이아웃을 수정하는 것이 appender와 이벤트를 변경하는 것보다 덜 복잡한 지 확인합니다. 그냥 생각 ...내가 펜더 사용자 정의의

대신 log4j에 이전 버전의 함께 일하고 있기 때문에 내가 어떻게했는지

+0

예! 그 소리는 Appenders를 수정하는 대신 틈새 접근법처럼 들립니다. 커스텀 레이아웃은 다중 appenders 대신에 사용될 수 있습니다. – hellojava

3

그게 전부, 내가 레이아웃을 사용자 정의하고 내가이 기능을 필요로 중이 appender에 그 레이아웃을 언급

public class LogValidatorLayout extends PatternLayout { 

    public LogValidatorLayout() { 
     super(); 
    } 

    public LogValidatorLayout(String pattern) { 
     super(pattern); 
    } 

    @Override 
    public String format(LoggingEvent event) { 

     // only process String type messages 
     if (event.getMessage() != null && event.getMessage() instanceof String) { 

      String message = event.getMessage().toString(); 
      message = StringUtils.trim("Some custom text --->>"+message); 

      // earlier versions of log4j don't provide any way to update messages, 
      // so use reflections to do this 
      try { 
       Field field = LoggingEvent.class.getDeclaredField("message"); 
       field.setAccessible(true); 
       field.set(event, message); 
      } catch (Exception e) { 
       // Dont log it as it will lead to infinite loop. Simply print the trace 
       e.printStackTrace(); 
      } 

     } 

     return super.format(event); 
    } 

} 

그리고 log4j.properties 또는 xml에이 레이아웃을 등록하십시오.

log4j.appender.STDOUT.layout=a.b.c.package.LogValidatorLayout 
관련 문제