2012-07-02 33 views
7

Khi bạn làm điều gì đó như LOG.debug("Exported {}.", product) trong slf4j, cuối cùng nó sẽ gọi toString() trên các đối số, ví dụ: product.slf4j mà không toString()

Vì một số lý do, tôi không thể ghi đè lênString() trên tất cả các lớp tôi muốn sử dụng làm đối số. Một số lớp đến từ các lọ bên thứ ba, một số khác sẽ có toString() của chúng được gọi trong các ngữ cảnh khác, nơi mà thông tin tôi muốn in trong câu lệnh tường trình của tôi không có sẵn.

Tuy nhiên, tôi có một lớp cho mục đích gỡ lỗi có phương thức DebugFormatter.format(Object) có một chuỗi dài các instanceofs chọn thường trình để tìm một số thông tin gỡ lỗi hữu ích về đối tượng đó.

Câu hỏi của tôi là: Có thể định cấu hình slf4j sao cho nó gọi phương thức tĩnh như vậy thay vì toString()?

Tất nhiên, tôi có thể gọi phương thức định dạng của tôi trên đối tượng trước khi chuyển nó thành tham số Logger.debug() nhưng sau đó nó sẽ được thực hiện ngay cả khi trình ghi nhật ký tương ứng không được bật. Vì vậy, tôi đã phải bao quanh nó với if (LOG.isDebugEnabled()) có nghĩa là toàn bộ các điểm có đối số trong debug() đã bị bỏ qua.

+0

Bạn chưa đề cập đến khung đăng nhập cơ bản. Có log4j không? đăng lại? – Ceki

+0

Đó là log4j. Tôi đang có kế hoạch chuyển sang logback, tuy nhiên. – Wolfgang

+0

Tôi đã chấp nhận câu trả lời của Andrew là câu trả lời chính thức vì nó dễ thực hiện hơn so với Ceki. Nó cũng độc lập với khung bên dưới. Thêm vào đó nó cho phép thực hiện định dạng tùy chọn, hoặc thậm chí chọn giữa các trình định dạng khác nhau. Tuy nhiên, đối với những người dùng khác, câu trả lời của Ceki có thể phù hợp hơn vì nó minh bạch hơn và tiết kiệm tài nguyên. – Wolfgang

Trả lời

9

Bạn có thể tạo hình ảnh, lấy đối tượng và gọi DebugFormatter.format() từ chức năng toString(). Một cái gì đó như thế này:

class DebugFormatObject { 
    private final Object o; 

    public static DebugFormatObject forDebug(Object o) { 
    return new DebugFormatObject(o); 
    } 

    private DebugFormatObject(Object o) { 
    this.o = o; 
    } 

    @Override 
    public String toString() { 
    return DebugFormatter.format(o); 
    } 
} 

Với việc nhập khẩu tĩnh thích hợp, tuyên bố khai thác gỗ của bạn trở nên này:

LOG.debug("Exported {}.", forDebug(product)); 

này có chi phí khoảng hơn thông qua các đối tượng trong thẳng, nhưng đó là một nhỏ, chi phí cố định - và vật thể tạo ra sẽ rất ngắn ngủi.

+0

Đó là một ý tưởng khá gọn gàng mà tôi chưa từng nghĩ trong khoảng. Nếu logger bị vô hiệu hóa, trình định dạng không được gọi. Hạn chế duy nhất là tôi sẽ tạo ra đối tượng bao bọc một cách vô dụng. Không chắc có bao nhiêu tác động. – Wolfgang

+0

@Andrew xin lỗi, tôi đã đọc sai câu trả lời của bạn. +1 –

1

Tôi sợ slf4j không được xây dựng để được mở rộng theo cách như vậy. Những gì bạn có thể làm là triển khai thực hiện slf4j của riêng bạn hoạt động như một người trang trí xung quanh việc triển khai hiện có, cung cấp xử lý đặc biệt cho một số loại đã biết. Nhưng bạn sẽ phải tái phát minh ra nhiều bánh xe. Ví dụ: các phương thức triển khai trình ghi nhật ký của bạn sẽ phải bắt đầu bằng if(underLyingLogger.isXyzEnabled()) mặc dù trình ghi nhật ký cơ bản sẽ thực hiện kiểm tra chính xác lần nữa sau khi trình trang trí của bạn gọi các phương thức cơ bản.

Mặt khác, một cách giải quyết khác mà tôi đã sử dụng trong quá khứ là ToString-Delegate. Biểu mẫu đơn giản nhất có thể chỉ là một đối tượng ẩn danh:

