C# 跨一系列异步操作标识日志项

C# 跨一系列异步操作标识日志项,c#,logging,asynchronous,C#,Logging,Asynchronous,我以前见过有人在线程ID上关联日志条目;一个不完美的解决方案,但在许多情况下都有效。在开始使用c#进行异步编程之后,我很好奇,处理跨多个异步操作的日志记录的最佳方法是什么。举个例子: async Task<string> GetSongNameAsync(byte [] songBytes, AuthInfo auth) { Log("Received request from user " + auth.User); if (!await Authentic

我以前见过有人在线程ID上关联日志条目;一个不完美的解决方案,但在许多情况下都有效。在开始使用c#进行异步编程之后,我很好奇,处理跨多个异步操作的日志记录的最佳方法是什么。举个例子:

async Task<string> GetSongNameAsync(byte [] songBytes, AuthInfo auth)
{    
    Log("Received request from user " + auth.User);
    if (!await Authentication.HasPremiumAccountAsync(auth)) // More logging in here
    {
        return "Error: Your subscription does not support this feature";
    }

    Song song = await SongIdentifier.IdentifyAsync(songBytes); // Even more logging here
    if (song != null)
    {
        return song.ToString()
    }

    return new Song() {Name = "Never Gonna Give You Up", Artist = "Rick Astley", Notes = "You just got rick-rolled!"};
}
异步任务GetSongNameAsync(字节[]songBytes,AuthInfo auth) { 日志(“收到来自用户的请求”+验证用户); if(!await Authentication.HasPremiumAccountAsync(auth))//此处有更多登录 { return“错误:您的订阅不支持此功能”; } Song Song=等待SongIdentifier.IdentificationAsync(songBytes);//这里还有更多日志记录 如果(歌曲!=null) { 返回歌曲 } return new Song(){Name=“永远不会放弃你”,Artist=“Rick Astley”,Notes=“你刚刚把Rick卷起来了!”; }
如果auth逻辑或song ID逻辑中存在错误,我希望将错误与特定请求关联起来,这样我就可以查询日志数据库,并按顺序查看请求是如何处理的,以及失败的原因。我可以开始传递Guid或类似的东西,并将其包含在消息中,但让每个构造函数和对象都公开这个值似乎有点麻烦,而我真的希望它位于异步等效的“线程本地存储”

您可以使用
Task.Id
和静态
Task.CurrentId
,使用方式与
Thread.Id
类似,但它并不能真正与async\wait配合使用,正如您所见。 我认为最好的方法是以一种独特的方式识别您收到的每个请求,或者通过使用基础结构(在WCF中,这将是
OperationContext.Current.IncomingMessageHeaders.MessageId
)提供的内容将requestId附加到它(或者基类),或者使用精确的勾号(可能带有线程id)当请求作为ID接收时


顺便说一句,如果您的请求是通过分布式系统处理的,那么这也是一个很好的解决方案


关于你的评论

你可以:

public static class MagicalHelpper
{
private static ConditionalWeakTable<object, int> _registry = new ConditionalWeakTable<object, int>();

public void GetId(object obj)
{
 int result = _registry.GetValue(obj, ((object key)=> DateTime.Now.Ticks +   
                                                  Thread.CurrentThread.ManagedThreadId));
  return result;

}
}
公共静态类MagicalHelper
{
私有静态条件weaktable_registry=new ConditionalWeakTable();
public void GetId(object obj)
{
int result=\u registry.GetValue(obj,((对象键)=>DateTime.Now.Ticks+
Thread.CurrentThread.ManagedThreadId));
返回结果;
}
}
这是在没有编译器的情况下编写的

现在,只要你想,你就可以去MagicalHelper.GetId(某物)只要记住坚持使用相同的请求对象

这应该是完全线程安全的,并且不会导致任何内存泄漏。
有条件的可扩展文档:

有一篇关于这个主题的大型MSDN文章。没有简单的解决方案,但它描述了一种使用
ExecutionContext
和线程本地存储来实现这一点的方法。这里有一些示例代码。不过,要注意的是


如果您正在运行.Net 4.5,那么还有一个备选答案,即任务的标识符不是问题,但是我可以通过某种方式附加它,使库代码可以调用
Log(task.CustomIdentifier+“/SongTooShort”)
,不管如何调用库代码,都保证与调用方的标识符匹配?