2009-11-17 22 views
11

Tôi đang tạo ứng dụng Composite WPF (Prism) với một số dự án khác nhau (Shell, mô-đun, v.v.). Tôi đang sẵn sàng để thực hiện đăng nhập, sử dụng Log4Net. Có vẻ như có hai cách để thiết lập ghi nhật ký:Cách tiếp cận đăng nhập tốt nhất cho ứng dụng tổng hợp?

  • Để cho dự án Shell thực hiện tất cả ghi nhật ký thực tế. Nó được tham chiếu đến Log4Net, và các dự án khác bắn các sự kiện hỗn hợp để cho Shell biết rằng nó cần phải đăng nhập một cái gì đó. Những dự án này chỉ kích hoạt các sự kiện cho các mức mà việc ghi nhật ký được bật trong tệp app.config của Shell (DEBUG, ERROR, vv), để không làm giảm hiệu suất.

  • Cung cấp cho mỗi dự án, bao gồm các mô-đun, tham chiếu Log4Net và để dự án tự ghi nhật ký vào một tệp nhật ký chung thay vì gửi thư đến Shell để ghi nhật ký.

Cách tiếp cận nào tốt hơn? Hoặc, có cách tiếp cận nào khác mà tôi nên cân nhắc không? Cảm ơn bạn đã giúp đỡ.

Trả lời

15

Cách tiếp cận đơn giản nhất để đăng nhập vào Prism là ghi đè thuộc tính LoggerFacade trong Bootstrapper của bạn. Bằng cách ghi đè số LoggerFacade, bạn có thể chuyển vào một cá thể của bất kỳ Trình ghi nào bạn muốn với bất kỳ cấu hình nào cần thiết miễn là trình ghi nhật ký triển khai giao diện ILoggerFacade.

tôi đã tìm thấy những điều sau đây để làm việc khá tốt để đăng nhập (Tôi đang sử dụng khối Logging Enterprise THƯ VIỆN, nhưng áp dụng một cái gì đó tương tự cho Log4Net nên thẳng về phía trước):

Tạo một boostrapper trong Shell của bạn:

-My Project 
    -Shell Module (add a reference to the Infrastructure project) 
    -Bootstrapper.cs 

Tạo một adapter Logging trong dự án cơ sở hạ tầng của bạn, ví dụ:

-My Project 
    -Infrastructure Module 
    -Adapters 
     -Logging 
     -MyCustomLoggerAdapter.cs 
     -MyCustomLoggerAdapterExtendedAdapter.cs 
     -IFormalLogger.cs 

Các MyCustomLoggerAdapt Lớp er sẽ được sử dụng để ghi đè thuộc tính 'LoggerFacade' trong Bootstrapper. Nó sẽ có một contstructor mặc định rằng tin tức tất cả mọi thứ lên.

Lưu ý: bằng cách ghi đè thuộc tính LoggerFacade trong Bootstrapper, bạn đang cung cấp cơ chế ghi nhật ký cho Prism để sử dụng để ghi lại các thông điệp nội bộ của chính nó.Bạn có thể sử dụng trình ghi nhật ký này trong toàn bộ ứng dụng của mình hoặc bạn có thể mở rộng trình ghi nhật ký để có trình ghi nhật ký đầy đủ tính năng hơn. (Xem MyCustomLoggerAdapterExtendedAdapter/IFormalLogger)

public class MyCustomLoggerAdapter : ILoggerFacade 
{ 

    #region ILoggerFacade Members 

    /// <summary> 
    /// Logs an entry using the Enterprise Library logging. 
    /// For logging a Category.Exception type, it is preferred to use 
    /// the EnterpriseLibraryLoggerAdapter.Exception methods." 
    /// </summary> 
    public void Log(string message, Category category, Priority priority) 
    { 
     if(category == Category.Exception) 
     { 
      Exception(new Exception(message), ExceptionPolicies.Default); 
      return; 
     } 

     Logger.Write(message, category.ToString(), (int)priority); 
    } 

    #endregion 


    /// <summary> 
    /// Logs an entry using the Enterprise Library Logging. 
    /// </summary> 
    /// <param name="entry">the LogEntry object used to log the 
    /// entry with Enterprise Library.</param> 
    public void Log(LogEntry entry) 
    { 
     Logger.Write(entry); 
    } 

