在Web服务日志中动态设置log4net文件名

时间:2016-11-04 13:05:07

标签: c# wcf f# log4net

我在网络服务中使用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的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 - 它会抛出异常

  

抛出异常:&#39; System.TypeInitializationException&#39;在SrvImplF.dll中   类型&#39; System.TypeInitializationException&#39;的例外情况发生在   SrvImplF.dll但未在用户代码中处理附加信息:   类型初始值设定项   &#39; FSharp.Configuration.AppSettingsTypeProvider&#39;抛出异常。

我认为这最后只是我的类型提供程序配置中的一个微不足道的错误,无论如何也直接输入值

        let configFile = "C:\reports\ClaimRpt\Log\ClaimLog_{UserNameWCF}.txt" //Settings.MyServiceLog
        appender.File <- configFile.Replace("{UserNameWCF}",sel.User)

没有例外,但日志文件名保持不变ClaimLog_(null).txt

1 个答案:

答案 0 :(得分:2)

在设置属性后,您必须致电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附加&#39;或者&#39;范围&#39;请求的属性,因为每个请求都由一个单独的(池化,重用)线程处理。 如果您使用async / await来设置属性并在同一个线程中获取记录器,请格外注意。