我有兴趣了解更多有关人们如何使用依赖注入平台注入日志记录的信息.虽然下面的链接和我的示例引用了log4net和Unity,但我不一定会使用其中任何一个.对于依赖注入/ IOC,我可能会使用MEF,因为这是项目的其余部分(大)正在解决的标准.
我是依赖注入/ ioc的新手,对于C#和.NET来说还是新手(在过去10年左右的VC6和VB6中,在C#/ .NET中编写了很少的生产代码).我已经对各种日志解决方案进行了大量调查,所以我认为我对其功能集有一个不错的处理.我只是不熟悉获得一个依赖注入的实际机制(或者,可能更"正确",获得一个依赖注入的抽象版本).
我见过其他与日志记录和/或依赖注入相关的帖子,如: 依赖注入和日志记录接口
我的问题与"如何使用ioc工具yyy注入日志记录平台xxx"无关?相反,我感兴趣的是人们如何处理包装日志记录平台(通常,但并不总是建议)和配置(即app.config).例如,使用log4net作为示例,我可以配置(在app.config中)一些记录器,然后以标准方式使用这样的代码来获取这些记录器(没有依赖注入):
private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
Run Code Online (Sandbox Code Playgroud)
或者,如果我的记录器没有为某个类命名,而是为了一个功能区,我可以这样做:
private static readonly ILog logger = LogManager.GetLogger("Login");
private static readonly ILog logger = LogManager.GetLogger("Query");
private static readonly ILog logger = LogManager.GetLogger("Report");
Run Code Online (Sandbox Code Playgroud)
所以,我想我的"要求"将是这样的:
我想将我的产品源与直接依赖于日志记录平台隔离开来.
我希望能够通过某种依赖注入(可能是MEF)直接或间接地解析特定的命名记录器实例(可能在同一命名实例的所有请求者之间共享相同的实例).
我不知道我是否会称之为硬性要求,但我希望能够根据需要获得一个命名记录器(不同于类记录器).例如,我可能会根据类名为我的类创建一个记录器,但是一个方法需要特别重的诊断,我想单独控制.换句话说,我可能希望单个类"依赖"两个独立的记录器实例.
让我们从数字1开始.我已经阅读了很多文章,主要是关于stackoverflow,关于它是否是一个好主意.请参阅上面的"最佳实践"链接,并转到jeffrey hantin的评论,了解一个关于为什么包装log4net不好的观点.如果你确实包装(如果你能有效地包装),你会严格包装以注入/消除直接的倾向吗?或者你也会尝试抽象出部分或全部log4net app.config信息?
假设我想使用System.Diagnostics,我可能想要实现一个基于接口的记录器(甚至可能使用"常见的"ILogger/ILog接口),可能基于TraceSource,以便我可以注入它.你会实现接口,比如TraceSource,只是按原样使用System.Diagnostics app.config信息吗?
像这样的东西:
public class MyLogger : ILogger
{
private TraceSource ts;
public MyLogger(string name)
{
ts = new TraceSource(name);
}
public void ILogger.Log(string msg)
{
ts.TraceEvent(msg);
}
} …Run Code Online (Sandbox Code Playgroud) 如何在标准跟踪,Logger.NET,企业库,log4net或Ukadc.Diagnostics之间进行选择?
是否存在一个比另一个更合适的情况?那会是什么?(ASP.NET,控制台应用,Azure云,SOHO,企业...)
有什么好处或缺点?
我是否错过了其他主要的日志记录框架?
更新于2014年11月18日 - 在浏览log4net源存储库时,我发现LogicalThreadContext的实现在2011年11月被修改为使用CallContext.LogicalSetData存储其属性(并使用LogicalGetData获取它们).这很重要,因为这意味着LogicalThreadContext现在应该可以正常工作.存储在LogicalThreadContext中的任何数据都应"流向"任何子线程或任务.这与ThreadContext(以及LogicalThreadContext的旧实现)相比较,其中存储在上下文中的数据将保持在当前线程的本地,而不是流向子线程/任务.
如果您有兴趣,可以参考以下内容:
希望有人发现这个老问题会发现这些信息很有用.
log4net提供了两个不同的"线程上下文"对象:ThreadContext和LogicalThreadContext,每个对象 都有一个属性包.ThreadContext有一个ThreadContextProperties包,而LogicalThreadContext有一个LogicalThreadContextProperties包.
ThreadContext可能更常被称为"MDC".LogicalContext可能更常被称为"LDC".我会在这篇文章的其余部分使用短名称.
MDC.Properties使用System.Threading.Thread.SetData实现,而LDC.Properties使用System.Runtime.Remoting.Messaging.CallContext.SetData实现.
为了进行比较,NLog仅公开"MDC"(现在称为MappedDiagnosticContext)来存储线程本地属性.NLog的实现使用System.Threading.Thread.SetData,因此它的实现与log4net相同.
在log4net和NLog中,"MDC"属性存储在字典中,该字典本身存储在线程本地存储中.
在这样的情况下,将字典存储在用[ThreadStatic]装饰的类成员变量中是否相同?
[ThreadStatic]
private static IDictionary<string, string> threadProperties;
Run Code Online (Sandbox Code Playgroud)
什么是使用.NET 4.0的新ThreadLocal类的等效(或类似)声明?
最终,LDC和MDC之间真正的,实际的差异是什么?即使在阅读了上面链接的MSDN主题之后,我也不清楚.你什么时候真的使用一个而不是另一个?似乎我在log4net和上下文中看到的绝大多数引用/示例都是针对GDC(全局 - 我理解),NDC(嵌套 - 我也理解)和MDC.我在google搜索时可以找到LDC(或LogicalThreadContext)的大多数引用都与登录到log4net源代码库有关,而不是真实世界的用法.最不发达国家几乎从未提出问题或例子.
我确实找到了这个链接,提供了一些关于与log4net开发人员之一Nicko Cadell的差异的非常好的信息,但我仍然不清楚.
一个更大的问题,与log4net没有直接关系的是Thread.SetData和CallContext.SetData之间的实际区别是什么?
根据CallContext MSDN文章,CallContext数据可以传播到另一个AppDomain.要传播,存储在CallContext中的数据项必须公开ILogicalThreadAffinative接口.所以,这似乎是Thread.SetData和CallContext之间的一个区别.
根据Nicko Cadell链接,log4net不实现ILogicalThreadAffinative,因此不会传播LDC属性.
也许这里有足够的东西我应该能够回答我自己的问题,也许不是.我还在努力理解.
如果你使用log4net,你们每个人都使用MDC,LDC吗?如果你使用MDC,是因为大多数"真实世界"的例子似乎都在使用它吗?如果你使用LDC,你有特定的理由使用它吗?如果你同时使用它们,你如何选择何时使用哪个?
请注意,我已经看到一些关于MDC(也许是LDC)的文章可能因为线程切换而无法在ASP.net应用程序中正常工作.我对这个问题不是特别感兴趣,因为我不在ASP.net工作.
实际上,我在SO上发现了一些可能有助于讨论的有用帖子:
提前致谢!
我的问题被标记为这个问题的可能重复:如何在没有循环的情况下组合两个词典?
我相信我的问题是不同的,因为我问的是如何以特定的方式组合两个词典:我希望Dictionary1中的所有项目加上Dictionary2中不存在的所有项目(即密钥不存在).
我有两个这样的词典:
var d1 = new Dictionary<string,object>();
var d2 = new Dictionary<string,object>();
d1["a"] = 1;
d1["b"] = 2;
d1["c"] = 3;
d2["a"] = 11;
d2["e"] = 12;
d2["c"] = 13;
Run Code Online (Sandbox Code Playgroud)
我想将它们组合成一个新的词典(从技术上讲,它不必是一个字典,它可能只是一个序列KeyValuePairs),以便输出包含所有KeyValuePairs来自d1和只有KeyValuePairs,d2其Key不会出现在d1.
概念:
var d3 = d1.Concat(d2.Except(d1))
Run Code Online (Sandbox Code Playgroud)
但这给了我d1和d2的所有元素.
似乎它应该是显而易见的,但我必须遗漏一些东西.
(如标签所示,我使用的是moq).
我有这样的界面:
interface ISource
{
string Name { get; set; }
int Id { get; set; }
}
interface IExample
{
string Name { get; }
ISource Source { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
在我的应用程序中,IExample的具体实例接受DTO(IDataTransferObject)作为Source.IExample的具体实现的一些属性只是委托给Source.像这样...
class Example : IExample
{
IDataTransferObject Source { get; set; }
string Name { get { return _data.Name; } }
}
Run Code Online (Sandbox Code Playgroud)
我想创建的IExample的一个独立的模拟(独立的意思,我不能使用捕获变量,因为模拟的IExample的几个实例将在测试的过程中创建)并设置这样的模拟是IExample.Name返回的值IExample.Source.Name.所以,我想创建一个这样的模拟:
var example = new Mock<IExample>();
example.SetupProperty(ex => ex.Source);
example.SetupGet(ex => ex.Name).Returns(what can I put here to return ex.Source.Name);
Run Code Online (Sandbox Code Playgroud)
本质上,我想配置mock作为一个属性的值,返回mock的子对象的属性值.
谢谢.
我在日志记录中看到了许多其他问题.最佳做法.什么日志记录平台最好.等等.这里有一些关于SO的链接以及关于该主题的非常好的讨论:
开始编辑:
键入这篇长篇文章后,我想我想弄清楚的主要是WCF日志/跟踪和活动ID传播与System.Diagnostics和TraceSources的紧密耦合.您是否可以使用第三方日志记录平台(如log4net或NLog)获得"良好"的WCF日志记录/跟踪和活动ID传播.如果你这样做,你怎么做?
有关ServiceTraceViewer的一些问题,请参阅本文的底部,
结束编辑.
我的问题的主题在任何这些帖子中都没有详细讨论.我对人们在日志记录和WCF方面做了什么感兴趣.如果您正在处理包含WCF服务的项目并且已登录项目,那么您是否需要特别努力来使用特定于WCF的日志记录功能.特别是,您是否尝试合并活动跟踪,活动传播和端到端跟踪等内容?如MSDN 中的这篇文章所述. 这是MSDN关于传播活动的另一篇文章.
这篇文章很好地解释了如何使用System.Diagnostics TraceSources进行活动跟踪,活动传播和端到端跟踪.它显示了如何配置WCF以通过app.config/web.config文件"打开"这些选项.WCF在内部使用TraceSources来记录通信结果.
下面是一些示例代码(来自上面链接的第二篇MSDN文章),它或多或少地显示了如何通过System.Diagnostics和TraceSources实现活动传播:
TraceSource ts = new TraceSource("myUserTraceSource");
Guid oldID = Trace.CorrelationManager.ActivityId;
Guid traceID = Guid.NewGuid();
ts.TraceTransfer(0, "transfer", traceID);
Trace.CorrelationManager.ActivityId = traceID; // Trace is static
ts.TraceEvent(TraceEventType.Start, 0, "Add request");
double value1 = 100.00D;
double value2 = 15.99D;
ts.TraceInformation("Client sends message to Add " + value1 + ", " + value2);
double result = client.Add(value1, value2);
ts.TraceInformation("Client receives Add response '" + …Run Code Online (Sandbox Code Playgroud) 我正在使用C#和Visual Studio 2010.
当我OutputDebugString用来编写调试信息时,它应该出现在输出窗口中吗?
我可以OutputDebugString在DebugView中看到输出,但我想我会在Visual Studio的Output窗口中看到它.我看过菜单工具?选项?调试?常规,输出未被重定向到立即窗口.我也看了菜单工具*?选项?调试?输出窗口和所有常规输出设置都设置为"开".最后,我使用了Output窗口中的下拉列表来指定应该出现Debug消息.
如果我更改菜单工具*?选项?调试?一般将输出重定向到立即窗口,OutputDebugString消息不会出现在即时窗口中.
这是我的整个测试程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace OutputDebugString
{
class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern void OutputDebugString(string message);
static void Main(string[] args)
{
Console.WriteLine("Main - Enter - Console.WriteLine");
Debug.WriteLine("Main - Enter - Debug.WriteLine");
OutputDebugString("Main …Run Code Online (Sandbox Code Playgroud) 在NLog中是否可以创建具有不同配置的多个记录器?
我有一个组件,每次实例化必须将所有事件记录到与新实例相关的不同文件.
NLog可以实现吗?如果没有,那么有日志框架可以做到这一点吗?
我有两个对我的应用程序至关重要的库,这些库依赖于不同版本的log4net.dll.当我正在使用的第三方片段被调用时,尝试我的bin文件夹中的两个dll都会出现常见错误:
无法加载文件或程序集'log4net,Version = 1.2.10.0,Culture = neutral,PublicKeyToken = 1b44e1d426115821'或其依赖项之一.定位的程序集的清单定义与程序集引用不匹配.(HRESULT异常:0x80131040)
有没有办法让应用程序可以引用这两个dll?第三方dll是Windsor的IOC容器和Paypal的API,这两者对这个应用程序至关重要.
有关背景信息,请参阅此问题:
该问题询问Tasks如何影响Trace.CorrelationManager.ActivityId.@Greg Samson用测试程序回答了他自己的问题,显示ActivityId在Tasks的上下文中是可靠的.测试程序在Task委托的开头设置一个ActivityId,休眠以模拟工作,然后检查最后的ActivityId以确保它是相同的值(即它没有被另一个线程修改).该程序成功运行.
在研究线程,任务和并行操作的其他"上下文"选项(最终为日志提供更好的上下文)时,我遇到了Trace.CorrelationManager.LogicalOperationStack的一个奇怪问题(无论如何我都很奇怪).我在下面的问题中复制了我的"答案".
我认为它充分描述了我遇到的问题(Trace.CorrelationManager.LogicalOperationStack显然已经损坏 - 或者什么 - 当在Parallel.For的上下文中使用时,但只有当Parallel.For本身包含在逻辑操作中时) .
这是我的问题:
Trace.CorrelationManager.LogicalOperationStack应该可以与Parallel.For一起使用吗?如果是这样,如果一个逻辑操作已经与Parallel.For启动有效,它是否会有所不同?
是否有一种"正确"的方式使用LogicalOperationStack与Parallel.For?我能不同地对这个示例程序进行编码以使其"有效"吗?通过"工作",我的意思是LogicalOperationStack总是具有预期的条目数,并且条目本身是预期的条目.
我已经使用Threads和ThreadPool线程做了一些额外的测试,但是我必须返回并重试这些测试,看看我是否遇到了类似的问题.
我会说,看起来任务/并行线程和ThreadPool线程确实从父线程"继承"了Trace.CorrelationManager.ActivityId和Trace.CorrelationManager.LogicalOperationStack值.这是预期的,因为CorrelationManager使用CallContext的LogicalSetData方法(而不是SetData)存储这些值.
请再次参考此问题,以获取我在下面发布的"答案"的原始背景:
另请参阅Microsoft的Parallel Extensions论坛上的类似问题(目前尚未得到解答):
[开始粘贴]
请原谅我发布这个作为答案,因为它不是你的问题的真正答案,但是,它与你的问题有关,因为它处理CorrelationManager行为和线程/任务/等.我一直在寻找使用CorrelationManager LogicalOperationStack(和StartLogicalOperation/StopLogicalOperation方法)在多线程场景中提供额外的上下文.
我拿了你的例子并稍微修改它以增加使用Parallel.For并行执行工作的能力.另外,我用StartLogicalOperation/StopLogicalOperation括号(内部)DoLongRunningWork.从概念上讲,DoLongRunningWork每次执行时都会执行以下操作:
DoLongRunningWork
StartLogicalOperation
Thread.Sleep(3000)
StopLogicalOperation
Run Code Online (Sandbox Code Playgroud)
我发现如果我将这些逻辑操作添加到您的代码中(或多或少),所有逻辑操作都保持同步(始终是堆栈上预期的操作数,并且堆栈上的操作值始终为预期).
在我自己的一些测试中,我发现并非总是这样.逻辑操作堆栈正在"损坏".我能想到的最好的解释是,当"子"线程退出时,将CallContext信息"合并"回"父"线程上下文导致"旧"子线程上下文信息(逻辑操作)为"继承"由另一个兄弟姐妹线程.
问题也可能与Parallel.For显然使用主线程(至少在示例代码中,如编写)作为"工作线程"之一(或者在并行域中应该调用它们)之间的事实有关.每当执行DoLongRunningWork时,就会启动一个新的逻辑操作(在开始时)并停止(在结束时)(也就是说,将其推送到LogicalOperationStack并从中弹出).如果主线程已经有效的逻辑操作,并且DoLongRunningWork在主线程上执行,则启动新的逻辑操作,因此主线程的LogicalOperationStack现在具有两个操作.DoLongRunningWork的任何后续执行(只要DoLongRunningWork的这个"迭代"在主线程上执行)将(显然)继承主线程的LogicalOperationStack(现在它有两个操作,而不仅仅是一个预期的操作).
我花了很长时间才弄清楚为什么LogicalOperationStack的行为在我的示例中与我的示例的修改版本不同.最后我看到在我的代码中我将整个程序放在逻辑操作中,而在我的测试程序的修改版本中,我没有.这意味着在我的测试程序中,每次执行"工作"(类似于DoLongRunningWork)时,已经存在逻辑操作.在我的测试程序的修改版本中,我没有在逻辑操作中将整个程序括起来.
所以,当我修改你的测试程序以在逻辑操作中包含整个程序时如果我使用Parallel.For,我遇到了完全相同的问题.
使用上面的概念模型,这将成功运行:
Parallel.For
DoLongRunningWork
StartLogicalOperation
Sleep(3000)
StopLogicalOperation
Run Code Online (Sandbox Code Playgroud)
虽然这最终会因为LogicalOperationStack显然不同步而断言:
StartLogicalOperation
Parallel.For
DoLongRunningWork
StartLogicalOperation
Sleep(3000)
StopLogicalOperation
StopLogicalOperation
Run Code Online (Sandbox Code Playgroud)
这是我的示例程序.它类似于你的,因为它有一个DoLongRunningWork方法来操作ActivityId以及LogicalOperationStack.我也有两种踢DoLongRunningWork的方式.一种风味使用任务一使用Parallel.For.还可以执行每种风格,使得整个并行操作被包含在逻辑操作中或不包含在逻辑操作中.因此,总共有4种方法来执行并行操作.要尝试每个,只需取消注释所需的"使用..."方法,重新编译并运行. UseTasks,UseTasks(true)并且UseParallelFor应该全部运行完成. UseParallelFor(true)因为LogicalOperationStack没有预期的条目数,所以会在某些时候断言.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; …Run Code Online (Sandbox Code Playgroud) .net c# system.diagnostics parallel-extensions task-parallel-library