    // Other methods if needed, i.e., a default Exception logger. 
    public void Exception (Exception ex) { // do stuff } 
} 

Các MyCustomLoggerAdapterExtendedAdapter được dervied từ MyCustomLoggerAdapter và có thể cung cấp nhà xây dựng bổ sung cho một full-fledged logger hơn.

public class MyCustomLoggerAdapterExtendedAdapter : MyCustomLoggerAdapter, IFormalLogger 
{ 

    private readonly ILoggingPolicySection _config; 
    private LogEntry _infoPolicy; 
    private LogEntry _debugPolicy; 
    private LogEntry _warnPolicy; 
    private LogEntry _errorPolicy; 

    private LogEntry InfoLog 
    { 
     get 
     { 
      if(_infoPolicy == null) 
      { 
       LogEntry log = GetLogEntryByPolicyName(LogPolicies.Info); 
       _infoPolicy = log; 
      } 
      return _infoPolicy; 
     } 
    } 

    // removed backing code for brevity 
    private LogEntry DebugLog... WarnLog... ErrorLog 


    // ILoggingPolicySection is passed via constructor injection in the bootstrapper 
    // and is used to configure various logging policies. 
    public MyCustomLoggerAdapterExtendedAdapter (ILoggingPolicySection loggingPolicySection) 
    { 
     _config = loggingPolicySection; 
    } 


    #region IFormalLogger Members 

    /// <summary> 
    /// Info: informational statements concerning program state, 
    /// representing program events or behavior tracking. 
    /// </summary> 
    /// <param name="message"></param> 
    public void Info(string message) 
    { 
     InfoLog.Message = message; 
     InfoLog.ExtendedProperties.Clear(); 
     base.Log(InfoLog); 
    } 

    /// <summary> 
    /// Debug: fine-grained statements concerning program state, 
    /// typically used for debugging. 
    /// </summary> 
    /// <param name="message"></param> 
    public void Debug(string message) 
    { 
     DebugLog.Message = message; 
     DebugLog.ExtendedProperties.Clear(); 
     base.Log(DebugLog); 
    } 

    /// <summary> 
    /// Warn: statements that describe potentially harmful 
    /// events or states in the program. 
    /// </summary> 
    /// <param name="message"></param> 
    public void Warn(string message) 
    { 
     WarnLog.Message = message; 
     WarnLog.ExtendedProperties.Clear(); 
     base.Log(WarnLog); 
    } 

    /// <summary> 
    /// Error: statements that describe non-fatal errors in the application; 
    /// sometimes used for handled exceptions. For more defined Exception 
    /// logging, use the Exception method in this class. 
    /// </summary> 
    /// <param name="message"></param> 
    public void Error(string message) 
    { 
     ErrorLog.Message = message; 
     ErrorLog.ExtendedProperties.Clear(); 
     base.Log(ErrorLog); 
    } 

    /// <summary> 
    /// Logs an Exception using the Default EntLib Exception policy 
    /// as defined in the Exceptions.config file. 
    /// </summary> 
    /// <param name="ex"></param> 
    public void Exception(Exception ex) 
    { 
     base.Exception(ex, ExceptionPolicies.Default); 
    } 

    #endregion 


    /// <summary> 
    /// Creates a LogEntry object based on the policy name as 
    /// defined in the logging config file. 
    /// </summary> 
    /// <param name="policyName">name of the policy to get.</param> 
    /// <returns>a new LogEntry object.</returns> 
    private LogEntry GetLogEntryByPolicyName(string policyName) 
    { 
     if(!_config.Policies.Contains(policyName)) 
     { 
      throw new ArgumentException(string.Format(
       "The policy '{0}' does not exist in the LoggingPoliciesCollection", 
       policyName)); 
     } 

     ILoggingPolicyElement policy = _config.Policies[policyName]; 

     var log = new LogEntry(); 
     log.Categories.Add(policy.Category); 
     log.Title = policy.Title; 
     log.EventId = policy.EventId; 
     log.Severity = policy.Severity; 
     log.Priority = (int)policy.Priority; 
     log.ExtendedProperties.Clear(); 

     return log; 
    } 

} 


public interface IFormalLogger 
{ 

    void Info(string message); 

    void Debug(string message); 

    void Warn(string message); 

