在 Web 服务日志中动态设置 log4net 文件名
Dynamically set log4net filename in a web service log
我在 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" />
并将其设置在我的网络方法的开头
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 项目 (why?) 中,并使用 Visual Studio.[=23= 的 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.config -它抛出异常
Exception thrown: 'System.TypeInitializationException' in SrvImplF.dll
An exception of type 'System.TypeInitializationException' occurred in
SrvImplF.dll but was not handled in user code Additional information:
The type initializer for
'FSharp.Configuration.AppSettingsTypeProvider' threw an exception.
我认为最后这只是我的类型提供程序配置中的一个小错误,反正也是直接输入值
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 其中 'attaches' 或 'scopes' 属性,因为每个请求都由单独的(合并、重用)处理) 线。
如果您使用 async
/await
设置 属性 并在同一线程中获取记录器,请格外注意。
我在 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" />
并将其设置在我的网络方法的开头
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 项目 (why?) 中,并使用 Visual Studio.[=23= 的 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.config -它抛出异常
Exception thrown: 'System.TypeInitializationException' in SrvImplF.dll An exception of type 'System.TypeInitializationException' occurred in SrvImplF.dll but was not handled in user code Additional information: The type initializer for 'FSharp.Configuration.AppSettingsTypeProvider' threw an exception.
我认为最后这只是我的类型提供程序配置中的一个小错误,反正也是直接输入值
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 其中 'attaches' 或 'scopes' 属性,因为每个请求都由单独的(合并、重用)处理) 线。
如果您使用 async
/await
设置 属性 并在同一线程中获取记录器,请格外注意。