final SomeObject myObj = ...; 
LOG.debug("foo {}", new Object(){ 
    public String toString(){ 
     return myObj.someMethod(); 
    } 
}); 

Ở đây bạn cũng có các đối tượng bao bọc ngắn gọn nhưng bạn không thực sự hiển thị các chuỗi cho đến khi bạn phải làm vậy.

vì cú pháp trên rất xấu, tôi khuyên bạn nên cung cấp lớp Nhà máy với các phương thức trợ giúp tĩnh để tạo các đối tượng ToString này. Trong trường hợp của tôi, tôi có một lớp trừu tượng được gọi là ToStringWrapper và nó có các phương thức nhà máy để Tham gia một phiên bản có thể lặp lại với một Guava Joiner, v.v.

+0

Điều đó khác với ý tưởng của Andrew như thế nào? Ngoại trừ việc ký hiệu của anh ngắn hơn nhiều ... * Chỉnh sửa: * Với bản chỉnh sửa, ý tưởng của bạn giờ đây khá giống với ý tưởng của Andrew. :-) – Wolfgang

+0

@Wolfgang vâng, tôi vừa nhận ra mình đã hiểu sai câu trả lời của anh ấy. Tôi nghĩ phương pháp 'forDebug' của anh ta đã thực hiện hành vi của định dạng –

+1

SLF4J phụ thuộc vào kiểu ràng buộc. – Ceki

5

Nếu khung khai thác cơ bản là thực hiện gốc như , gọi toString() trên đối số được thực hiện bởi khung ghi nhật ký và không phải bởi SLF4J. Sau đó bạn có thể gọi DebugFormatter.format (o) khi thông điệp tường trình thực sự được xuất/in bằng cách tạo ra một custom converter. Cụ thể hơn, bạn sẽ tạo trình chuyển đổi thay thế thông điệp% msg /%.

Đối với triển khai không phải bản địa, lệnh gọi hàm toString() được thực hiện bởi SLF4J. Vì vậy, các câu trả lời được cung cấp bởi Andrew và Sean áp dụng.

+0

Đó là khá nhiều những gì tôi đang tìm kiếm. Tuyệt quá! Nhưng nó có nghĩa là tôi phải thực hiện thay thế biến, phải không? Tôi sẽ xem xét làm thế nào mà được thực hiện trong logback và thử nó ra. Bây giờ (chúng tôi đang ở trên log4j), tôi sẽ đi với giải pháp của Andrew và Sean. – Wolfgang

3

Đối với những người muốn viết một tùy chỉnh Logback Convereter, đây là mã của một tôi đã viết để tránh bãi đầy đủ của một đối tượng kế thừa:

... 
import ch.qos.logback.classic.pattern.MessageConverter; 
import ch.qos.logback.classic.spi.ILoggingEvent; 

public class PrettyMessageConverter extends MessageConverter { 

    private static final String EMPTY = ""; 
    private static final String PLACEHOLDER = "{}"; 

    @Override 
    public String convert(ILoggingEvent event) { 
     StringBuilder message = new StringBuilder(event.getMessage()); 

    int argumentIndex = 0; 
     int placeholderIndex = -1; 
     while ((placeholderIndex=message.indexOf(PLACEHOLDER, placeholderIndex))!=-1 && message.charAt(placeholderIndex-1)!='\\') { 
     String stringValue = valueOf(getArgument(event.getArgumentArray(), argumentIndex++)); 
      message.replace(placeholderIndex, placeholderIndex+PLACEHOLDER.length(), stringValue); 
     } 
     return message.toString(); 
    } 

    /** 
    * Return a {@link String} representation of the given object. It use convert 
    * some complex objects as a single line string and use {@link String#valueOf(Object))} 
    * for other objects. 
    */ 
    private String valueOf(final Object object) { 
     if (object instanceof AuthenticatedUser) 
      return valueOf((AuthenticatedUser) object); 

     return String.valueOf(object); 
    } 

    private String valueOf(AuthenticatedUser user) { 
     return user.getUsername(); 
    } 

    /** 
    * Retrieve an argument at a given position but avoid {@link ArrayIndexOutOfBoundsException} 
    * by returning {@link PrettyMessageConverter#EMPTY} string when the index 
    * is out of bounds. 
    */ 
    private Object getArgument(Object[] arguments, int index) { 
     return (index<arguments.length)?arguments[index]:EMPTY; 
    } 

} 

Với dòng này từ bạn logback.xml :

<conversionRule conversionWord="msg" converterClass="PrettyMessageConverter" /> 
Các vấn đề liên quan