Go Functional Go Functional - 9 months ago 57
C# Question

Dynamically set log4net filename in a web service log

I'm using log4net in a web service and I want to set the log filename per request based on a user parameter sent in the request itself.

I've defined a property in the config

<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" />


and set it at the beginning of my web method

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 ->


I'm wrapping the F# library (SrvImplF.dll) in a C# WCF project (why?) and testing it with the WCF Test Client of Visual Studio.

But the log filename remains at its initial value
ClaimLog_(null).txt
even though I can set a breakpoint and see that
sel.User
is correctly filled

I've also tried with the AppSettings type provider from appSettings

<appSettings>
<add key="my_service_log" value="C:\projects\reports\ClaimRpt\Log\ClaimLog_{UserNameWCF}.txt" />
</appSettings>


by coding

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)


but - besides the fact that I need to create a fictitious app.config under the dll and duplicate the app key there because the type provider doesn't see the app.config of the wrapping project - it throws the exception


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.


I think this last is only a trivial error in my type provider configuration, anyway also directly entering the value

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


there is no exception but the log filename remains unchanged
ClaimLog_(null).txt

Answer Source

You have to call LogManager.GetLogger("ClaimServiceF") after setting the property.

Note that your approach is prone to race conditions (different requests happening at the same time). Consider using ThreadContext.