动态设置web服务日志中的log4net文件名
本文关键字:log4net 文件名 日志 设置 web 服务 动态 | 更新日期: 2023-09-27 18:16:31
我在web服务中使用log4net,我想根据请求本身发送的用户参数设置每个请求的日志文件名。
我在配置
中定义了一个属性<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<appender name="ClaimLog" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="C:'projects'reports'ClaimRpt'Log'ClaimLog_%property{UserNameWCF}.txt" />
并将其设置在我的web方法
的开头type ClaimServiceF() =
let log = LogManager.GetLogger("ClaimServiceF")
…
member this.BeginReport(sel: Model.Selection) : Model.RptResponse =
GlobalContext.Properties.Item("UserNameWCF") <- sel.User
let rptResp = new Model.RptResponse()
log.Info("Started")
match sel with
| null ->
我将f#库(SrvImplF.dll)包装在c# WCF项目中(为什么?)并使用Visual Studio的WCF测试客户端进行测试。
但是日志文件名仍然保持其初始值ClaimLog_(null).txt
,即使我可以设置一个断点并看到sel.User
被正确填充
我也尝试了从AppSettings
的AppSettings类型提供程序 <appSettings>
<add key="my_service_log" value="C:'projects'reports'ClaimRpt'Log'ClaimLog_{UserNameWCF}.txt" />
</appSettings>
由编码open FSharp.Configuration
type Settings = AppSettings<"app.config">
…
let appender = LogManager.GetRepository().GetAppenders() |> Seq.find(fun x -> x.Name.Equals("ClaimLog") ) :?> log4net.Appender.RollingFileAppender
…
appender.File <- Settings.MyServiceLog.Replace("{UserNameWCF}",sel.User)
但是-除了我需要在dll下创建一个虚构的app.config并在那里复制app键的事实之外,因为类型提供程序没有看到包装项目的app.config -它抛出异常
抛出异常:'System. '在SrvImplF.dll中的TypeInitializationException类型为"System"的异常。TypeInitializationException'发生在SrvImplF.dll,但未在用户代码中处理。的类型初始化项"FSharp.Configuration。AppSettingsTypeProvider'抛出异常。
我认为最后这只是我的类型提供程序配置中的一个小错误,无论如何也直接输入值
let configFile = "C:'reports'ClaimRpt'Log'ClaimLog_{UserNameWCF}.txt" //Settings.MyServiceLog
appender.File <- configFile.Replace("{UserNameWCF}",sel.User)
没有例外,但日志文件名保持不变ClaimLog_(null).txt
必须在设置属性后调用LogManager.GetLogger("ClaimServiceF")
请注意,您的方法容易出现竞争条件。如果多个客户机同时访问您的应用程序,您可能最终会记录到错误的文件。例如,假设两个并行请求的执行顺序如下:
// incoming request A
GlobalContext.Properties.Item("UserNameWCF") <- sel.User
// incoming request B
GlobalContext.Properties.Item("UserNameWCF") <- sel.User
// request A
var logger = LogManager.GetLogger("ClaimServiceF")
// logger has 'UserNameWCF' set to 'sel.User' of request B
要解决这个问题,可以考虑使用ThreadContext,它将属性"附加"或"作用域"到一个请求,因为每个请求都由一个单独的(池化的,重用的)线程处理。如果您使用async
/await
来设置属性并在同一线程中获取记录器,则要格外注意。