    void Error(string message); 

    void Exception(Exception ex); 

} 

Trong Bootstrapper:

public class MyProjectBootstrapper : UnityBootstrapper 
{ 

    protected override void ConfigureContainer() 
    { 
    // ... arbitrary stuff 

    // create constructor injection for the MyCustomLoggerAdapterExtendedAdapter 
     var logPolicyConfigSection = ConfigurationManager.GetSection(LogPolicies.CorporateLoggingConfiguration); 
     var injectedLogPolicy = new InjectionConstructor(logPolicyConfigSection as LoggingPolicySection); 

     // register the MyCustomLoggerAdapterExtendedAdapter 
     Container.RegisterType<IFormalLogger, MyCustomLoggerAdapterExtendedAdapter>(
       new ContainerControlledLifetimeManager(), injectedLogPolicy); 

    } 

    private readonly MyCustomLoggerAdapter _logger = new MyCustomLoggerAdapter(); 
    protected override ILoggerFacade LoggerFacade 
    { 
     get 
     { 
      return _logger; 
     } 
    } 

} 

Cuối cùng, để sử dụng hoặc logger, tất cả các bạn cần làm là thêm giao diện phù hợp với lớp constructor của bạn và UnityContainer sẽ bơm các logger cho bạn:

public partial class Shell : Window, IShellView 
{ 
    private readonly IFormalLogger _logger; 
    private readonly ILoggerFacade _loggerFacade; 

    public Shell(IFormalLogger logger, ILoggerFacade loggerFacade) 
    { 
     _logger = logger; 
     _loggerFacade = loggerFacade 

     _logger.Debug("Shell: Instantiating the .ctor."); 
     _loggerFacade.Log("My Message", Category.Debug, Priority.None); 

     InitializeComponent(); 
    } 


    #region IShellView Members 

    public void ShowView() 
    { 
     _logger.Debug("Shell: Showing the Shell (ShowView)."); 
     _loggerFacade.Log("Shell: Showing the Shell (ShowView).", Category.Debug, Priority.None); 
     this.Show(); 
    } 

