My application can be started both as a Windows Service and in console environment. For each case, I need some log4net appenders to be active (or not, respectively) as well as some which are active in both cases. (Service: RollingFileAppender, DebugAppender (if enabled), EventLogAppender | Console: ColoredConsoleAppender, DebugAppender (if enabled))
The only way to achieve something similar is using the PropertyFilter together with ThreadContext.Properties like so:
<filter type="log4net.Filter.PropertyFilter">
<key value="ApplicationMode" />
<stringToMatch value="Service" />
</filter>
if(!Environment.UserInteractive)
ThreadContext.Properties["ApplicationMode"] = "Service";
However, since the property is declared on the thread context, it only works on the current thread. If the thread changes, the configuration is being reset and I have to declare it again.
Does log4net support a way to declare a PropertyFilter in configuration to setup the desired environment automatically? Like this:
<filter type="log4net.Filter.PropertyFilter">
<key value="{Environment.UserInteractive}" />
<stringToMatch value="false" />
</filter>
Or... is there a better approach? Since I didn't found a solution yet.. is this an uncommon practice?
See the last part of my answer to this question:
Capture username with log4net
To summarize, you can implement an object that contains the logic that you have above and put that object in GlobalContext.Properties. When log4net retrieves the value (your object) from the Properties, it will call ToString to get the actual value. Put your logic inside ToString.
Maybe something like this:
public class ApplicationModeProvider
{
public override string ToString()
{
return Environment.UserInteractive ? "Console" : "Service";
}
}
Put it in your dictionary at startup:
GlobalContext.Properties["ApplicationMode"] = new ApplicationModeProvider();
In effect, this is sort of like adding
if(!Environment.UserInteractive)
ThreadContext.Properties["ApplicationMode"] = "Service";
before ever logging statement.
I'm not sure, but I think that then you can configure your filter as you describe in your post.
Related
I want to enable developers to log objects as JSON with NLog. To do this I need to implement some logic before sending to nLog OR before sending to target.
I can build my own Target(TargetWithLayout) but I canĀ“t find a way to check the log level from the config for this specific target/logger? Another drawback is that I need to make a new TargetWithLayout class for each target that we will use (EventLog, File, WebService and so on).
Another solution would be to do it in my LogHandler that uses NLog. The only way to know if I should translate the object is probably to read all the loggers from the config file, if any of them is set to log objects then I serialize. I am however not sure if I can check this information from the LogHandler (without doing it manually)?
You can use the NLog-Logger object to query active logging-rules:
if (myLogger.IsTraceEnabled)
myLogger.Trace("Hello World");
You can use the NLog json layout to write json in you log files, no need to check and do the serialization yourself:
<target name="jsonFile" xsi:type="File" fileName="${logFileNamePrefix}.json">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>
The log messages formatting is handled by NLog instead of doing it yourself.
release notes nlog
To add some theory ;)
Another drawback is that I need to make a new TargetWithLayout class for each target that we will use (EventLog, File, WebService and so on).
That's the reasons there are Layouts in NLog. Those are the layouts that could be used in the target, but those are independent of the target.
(don't get confused with Layout Renderers, those ${..} things.)
There are multiple layouts (plain text, CSV, JSON) (see list) , and you could easily add your own layout, analogous to adding a custom Target / Layout renderer, see the wiki
I have simple C# console application:
static int main(string[] args){
return SomeBoolMethod() ? 1:0;
}
How in WiX 2.0 should I define property and set this value to it? I don't care about future upgrade/uninstall
UPD
I want latter use this property in condition: so the group B will not execute if MYPROPERTY == 0 but all further components in feature F_A will
I.E.
<Feature Id="F_A" Level="1">
<Condition Level="0">NOT INSTALLED</Condition> <!-- Another custom property -->
<ComponentGroupRef Id="B" />
<ComponentRef Id="C_AnotherComponent" />
</Feature>
<ComponentGroup Id="B">
<Condition Level="0">NOT MYPROPERTY</Condition> <!-- property that set in console-->
<ComponentRef Id="C_ComponentName" />
</ComponentGroup>
TIA
Executables that are run as Custom Actions do not have access to the Installation session, so they cannot modify a property. Ideally you would rewrite the code that's currently in an exe to reside in a dll, so it could be called as a dll custom action.
If you cannot rewrite things (perhaps you received the exe from elsewhere), you would need to write a dll custom action that launches the exe, examines its exit code, and sets properties accordingly.
In neither case do you need to predefine the property; you can just set it in the dll custom action. In both cases, if the dll is written in C# you will have to use a technology like DTF to invoke it, as Windows Installer cannot directly invoke managed code.
Let's say i have 3 smtp appenders in same log4net file whose names are:
<appender name = "emailDevelopment".. />
<appender name = "emailBeta".. />
<appender name = "emailProduction".. />
Let's say i have 3 different servers(Dev, Beta, Production). Depending upon the server, i want to fire the log. In case of Development server, it would fire log from "emailDevelopment". I have a system variable in each server named "ApplicationEnvironment" whose value is Development, Beta, Production based on the server names. Now is there anyway i can setup root in log4net so that it fires email depending upon the server name.
<root>
<priority value="ALL" />
<appender-ref ref="email<environment name from whose appender should be used>" />
</root>
This doesn't directly answer your question, but another approach is to simply have multiple log4net configuration files and call XmlConfigurator.Configure() on the right one. For example, you might have Logging.Development.Config, Logging.Beta.Config and so on.
Somewhere in code, you determine the "environment" and configure using the file you want.
I've even gone so far as to have multiple config files and pull different parts of them out into a single XML representing the "true" config, and then calling the Configure() method on that. For example, Logging.Appenders.Config which has all the appenders, and takes all of them and combines it with one of your environment-specific config files above; the environment-specific ones simply reference what they need, and the rest are effectively inactive/unreferenced for that environment.
Even after having written the only XSD file for log4net configuration I'm still not aware of an easy way to achieve this.
You might be able to do something like:
log4net.GlobalContext.Properties["host"] = new ClassThatToStringsHost();
class ClassThatToStringsHost
{ public override string ToString() { return "whatever"; } }
Now you can reference this value from the Log format with: "%property{host}"
To perform the filtering you will need to use a filter configuration in the adapter(s):
<appender name="file" type="log4net.Appender.RollingFileAppender">
<filter type="log4net.Filter.PropertyFilter">
<Key value="host" />
<StringToMatch value="whatever" />
</filter>
<!-- Anything not accepted by the above should be excluded -->
<filter type="log4net.Filter.DenyAllFilter" />
</appender>
There may even be a built-in property you could leverage and this should work. See also this post: http://geekswithblogs.net/rgupta/archive/2009/03/03/dynamic-log-filenames-with-log4net.aspx
For me, myself, and I... I would approach it another way all together. I would derive my own SMTP appender from the default and in the ActivateOptions() method I'd configure the values according to the environment. This would allow you to use one SMTP appender with consistent rules and yet provide three public properties for each of the email addresses you want to send from. It's not hard, give it a try!
I have a solution which has multiple output projects (a website, and admin tool, and a SOAP API layer).
They each share common projects in the solution (the service layer, data layer etc). In one of these common projects, I am looking to store a config layer.
Right now, we have three seperate appsettings config files for each output project -
development.AppSettings.config
testing.AppSettings.config
production.AppSettings.config
So altogether, there are nine config files. Only one is used in each project, as they are referenced by utilising the configSource attribute in the web.config appsettings node.
Anyhoo, it's getting to be a pain any time we want to add/remove values from our config files, because it means that we have to change all nine files to do this. And here's what I'd like to do:
In the common project, we have three config files as above. These would be set to copy to the output directory, so that each project has a copy of them. These would be the 'base' config.
Then in each project, I would like to have three files again, but they wouldn't necessarily have to contain the same values as the base configs. If they did however, then the base config value would be overridden by the value in the output project config. A form of configuration inheritance, I suppose.
On application start, I'd like to be able to get these two config files - the base config, and the project config file. And then set the app settings accordingly.
What I'm wondering though, is what's a nice way of determining which file to use? Also, I'm wondering if this is a good way of sharing application values across a large solution, and if there's another, perhaps more efficient way of doing it?
If I'm in development mode, then I don't want production.appsettings.config, and vice versa if I'm in production mode.
Is there a simple way to get the mode (development/testing/production) that I'm in before I go off and get the configurations?
You can have one set of files (3 configs) and link/share them in whatever projects you need.
http://www.devx.com/vb2themax/Tip/18855
Hope this helps.
You could use the ConfigurationManager.OpenExeConfiguration static method. This will allow you to work with as many config files as you want.
You may also try creating a custom class to store all of your settings. You could then serialize your object to save it as a file. You could extend your base custom config class to fit all your other projects.
After some careful thought, and a trip to the toilet at 03:30, I came across a solution which works.
Let's say that we have some appSettings in our base config file:
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="MyValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
And in my output project, I have three appSettings:
<!-- This is used to identify which config to use. -->
<add key="Config" value="Development" />
<!-- Different value to the one in the base -->
<add key="MyKey2" value="NewValue2" />
<!-- This key does not exist in the base config -->
<add key="MyKey6" value="MyValue6" />
In my Application_Start, I have a call to GetConfigs():
ConfigHelper.GetConfig(HostingEnvironment.MapPath("~/bin/BaseConfig"));
And the actual GetConfigs function:
public static void GetConfigs()
{
if (configMode == null)
{
configMode = ConfigurationManager.AppSettings.Get("Config").ToLowerInvariant();
}
//Now load the app settings file and retrieve all the config values.
var config = XElement.Load(#"{0}\AppSettings.{1}.config".FormatWith(directory, configMode))
.Elements("add")
.Select(x => new { Key = x.Attribute("key").Value, Value = x.Attribute("value").Value })
//If the current application instance does not contain this key in the config, then add it.
//This way, we create a form of configuration inheritance.
.Where(x => ConfigurationManager.AppSettings.Get(x.Key) == null);
foreach (var configSetting in config)
{
ConfigurationManager.AppSettings.Set(configSetting.Key, configSetting.Value);
}
}
Now, my output project effectively has the following configuration settings:
<add key="Config" value="Development" />
<add key="MyKey1" value="MyValue1" />
<add key="MyKey2" value="NewValue2" />
<!-- And so on... -->
<add key="MyKey5" value="MyValue5" />
<add key="MyKey6" value="MyValue6" />
Simples!
I have the following two profile properties in my web.config file between the System.Web tags:
<anonymousIdentification enabled="true"/>
<profile>
<properties>
<add name="CustomerName" allowAnonymous="true"/>
<add name="CustomerID" allowAnonymous="true"/>
</properties>
</profile>
When I try to access the Profile.CustomerName or Profile.CustomerID from an aspx.cs file, it doesn't show up. I thought when you create a custom profile property, it automatically updates the aspnetdb with the new properties. Profile doesn't show up in intellisense as well.
This problem occurs if you are creating a Web Application. Web Apps, unlike Websites, don't generate the ProfileCommon class.
You could either follow this method:
http://webproject.scottgu.com/CSharp/Migration2/Migration2.aspx
(Appendix 2)
I haven't personally used the above assembly, so someone else might give some advice on that.
However, you could also try:
Object customerName = HttpContext.Current.Profile.GetPropertyValue("CustomerName")
You won't get intellisense with this method, but as long as you know the property name, it can be accessed like that.
Specify properties in web.config file as follows : -
Now, write the following code : -
Compile and run the code. You will get following output: -