Tôi đã từng sử dụng các mặt tiền ghi nhật ký như Common.Logging (thậm chí để ẩn thư viện CutEdge.Logging của riêng mình ), nhưng ngày nay tôi sử dụng mẫu Dependency Injection và điều này cho phép tôi ẩn các log đằng sau phần trừu tượng (đơn giản) của riêng tôi tuân theo cả Dependency Nguyên tắc nghịch đảo và nguyên tắc phân tách giao diện(ISP) vì nó có một thành viên và vì giao diện được xác định bởi ứng dụng của tôi; không phải là một thư viện bên ngoài. Giảm thiểu kiến thức mà các phần cốt lõi của ứng dụng của bạn có về sự tồn tại của các thư viện bên ngoài, thì càng tốt; ngay cả khi bạn không có ý định thay thế thư viện ghi nhật ký của mình. Việc phụ thuộc nhiều vào thư viện bên ngoài khiến việc kiểm tra mã của bạn trở nên khó khăn hơn và làm phức tạp ứng dụng của bạn với một API chưa bao giờ được thiết kế riêng cho ứng dụng của bạn.
Đây là những gì trừu tượng thường trông giống như trong các ứng dụng của tôi:
public interface ILogger
{
void Log(LogEntry entry);
}
public enum LoggingEventType { Debug, Information, Warning, Error, Fatal };
// Immutable DTO that contains the log information.
public class LogEntry
{
public readonly LoggingEventType Severity;
public readonly string Message;
public readonly Exception Exception;
public LogEntry(LoggingEventType severity, string message, Exception exception = null)
{
if (message == null) throw new ArgumentNullException("message");
if (message == string.Empty) throw new ArgumentException("empty", "message");
this.Severity = severity;
this.Message = message;
this.Exception = exception;
}
}
Theo tùy chọn, phần trừu tượng này có thể được mở rộng bằng một số phương pháp mở rộng đơn giản (cho phép giao diện thu hẹp và tiếp tục tuân thủ ISP). Điều này làm cho mã cho người tiêu dùng của giao diện này đơn giản hơn nhiều:
public static class LoggerExtensions
{
public static void Log(this ILogger logger, string message) {
logger.Log(new LogEntry(LoggingEventType.Information, message));
}
public static void Log(this ILogger logger, Exception exception) {
logger.Log(new LogEntry(LoggingEventType.Error, exception.Message, exception));
}
// More methods here.
}
Vì giao diện chỉ chứa một phương thức duy nhất, bạn có thể dễ dàng tạo một ILogger
triển khai proxy tới log4net , tới Serilog , Microsoft.Extensions.Logging , NLog hoặc bất kỳ thư viện ghi nhật ký nào khác và định cấu hình vùng chứa DI của bạn để đưa nó vào các lớp cóILogger
trong chúng constructor.
Xin lưu ý rằng việc có các phương thức mở rộng tĩnh trên đầu giao diện với một phương thức duy nhất khá khác với việc có một giao diện có nhiều thành viên. Các phương thức mở rộng chỉ là các phương thức trợ giúp tạo LogEntry
thông báo và chuyển nó qua phương thức duy nhất trên ILogger
giao diện. Các phương pháp mở rộng trở thành một phần của mã của người tiêu dùng; không phải là một phần của trừu tượng. Điều này không chỉ cho phép các phương thức mở rộng phát triển mà không cần thay đổi phần trừu tượng, các phương thức mở rộng vàLogEntry
hàm tạo luôn được thực thi khi tính trừu tượng của trình ghi nhật ký được sử dụng, ngay cả khi trình ghi nhật ký đó được khai thác / chế nhạo. Điều này mang lại sự chắc chắn hơn về tính đúng đắn của các lệnh gọi đến trình ghi nhật ký khi chạy trong bộ thử nghiệm. Giao diện một ghi nhớ cũng làm cho việc kiểm tra dễ dàng hơn nhiều; Có một sự trừu tượng với nhiều thành viên khiến cho việc tạo triển khai (chẳng hạn như mocks, adapter và decorator) trở nên khó khăn.
Khi bạn làm điều này, hầu như không cần đến một số trừu tượng tĩnh mà các mặt đăng nhập (hoặc bất kỳ thư viện nào khác) có thể cung cấp.