将 TPL 与异步等待一起使用时无法添加日志记录
Not able to add logging when using TPL with async await
我一直在寻找这个问题的答案但没有成功,所以在这里试试。
我正在尝试在任务中使用我的代码进行日志记录。
我试图尽可能地简化我的示例。
public void ExecuteRequestOnNewTask(string clientRequestContent)
{
WriteToTextFile("just entered ExecuteRequestOnNewTask");
try
{
string response = string.Empty;
Task.Factory.StartNew(
async () =>
{
WriteToTextFile("About to call ExecuteRequest");
response = await ExecuteRequest(clientRequestContent);
WriteToTextFile("Response received:" + response);
});
}
catch (Exception ex)
{
WriteToTextFile("Exception from task:" + ex);
}
}
public async Task<string> ExecuteRequest(string clientRequestContent)
{
// long running stuff here
}
public void WriteToTextFile(string text)
{
string textWithCurrentDateTime = "\r\n" + DateTime.UtcNow.AddHours(1) + ": " + text;
string path = @"C:\Log.txt";
if (!File.Exists(path))
{
File.Create(path);
TextWriter tw = new StreamWriter(path);
tw.WriteLine(textWithCurrentDateTime);
tw.Close();
}
else if (File.Exists(path))
{
TextWriter tw = new StreamWriter(path, true);
tw.WriteLine(textWithCurrentDateTime);
tw.Close();
}
}
我在日志文件中得到的唯一输出是:
"just entered ExecuteRequestOnNewTask"
"About to call ExecuteRequest"
我没有记录响应,也没有在长 运行 方法中记录任何内容。
代码有几个问题:
Task.Factory.StartNew
很low-level, dangerous method。它有一个不明显的默认任务调度程序,并且不理解 async
委托。如果您想 运行 在线程池线程上编写代码,请改用 Task.Run
。
- 从
StartNew
返回的任务被忽略。因此,catch
块永远不会做任何事情。如果你想在一劳永逸的场景中捕获并记录异常,你需要在委托中完成。换句话说,你不能从已经被解雇和遗忘的东西中捕获异常,因为它已经被遗忘!
此外,WriteToTextFile
不是线程安全的,这在这里肯定是个问题。但是 t运行cated 日志最可能的原因是即发即弃。如果您的应用程序退出,即发即弃任务将终止 - 无一例外,无通知。这是设计使然 - 毕竟,它们是 即发即弃。
如果您想记录即发即弃任务的异常,您可以使用以下一个不错的扩展方法:
public static Task OnExceptionLogError(this Task task, string message)
{
task.ContinueWith(t =>
{
var exception = t.Exception;
var innerExceptions = exception.Flatten().InnerExceptions;
var lines = innerExceptions.Select(ex => $"{ex.GetType().FullName}: {ex.Message}");
string literal = innerExceptions.Count == 1 ? "an exception" : $"{innerExceptions.Count} exceptions";
WriteToTextFile($"{message}, {literal} occured on task #{task.Id}:\r\n " + String.Join("\r\n ", lines));
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
return task;
}
用法示例:
Task.Factory.StartNew(
async () =>
{
WriteToTextFile("About to call ExecuteRequest");
response = await ExecuteRequest(clientRequestContent);
WriteToTextFile("Response received:" + response);
}).OnExceptionLogError("ClientRequestContent"); // <--- give a title for the error
它通过调用 Task.WhenAll
记录所有可能聚合的嵌套错误。
这是另一种记录即发即弃任务异常的方法。它的优点是它是一个位于一个地方的全局处理程序,缺点是它无法记录除异常本身之外的有关错误的更多信息。
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
// ...
}
private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
// Log the e.Exception, which is an AggregateException
WriteToTextFile("Exception from task:" + e.Exception);
}
更新: 注意事项:
1) TaskScheduler.UnobservedTaskException
事件在任务被垃圾收集时引发,这可能会在很久以后发生或根本不会发生!
2) 调试版本不会引发此事件,only on Release builds!
我一直在寻找这个问题的答案但没有成功,所以在这里试试。 我正在尝试在任务中使用我的代码进行日志记录。 我试图尽可能地简化我的示例。
public void ExecuteRequestOnNewTask(string clientRequestContent)
{
WriteToTextFile("just entered ExecuteRequestOnNewTask");
try
{
string response = string.Empty;
Task.Factory.StartNew(
async () =>
{
WriteToTextFile("About to call ExecuteRequest");
response = await ExecuteRequest(clientRequestContent);
WriteToTextFile("Response received:" + response);
});
}
catch (Exception ex)
{
WriteToTextFile("Exception from task:" + ex);
}
}
public async Task<string> ExecuteRequest(string clientRequestContent)
{
// long running stuff here
}
public void WriteToTextFile(string text)
{
string textWithCurrentDateTime = "\r\n" + DateTime.UtcNow.AddHours(1) + ": " + text;
string path = @"C:\Log.txt";
if (!File.Exists(path))
{
File.Create(path);
TextWriter tw = new StreamWriter(path);
tw.WriteLine(textWithCurrentDateTime);
tw.Close();
}
else if (File.Exists(path))
{
TextWriter tw = new StreamWriter(path, true);
tw.WriteLine(textWithCurrentDateTime);
tw.Close();
}
}
我在日志文件中得到的唯一输出是:
"just entered ExecuteRequestOnNewTask" "About to call ExecuteRequest"
我没有记录响应,也没有在长 运行 方法中记录任何内容。
代码有几个问题:
Task.Factory.StartNew
很low-level, dangerous method。它有一个不明显的默认任务调度程序,并且不理解async
委托。如果您想 运行 在线程池线程上编写代码,请改用Task.Run
。- 从
StartNew
返回的任务被忽略。因此,catch
块永远不会做任何事情。如果你想在一劳永逸的场景中捕获并记录异常,你需要在委托中完成。换句话说,你不能从已经被解雇和遗忘的东西中捕获异常,因为它已经被遗忘!
此外,WriteToTextFile
不是线程安全的,这在这里肯定是个问题。但是 t运行cated 日志最可能的原因是即发即弃。如果您的应用程序退出,即发即弃任务将终止 - 无一例外,无通知。这是设计使然 - 毕竟,它们是 即发即弃。
如果您想记录即发即弃任务的异常,您可以使用以下一个不错的扩展方法:
public static Task OnExceptionLogError(this Task task, string message)
{
task.ContinueWith(t =>
{
var exception = t.Exception;
var innerExceptions = exception.Flatten().InnerExceptions;
var lines = innerExceptions.Select(ex => $"{ex.GetType().FullName}: {ex.Message}");
string literal = innerExceptions.Count == 1 ? "an exception" : $"{innerExceptions.Count} exceptions";
WriteToTextFile($"{message}, {literal} occured on task #{task.Id}:\r\n " + String.Join("\r\n ", lines));
}, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
return task;
}
用法示例:
Task.Factory.StartNew(
async () =>
{
WriteToTextFile("About to call ExecuteRequest");
response = await ExecuteRequest(clientRequestContent);
WriteToTextFile("Response received:" + response);
}).OnExceptionLogError("ClientRequestContent"); // <--- give a title for the error
它通过调用 Task.WhenAll
记录所有可能聚合的嵌套错误。
这是另一种记录即发即弃任务异常的方法。它的优点是它是一个位于一个地方的全局处理程序,缺点是它无法记录除异常本身之外的有关错误的更多信息。
static void Main(string[] args)
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
// ...
}
private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
// Log the e.Exception, which is an AggregateException
WriteToTextFile("Exception from task:" + e.Exception);
}
更新: 注意事项:
1) TaskScheduler.UnobservedTaskException
事件在任务被垃圾收集时引发,这可能会在很久以后发生或根本不会发生!
2) 调试版本不会引发此事件,only on Release builds!