2016-12-22 47 views
0

所有我見過的使用面向方面的編程來記錄日誌類,方法名和持續時間的例子,並且如果他們記錄參數並返回值,他們只需使用ToString()。我需要對記錄的內容有更多的控制權。例如,我想跳過密碼,或者在某些情況下記錄對象的所有屬性,但在其他情況下只是id屬性。 有什麼建議嗎?我在C#中查看了Java中的AspectJ和Unity攔截,並找不到解決方案。使用面向方面編程時的自定義參數日誌記錄

回答

0

您可以嘗試引入參數註釋以增強具有某些屬性的參數。其中一個屬性可能表示跳過記錄參數,另一個屬性可用於爲字符串表示指定轉換器類。

與以下注釋:

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface Log { 
} 


@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.PARAMETER) 
public @interface SkipLogging { 
} 

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.PARAMETER) 
public @interface ToStringWith { 
    Class<? extends Function<?, String>> value(); 
} 

方面看起來是這樣的:

import java.lang.reflect.Parameter; 
import java.util.function.Function; 
import java.util.stream.Collectors; 
import java.util.stream.IntStream; 

import org.aspectj.lang.reflect.MethodSignature; 
import org.slf4j.Logger; 
import org.slf4j.LoggerFactory; 

public aspect LoggingAspect { 

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

    pointcut loggableMethod(): execution(@Log * *..*.*(..)); 

    before(): loggableMethod() { 
     MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature(); 
     Parameter[] parameters = signature.getMethod() 
      .getParameters(); 
     String message = IntStream.range(0, parameters.length) 
      .filter(i -> this.isLoggable(parameters[i])) 
      .<String>mapToObj(i -> toString(parameters[i], thisJoinPoint.getArgs()[i])) 
      .collect(Collectors.joining(", ", 
        "method execution " + signature.getName() + "(", ")")); 
     Logger methodLogger = LoggerFactory.getLogger(
       thisJoinPointStaticPart.getSignature().getDeclaringType()); 
     methodLogger.debug(message); 
    } 

    private boolean isLoggable(Parameter parameter) { 
     return parameter.getAnnotation(SkipLogging.class) == null; 
    } 

    private String toString(Parameter parameter, Object value) { 
     ToStringWith toStringWith = parameter.getAnnotation(ToStringWith.class); 
     if (toStringWith != null) { 
      Class<? extends Function<?, String>> converterClass = 
        toStringWith.value(); 
      try { 
       @SuppressWarnings("unchecked") 
       Function<Object, String> converter = (Function<Object, String>) 
        converterClass.newInstance(); 
       String str = converter.apply(value); 
       return String.format("%s='%s'", parameter.getName(), str); 
      } catch (Exception e) { 
       logger.error("Couldn't instantiate toString converter for logging " 
         + converterClass.getName(), e); 
       return String.format("%s=<error converting to string>", 
         parameter.getName()); 
      } 
     } else { 
      return String.format("%s='%s'", parameter.getName(), String.valueOf(value)); 
     } 
    } 

} 

測試代碼:

public static class SomethingToStringConverter implements Function<Something, String> { 

    @Override 
    public String apply(Something something) { 
     return "Something nice"; 
    } 

} 

@Log 
public void test(
     @ToStringWith(SomethingToStringConverter.class) Something something, 
     String string, 
     @SkipLogging Class<?> cls, 
     Object object) { 

} 

public static void main(String[] args) { 
// execution of this method should log the following message: 
// method execution test(something='Something nice', string='some string', object='null') 
    test(new Something(), "some string", Object.class, null); 
} 

我使用的Java 8流API在我回答它的緊湊性,你可以將代碼轉換爲正常的Java代碼,如果你不使用Java 8功能或需要更好的效率。這只是給你一個想法。

+0

感謝您的詳細回覆。這是他們的方式攔截日誌通常用於?方法聲明看起來有點凌亂與所有註釋,並且ToStringConverter不是類型安全的。當某些方面只會在運行時失敗時,代碼如何維護? – shoren

+0

我發佈的這段代碼只是抓住了你可以做的日誌記錄的表面。如果您認爲涉及日誌方法的方法簽名過於冗長,則可以根據類型引入有意義的默認值,您可以在該類本身聲明日誌記錄行爲,以便跳過顯式的'@ ToStringWith'或'@ SkipLogging'註釋方法聲明。這更像是你如何使用AspectJ進行這種跟蹤/記錄的例子。 –

+0

類型安全性如何? ToStringConverter必須從一個對象進行投射。沒有類型安全的解決方案嗎? – shoren