2014-10-02 12 views
7

Vì vậy, chúng tôi có một hệ thống đăng nhập, nơi chúng tôi sử dụng Log.Info và nó ghi vào một ILogger.ThreadLocal và chờ

Bây giờ, chúng tôi có nhiều công nhân chạy ở chế độ nền và chúng tôi muốn các công việc này ghi vào nhật ký của riêng họ. Vì vậy, mọi thứ được đóng gói cho mỗi Công nhân. Mọi thứ được ghi lại khi tác vụ này đang được thực thi sẽ được chuyển tiếp tới trình ghi nhật ký riêng của nó.

Chúng tôi đang suy nghĩ về cách tạo phương thức Log.SetLoggerForCurrentThread, triển khai nó bằng ThreadLocal. Mã thực hiện sẽ giống như thế này:

public class Worker 
{ 
    ILogger _Logger; 

    public void ExecuteTask() 
    { 
     Log.Info("This goes to the 'Global' logger"); 

     using (Log.SetLoggerForCurrentThread(_Logger)) 
     { 
      Log.Info("This goes to local logger"); 
      DoWork(); 
     } 
    } 

    private async void DoWork() 
    { 
     Log.Info("Starting..."); 

     // SomeMethod does some logging, 
     // that also needs to be forwared to the local logger 
     var value = await SomeDeepDomainClass.SomeMethod(); 

     // if we use ThreadLocal, and this thread has been reused, 
     // it could be a completely different logger that is now attached. 
     Log.Info("Ended..."); 
    } 
} 

Câu hỏi

  • Khi chúng tôi sử dụng chờ đợi, thread có thể về mặt lý thuyết quá trình làm việc trên công nhân khác, do đó trộn lên logger địa phương.
  • Mô hình tốt nhất để làm điều gì đó tương tự? Tôi có thể sử dụng phương pháp lưu trữ nào?
  • Làm cách nào để CultureInfo xử lý việc này?

Thông tin cơ bản

Hầu hết các công nhân sẽ chạy trong một thể hiện Azure WorkerRole, nhưng bây giờ và hơn họ cũng được kích hoạt (một lần) từ một ứng dụng Console.

+0

'CultureInfo' được thiết lập vào đầu mỗi thread quản lý - đó là độc lập của 'Task'. Tôi khuyên bạn nên sử dụng rõ ràng 'Log.SetLoggerForCurrentThread' nếu bạn muốn đăng nhập từ một' Task'. Nó cho thấy bạn đã suy nghĩ rõ ràng về điều này và được đền bù. –

+0

Trước khi tôi bắt đầu làm việc trên một câu trả lời, bạn nên biết rằng log4net là miễn phí và giải quyết hầu hết các vấn đề của bạn rồi. –

+0

Xin chào @GuillaumeCR, bạn có thể đúng, nhưng xem nó như một câu hỏi lập trình chung cho các mẫu tương tự. –

Trả lời

6

Bạn có thể sử dụng dữ liệu CallContext để truyền (có thể tuần tự hóa) qua các chuỗi. Xem bài viết này cho một ví dụ:

http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html

Đối với một số thông tin cơ bản, xem bài viết này:

http://blogs.msdn.com/b/pfxteam/archive/2012/06/15/executioncontext-vs-synchronizationcontext.aspx

+5

Bạn nên đăng một ví dụ mã có ý nghĩa về câu trả lời của mình. Không tiếp tục trên các liên kết bên ngoài có thể chết. –

+0

bài viết tuyệt vời! Cảm ơn! :) –

6

Theo tôi, giải pháp tốt nhất là một trong hai vượt qua các trường hợp logger như các đối số (hoặc biến thành viên), hoặc tiêm chúng (ví dụ, sử dụng phạm vi lồng nhau).

Tuy nhiên, nếu bạn muốn lưu trữ và chuyển trường hợp ghi nhật ký ngầm theo cách tương thích với await, thì bạn sẽ cần sử dụng ngữ cảnh cuộc gọi logic. Tôi có một số blog post describing this approach, trong đó chỉ ra các giới hạn của phương pháp này:

  1. Nó chỉ hoạt động trên khuôn khổ .NET 4.5 đầy đủ.
  2. Bạn phải sử dụng ngữ nghĩa "ghi đè". Điều này thường có nghĩa là chỉ lưu trữ dữ liệu bất biến.

Với điều này trong tâm trí, đây là một số mã mà nên làm việc cho các nhu cầu của bạn:

public static class LocalLogger 
{ 
    private static readonly string name = Guid.NewGuid().ToString("N"); 

    // Static Log methods should read this. 
    public static ILogger CurrentLogger 
    { 
    public get 
    { 
     var ret = CallContext.LogicalGetData(name) as ILogger; 
     return ret == null ? Logger.GlobalLogger : ret; 
    } 

    private set 
    { 
     CallContext.LogicalSetData(name, value); 
    } 
    } 

    // Client code uses this. 
    public static IDisposable UseLogger(ILogger logger) 
    { 
    var oldLogger = CurrentLogger; 
    CurrentLogger = logger; 
    if (oldLogger == GlobalLogger) 
     return NoopDisposable.Instance; 
    return new SetWhenDisposed(oldLogger); 
    } 

    private sealed class NoopDisposable : IDisposable 
    { 
    public void Dispose() { } 
    public static readonly Instance = new NoopDisposable(); 
    } 

    private sealed class SetWhenDisposed : IDisposable 
    { 
    private readonly ILogger _oldLogger; 
    private bool _disposed; 

    public SetWhenDisposed(ILogger oldLogger) 
    { 
     _oldLogger = oldLogger; 
    } 

    public void Dispose() 
    { 
     if (_disposed) 
     return; 
     CurrentLogger = _oldLogger; 
     _disposed = true; 
    } 
    } 
} 
Các vấn đề liên quan