更新:
感谢您的澄清,我更好地理解您正在尝试做什么。您希望从IParameterInspector实现记录的消息反映出Example.MyService是您的服务(如instanceType参数所指示的)的“Example.MyService.GetContacts”的调用站点,并且“GetContacts”是操作。您可以手动合成呼叫站点信息。你仍然会使用NLog的Logger.Log方法,并且你仍然会创建一个LogEventInfo对象。此外,您可以将“类”和“方法”存储在LogEventInfo.Properties对象中。而不是根据instanceType(即服务)检索记录器(来自LogManager),根据参数检查器的类型(您的情况为NLogLogger)检索记录器。最后,您可以在NLog.config中添加一条附加规则(并将其应用于NLogLogger类型),以便规则具有不同的日志记录格式。您将在包含调用站点信息(存储在LogEventInfo.Properties集合中)的日志记录格式中手动添加一个字段,其位置与其他日志规则配置中的“真实”调用站点LayoutRenderer相同。
接下来我将发布一个新版本的NLogLogger实现,它可以完成我上面描述的任务。
public class NLogLogger : IParameterInspector
{
private static readonly NLog.Logger logger = LogManager.GetCurrentClassLogger();
private void Log(Type instanceType, string operationName, string msg)
{
NLog.Logger serviceLogger = LogManager.GetLogger(
instanceType.FullName, instanceType);
//Create LogEventInfo with the Logger.Name from the logger associated with the service
LogEventInfo le = new LogEventInfo(LogLevel.Info, serviceLogger.Name, msg);
le.Properties.Add("fakecallsite", string.Format("{0}.{1}",instanceType.ToString(),operationName);
//Log the message using the parameter inspector's logger.
logger.Log(typeof(NLogLogger), le);
}
public object BeforeCall(string operationName, object[] inputs)
{
// Retrieve the service instance type for the logger then log the call.
OperationContext operationContext = OperationContext.Current;
Type instanceType = operationContext.InstanceContext
.GetServiceInstance().GetType();
Log(instanceType, operationName, "BeforeCall");
return instanceType;
}
public void AfterCall(
string operationName, object[] outputs,
object returnValue, object correlationState
)
{
if (correlationState is Type)
Log(correlationState, operationName, "AfterCall");
}
}
你的NLog.config将有类似这样的规则。一条规则专门用于您的NLogLogger参数检查器。它记录到“f1”并且是“最终”规则,这意味着来自参数检查器的记录消息不会被任何其他规则记录。另一个规则适用于所有其他记录器。每个都记录到不同的文件目标,但是两个文件目标都写入同一个文件(我认为这是有效的)。关键是每个文件都有自己的布局。
<logger name="Your.Full.NameSpace.NLogLogger" minlevel="*" writeTo="f1" final="true" />
<logger name="*" minlevel="*" writeTo="f2" />
你的目标和布局看起来像这样。我们正在定义一个变量,其值是EventPropertiesLayoutRenderer的值,它是我们存储在LogEventInfo.Properties [“fakecallsite”]中的假呼叫站点。
<variable name="fakecallsite" value="${event-properties:fakecallsite}"/>
<variable name="f1layout" value="${longdate} | ${level} | ${logger} | ${fakecallsite} | ${message}"/>
<variable name="f2layout" value="${longdate} | ${level} | ${logger} | ${callsite} | ${message}"/>
<targets>
<target name="f1" xsi:type="File" layout="${f1layout}" fileName="${basedir}/${shortdate}.log" />
<target name="f2" xsi:type="File" layout="${f2layout}" fileName="${basedir}/${shortdate}.log" />
</targets>
请注意,我没有试过,但我认为它应该工作(或者应该是足够接近,你可以得到它的工作)。一个限制是,由于我们正在计算假呼叫站点,因此我们不能使用真实呼叫站点LayoutRenderer来处理输出中的fakecallsite字段的内容。如果这一点很重要,可以通过单独存储类和方法(在LogEventInfo.Properties中),然后在NLog.config中设置“fakecallsite”变量来包含类和/或方法来模拟。
END UPDATE
你的包装应该使用Log方法。另外,您传递给NLog Logger.Log方法的类型应该是NLog Logger包装器的类型,而不是服务实例类型的类型。您仍然可以使用您的服务实例的类型来检索正确的Logger实例。它应该看起来像这样:
public class NLogLogger : IParameterInspector
{
private void Log(Type instanceType, string operationName, string msg)
{
NLog.Logger logger = LogManager.GetLogger(
instanceType.FullName, instanceType);
//This is the key to preserving the call site in a wrapper. Create a LogEventInfo
//then use NLog's Logger.Log method to log the message, passing the type of your
//wrapper as the first argument.
LogEventInfo le = new LogEventInfo(LogLevel.Info, logger.Name, msg);
logger.Log(typeof(NLogLogger), le);
}
public object BeforeCall(string operationName, object[] inputs)
{
// Retrieve the service instance type for the logger then log the call.
OperationContext operationContext = OperationContext.Current;
Type instanceType = operationContext.InstanceContext
.GetServiceInstance().GetType();
Log(instanceType, operationName, "BeforeCall");
return instanceType;
}
public void AfterCall(
string operationName, object[] outputs,
object returnValue, object correlationState
)
{
if (correlationState is Type)
Log(correlationState, operationName, "AfterCall");
}
}
您需要使用Log方法。你不能编写一个包装器,只是委托给NLog的Info/Debug/Trace/etc方法。如果你再看看你发布的链接(对于我发布的回复),你会看到我使用Log方法展示了如何编写一个包装器(保存呼叫站点信息)。 – wageoghe
我尝试了不同的东西,其中还有'Log'方法和'LogEventInfo'类。问题是'NLogLogger'不是真正的包装。请重新阅读关于堆栈跟踪的内容。 – nalply
你说得对,我误解了你正在做的事。我会再添加一条建议。我不认为这是一个好的,但它可能会有所帮助。 – wageoghe