Transformation best practice - log4net.config (or any non web/app config)

So, I know this is documented, but I find it quite confusing. And I know this question has been asked multiple times - but I’m still not clear on the answer - my apologies.

I have an externalised log4net.config. I want to be able to change the path per environment, and ideally I’d like to be able to do this without a code/source change - i.e. a prod support staff member should be able to change the location of the log file without asking a dev to make a change, commit, build, deploy, unit test, integration test, promote to test, promote to UAT and finally get the change in. However this file is actually used by our developers in their dev environment, so I can’t put octopus variables in them.

How should I best do this?

Should I actually have a transform in the solution/source which converts the value of the path to an octopus variable on deployment, and then due to the ordering of config file transformations/processing the variable will then be substituted? Any advice appreciated.

Hi,

Thanks for getting in touch!

I assume the reference to your log4net.config file is in a web.config or app.config file? If so, there’s a good example on StackOverflow for using the <appSettings> element to point at an external file. If you can do that, you should be able to use the Configuration Variables feature to match a scoped variable to the appSettings element.

For example, your web.config file might look like this:

<appSettings>
    <add key="log4net.Config" value="log4net.config" />
<appSettings>

If you turned on the Configuration Variables feature and set a variable called log4net.Config with a different value for different environments, it should replace that value in the web.config. For example, your log4net.Config variable could have a value of configs\log4net-dev.config scoped to your Development environment. That appSetting would then be replaced accordingly when deploying to Development.

I hope that helps!

Damo

Thanks for the prompt response Damo - while this would solve the current problem I have, I’m interested in what you’d advise to resolve the specifics. If (for whatever reason) I didn’t want to maintain different log4net.configs, but instead I wanted one log4net.config and I wanted to replace the value of the file node in the following xml fragment. How would you recommend I do this? If my goal was to allow this one value to be maintained as an Octopus variable, so that the configuration could be centralised in Octopus, only accessible to prod support staff, not dev staff.

<?xml version="1.0"?>
<log4net debug="false">
  <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="c:\\Logs\\Log.txt"/>
    <appendToFile value="true"/>

Hi,

Thanks for the response!

If I’m interpreting correctly, I think the best solution is probably a combination of the Configuration transforms and Substitute variables in files features. It seems a bit complex, but it’s a really common scenario for a situation like this. :slight_smile:

First, you’d need a single transformation file called (for example) Log4Net.DeploymentTransform.config. This transformation would simply target the <file> element, replacing the value with an Octopus replacement variable. Based on the fragment you’ve given me, I’ll assume your original log4net config file looks something like this:

<?xml version="1.0"?>
<log4net debug="false">
  <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="c:\\Logs\\Log.txt"/>
    <appendToFile value="true"/>
  </appender>
</log4net>

Your transform file might contain this (note the #{LogFileLocation} variable):

<?xml version="1.0"?>
<log4net debug="false" xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender" xdt:Transform="Replace" xdt:Match="name">
    <file value="#{LogFileLocation}" />
    <appendToFile value="true" />
  </appender>
</log4net>

You could then turn on the “Substitute variables in files” feature, and point at the Log4Net.DeploymentTransform.config transformation file (variable replacement happens before transformation). That would result in the #{LogFileLocation} variable being replaced with whatever value was set for your LogFileLocation variable in the current scope.

You’d also turn on the Configuration transforms feature, and fill out the Additional transforms section in the Configuration Transforms feature to identify your transform file (e.g. Log4Net.DeploymentTransform.config => log4net.config).

So the variable replacement happens in the transform file, then the transformation happens. This should result in a log4net.config file with this:

<?xml version="1.0"?>
<log4net debug="false">
  <appender name="RollingLogFileAppender"><file value="ThisIsTheValueOfMyVariable" /><appendToFile value="true" /></appender>
</log4net>

I hope this makes sense!

Let me know how you go,
Damo