异步记录器。我可以丢失/延迟日志条目吗? [英] Async Logger. Can I lose/delay log entries?

查看:84
本文介绍了异步记录器。我可以丢失/延迟日志条目吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在实现自己的日志记录框架。以下是我的 BaseLogger ,它接收日志条目并将其推送到实现抽象日志方法的实际Logger中。 / p>

我使用C#TPL以异步方式记录日志。我使用线程而不是TPL。 (TPL任务没有真正的线程。因此,如果应用程序的所有线程结束,任务也将停止,这将导致所有等待日志条目丢失。)

 公共抽象类BaseLogger 
{
// ...省略的属性构造函数.etc。 ... //

public virtual void AddLogEntry(LogEntry条目)
{
if(!AsyncSupported)
{
//基础记录器没有不支持异步。
//只需调用log方法并返回。
日志(条目);
的回报;
}
//记录器支持异步。
LogAsync(entry);
}

private void LogAsync(LogEntry entry)
{
lock(LogQueueSyncRoot)//确保在访问队列之前我们已锁定。
{
LogQueue.Enqueue(entry);
}

if(LogThread == null || LogThread.ThreadState == ThreadState.Stopped)
{//线程已完成,或者这是我们第一次正在记录到该记录器。
LogTask = new new Thread(new ThreadStart(()=>
{
而(true)
{
LogEntry logEntry;
锁定(LogQueueSyncRoot )
{
if(LogQueue.Count> 0)
{
logEntry = LogQueue.Dequeue();
}
否则
{
break;
//是否可以添加一条消息,
//在中断之后并且我倾斜锁{}但
//在退出之前
}
}
Log(logEntry);
}
}));
LogThread.Start();
}
}

//实际的记录器实现将实现此方法。
受保护的抽象void Log(LogEntry条目);
}

请注意, AddLogEntry 可以同时从多个线程调用。



我的问题是,此实现有可能丢失日志条目吗?
我担心,是否有可能在我的线程存在带有break语句的循环并退出lock块(位于else子句中)和该线程之后,立即向队列中添加日志条目



我确实意识到,因为我正在使用队列,即使我错过了一个条目,下一个登录请求也是如此,也会推送错过的条目。但这是不能接受的,特别是如果在应用程序的最后一个日志条目中发生这种情况。



此外,请让我知道是否以及如何实现相同的功能,但是使用带有更清晰代码的新C#5.0 async await 关键字。我不介意使用.NET 4.5。



预先感谢。

解决方案

我看到两个竞赛条件不在我的头上:


  1. 您可以旋转多个线程,如果有多个线程调用 AddLogEntry

  2. 是的,可以在 Thread 退出时将事件排队,然后进入

此外,这里还有一个严重的性能问题:除非您不断记录(数千每秒),您将为每个日志条目创建一个新的 Thread 线程。



就像James,我同意您应该使用已建立的日志记录库。日志记录似乎并不简单,并且已经有很多解决方案。



也就是说,如果您想要一个基于.NET 4.5的不错的方法,这很容易:

 公共抽象类BaseLogger 
{
private只读ActionBlock< LogEntry>块;

受保护的BaseLogger(int maxDegreeOfParallelism = 1)
{
块=新的ActionBlock< LogEntry>(
条目=>
{
Log(entry);
},
新的ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = maxDegreeOfParallelism,
});
}

public virtual void AddLogEntry(LogEntry entry)
{
block.Post(entry);
}

受保护的抽象void Log(LogEntry entry);
}


I'm implementing my own logging framework. Following is my BaseLogger which receives the log entries and push it to the actual Logger which implements the abstract Log method.

I use the C# TPL for logging in an Async manner. I use Threads instead of TPL. (TPL task doesn't hold a real thread. So if all threads of the application end, tasks will stop as well, which will cause all 'waiting' log entries to be lost.)

public abstract class BaseLogger
{
    // ... Omitted properties constructor .etc. ... //

public virtual void AddLogEntry(LogEntry entry)
{
    if (!AsyncSupported)
    {
        // the underlying logger doesn't support Async.
        // Simply call the log method and return.
        Log(entry);
        return;
    }
    // Logger supports Async.
    LogAsync(entry);
}

private void LogAsync(LogEntry entry)
{
    lock (LogQueueSyncRoot) // Make sure we ave a lock before accessing the queue.
    {
        LogQueue.Enqueue(entry);
    }

    if (LogThread == null || LogThread.ThreadState == ThreadState.Stopped)
    { // either the thread is completed, or this is the first time we're logging to this logger.
        LogTask = new  new Thread(new ThreadStart(() =>
        {
            while (true)
            {
                LogEntry logEntry;
                lock (LogQueueSyncRoot)
                {
                    if (LogQueue.Count > 0)
                    {
                        logEntry = LogQueue.Dequeue();
                    }
                    else
                    {
                        break; 
                        // is it possible for a message to be added,
                        // right after the break and I leanve the lock {} but 
                        // before I exit the loop and task gets 'completed' ??
                    }
                }
                Log(logEntry);
             }
        }));
        LogThread.Start();
    }
}

// Actual logger implimentations will impliment this method.
protected abstract void Log(LogEntry entry);
}

Note that AddLogEntry can be called from multiple threads at the same time.

My question is, is it possible for this implementation to lose log entries ? I'm worried that, is it possible to add a log entry to the queue, right after my thread exists the loop with the break statement and exits the lock block, and which is in the else clause, and the thread is still in the 'Running' state.

I do realize that, because I'm using a queue, even if I miss an entry, the next request to log, will push the missed entry as well. But this is not acceptable, specially if this happens for the last log entry of the application.

Also, please let me know whether and how I can implement the same, but using the new C# 5.0 async and await keywords with a cleaner code. I don't mind requiring .NET 4.5.

Thanks in Advance.

解决方案

I see two race conditions off the top of my head:

  1. You can spin up more than one Thread if multiple threads call AddLogEntry. This won't cause lost events but is inefficient.
  2. Yes, an event can be queued while the Thread is exiting, and in that case it would be "lost".

Also, there's a serious performance issue here: unless you're logging constantly (thousands of times a second), you're going to be spinning up a new Thread for each log entry. That will get expensive quickly.

Like James, I agree that you should use an established logging library. Logging is not as trivial as it seems, and there are already many solutions.

That said, if you want a nice .NET 4.5-based approach, it's pretty easy:

public abstract class BaseLogger
{
  private readonly ActionBlock<LogEntry> block;

  protected BaseLogger(int maxDegreeOfParallelism = 1)
  {
    block = new ActionBlock<LogEntry>(
        entry =>
        {
          Log(entry);
        },
        new ExecutionDataflowBlockOptions
        {
          MaxDegreeOfParallelism = maxDegreeOfParallelism,
        });
  }

  public virtual void AddLogEntry(LogEntry entry)
  {
    block.Post(entry);
  }

  protected abstract void Log(LogEntry entry);
}

这篇关于异步记录器。我可以丢失/延迟日志条目吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