在asp.net mvc中使用异步写日志

时间:2012-04-19 05:49:27

标签: asp.net-mvc asynchronous

我希望 async 在asp.net mvc中写入日志,代码如下:

public ActionResult Index()
        {
            byte[] buffer = Encoding.UTF8.GetBytes(string.Format("log:{0}{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff"), Environment.NewLine));
            FileStream fs = new FileStream(@"c:\log.txt", FileMode.Append, FileAccess.Write, FileShare.Write, 1024, true);
            fs.BeginWrite(buffer, 0, buffer.Length, ar =>
              {
                  fs.EndWrite(ar);
                  fs.Close();
              }, null);
         }

但结果不是我预期的,有些日志没有记录,有人能告诉我代码有什么问题

1 个答案:

答案 0 :(得分:6)

您需要确保只有一个线程正在写入该文件。如果有2个并发用户同时调用Index操作,他们可能会同时尝试写入日志文件,而第二个用户将失败。在ASP.NET等多线程应用程序中,始终确保正确同步对共享资源的访问。

同样在您的代码中,您无法防范错误。如果抛出异常怎么办?您永远不会关闭此流,第二次尝试访问它将引发异常。

因此,简化此代码的一种可能性是使用TPL:

private static object _synRoot = new object();

public ActionResult Index()
{
    Task.Factory.StartNew(() =>
    {
        lock (_synRoot)
        {
            var data = string.Format("log:{0}{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff"), Environment.NewLine)
            System.IO.File.AppendAllText("log.txt", data);
        }
    });
    return View();
}

为了避免控制器中出现这种噪音和污染,您可以使用动作过滤器:

public class LogAttribute : ActionFilterAttribute
{
    private static object _synRoot = new object();

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Task.Factory.StartNew(() =>
        {
            lock (_synRoot)
            {
                var data = string.Format("log:{0}{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffff"), Environment.NewLine)
                System.IO.File.AppendAllText(@"log.txt", data);
            }
        });
    }
}

然后:

[Log]
public ActionResult Index()
{
    return View();
}

但是我建议您使用.NET框架中内置的tracing capabilities而不是重新发明轮子。