Go Functional Go Functional - 1 month ago 21
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

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.