    #endregion 

} 

Tôi không nghĩ bạn cần một mô-đun riêng cho chính sách ghi nhật ký. Bằng cách thêm chính sách ghi vào mô-đun cơ sở hạ tầng của bạn, tất cả các mô-đun khác sẽ nhận được các tham chiếu bắt buộc (giả sử bạn thêm mô-đun cơ sở hạ tầng làm tham chiếu đến các mô-đun khác của bạn). Và bằng cách thêm bộ ghi vào Boostrapper của bạn, bạn có thể cho phép UnityContainer tiêm chính sách ghi nhật ký khi cần thiết.

Có một simple example of uisng Log4Net trên dự án đóng góp CompositeWPF trên CodePlex.

HTH's

+0

Tôi thiếu đăng nhập phân cấp của log4net trong giải pháp này. Một cái gì đó mà tôi nghĩ là mất tích trong lăng kính quá. –

0

Có cấu hình trình ghi nhật ký riêng cho từng mô-đun có thể trở thành vấn đề khi triển khai. Hãy nhớ rằng người dùng hoặc quản trị viên có thể thay đổi hoàn toàn mục tiêu ghi nhật ký của bạn, chuyển hướng đến cơ sở dữ liệu hoặc dịch vụ ghi nhật ký tổng hợp kho lưu trữ trung tâm (như là my company). Nếu tất cả các mô-đun riêng biệt có cấu hình riêng, người dùng/quản trị viên phải lặp lại cấu hình cho từng mô-đun (trong mỗi tệp .config hoặc trong phần của từng mô-đun trong app.config chính) và lặp lại điều này mỗi lần thay đổi vị trí/định dạng xảy ra. Và bên cạnh đó, cho rằng các ứng dụng được thêm vào lúc chạy từ cấu hình và có thể có các ứng dụng mà bạn không biết gì vào lúc này, ai đó có thể sử dụng một appender khóa tệp và dẫn đến xung đột giữa các mô-đun ứng dụng. Hsving một cấu hình log4.net đơn giản hóa việc quản trị.

Mô-đun riêng lẻ vẫn có thể được định cấu hình theo nhu cầu của từng người, riêng biệt (ví dụ: INFO cho lớp DB, L ERI cho lớp giao diện người dùng). Mỗi mô-đun sẽ nhận được logger bằng cách yêu cầu loại riêng của nó: LogManager.GetLogger(typeof(MyModule); nhưng chỉ Shell sẽ cấu hình trình ghi nhật ký (ví dụ: gọi XmlConfigurator.Configure), sử dụng app.config của riêng nó.

+1

Ngay cả khi mỗi dự án có tham chiếu riêng của mình với Log4Net, chúng sẽ không có tệp cấu hình riêng biệt - điều đó sẽ không thể quản lý được. Trong đó mỗi dự án trong một giải pháp thực hiện việc ghi nhật ký riêng của nó (với tham chiếu Log4Net của riêng nó) thì thực tế có vẻ là sử dụng một tệp cấu hình duy nhất trong ứng dụng chính. Xem http://nirajrules.wordpress.com/2008/06/14/blogging-best-practicies/ –

+0

Câu hỏi này vẫn mở. Tôi đã học được rằng Prism có đăng nhập riêng của nó, và rằng nó có thể là giao diện cho một logger như Log4Net. Nhưng tôi vẫn chưa tìm ra cách thực hành tốt nhất cho chiến lược khai thác gỗ. –

+0

Tôi cuối cùng đã tìm thấy câu trả lời của tôi - xem bên dưới. Tôi sẽ không thay đổi câu trả lời 'đúng' từ câu trả lời của Metro Smurf. Ông đưa ra một ví dụ tốt về giao tiếp Log4Net với Prism. Nhưng phản ứng của tôi dưới đây thực sự giải quyết vấn đề cốt lõi của cách để có được logger bạn thiết lập. –

3

Cuối cùng tôi đã quay lại câu trả lời này và câu trả lời thực sự khá đơn giản. Trong dự án Shell, hãy cấu hình Log4Net làm trình ghi nhật ký tùy chỉnh. Tài liệu lăng kính (tháng 2 năm 2009) giải thích cách thực hiện điều đó tại trang p. 287). Dự án Shell là dự án duy nhất cần tham chiếu đến Log4Net. Để truy cập trình ghi nhật ký (giả sử tất cả các mô-đun được truyền tham chiếu đến thùng chứa Prism IOC), chỉ cần giải quyết ILoggerFacade trong thùng chứa IOC, nó sẽ cung cấp cho bạn một tham chiếu tới trình ghi nhật ký tùy chỉnh của bạn. Chuyển một thông báo tới trình ghi nhật ký này theo cách thông thường.

Vì vậy, không cần thiết phải quay trở lại Shell và không cần mô-đun để có tham chiếu Log4Net. Thánh cá thu, tôi yêu container IOC!

+0

Tôi cũng yêu em! :) – Attilah

2

Sự cố với LoggerFacade, được đề xuất ở trên, là các phần không lăng kính trong ứng dụng của bạn sẽ không biết về nó. Trình ghi nhật ký IMHO cần phải ở mức thấp hơn và có thể truy cập phổ biến hơn so với chỉ trong khuôn khổ Composite.

Đề xuất của tôi là, tại sao không chỉ dựa vào tiêu chuẩn Debug/Trace và triển khai TraceListener của riêng bạn. Bằng cách này, nó sẽ làm việc tốt cho cả hai phần Prism/nonPrism. Bạn có thể đạt được mức độ linh hoạt mong muốn với điều này.

+0

Amen. Tôi đang làm việc trên một ứng dụng mà các nhà phát triển đã tham khảo mặt tiền 'ILogger' ở một số mã mức rất thấp. Nó mang lại cho tôi một cảm giác xấu. – Gusdor

+1

có, nhưng log4net là trình ghi nhật ký phân cấp, vượt trội so với ghi nhật ký dò vết gỡ lỗi đơn giản. Các logging lớn khó khai thác, log4net có thể cấu hình và có thể mở rộng ngay cả đối với các thủ tục lowlevel. Tại thời điểm này tôi không thấy lý do tại sao ngay cả mô-đun cấp thấp không nên có một sự phụ thuộc vào log4net. Đó là một mối quan tâm cắt ngang. Nếu thực sự cần thiết tạo ra một assembly logger giao diện và có một depency đến đó, và sử dụng một thư viện impl –

Các vấn đề liên quan