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

Jef*_*hnn 1 asp.net-mvc asynchronous

我想 在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);
         }
Run Code Online (Sandbox Code Playgroud)

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

Dar*_*rov 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();
}
Run Code Online (Sandbox Code Playgroud)

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

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);
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

然后:

[Log]
public ActionResult Index()
{
    return View();
}
Run Code Online (Sandbox Code Playgroud)

但是,我建议您使用.NET框架内置的跟踪功能,而不是重新发明轮子.