Given an assembly with 2 classes, Foo and Bar, via the configuration file, how to I enable logging at the Info Level for Foo, and the Warning level for Bar?
What configuration you use, depends on how the Logger is created. The basic configuration looks like this:
<logger name="(INSERT LOGGER NAME HERE)">
<level value="(WHATEVER LOG LEVEL TO APPLY FOR THIS PARTICULAR LOGGER)" />
</logger>
What determines your Logger Name is how you create it.
LogManager.GetCurrentClassLogger() // The name will be "namespace.name" of the current class.
LogManager.GetLogger<T>() // The name will be "namespace.name" of the Type T.
LogManager.GetLogger(Type type) // The name will be "namespace.name" of the type.
LogManager.GetLogger(string name) // The name will be name.
So if you have a particular process that you want to log in multiple classes / files, define the name of the process and use the string overload.
If you want to be able to turn on logging for a single class, use one of the other overloads.
Related
Let's say I have an app that accepts a commandline argument of the state code. Each state has it's own job so the app itself can be running many times at the same time but only ever once per state at a time. I want to use this commandline argument to create/write to a folder with it's same value for logging so that they aren't tripping over each other. So at the end I'd have:
logfiles/WI/logfile.log
logfiles/MN/logfile.log
logfiles/MI/logfile.log
Where WI, MN, MI were commandline arguments to the 1 app.
What I currently tried to do was use the formatting of string.Format() in the app.config and in code loop over and use that cmdline value to create the log folder by state name.
<param name="File" value="./logfiles/{0}/logfile.txt" />
public static void SetupLogFileVendorPath(string state)
{
foreach (var fileAppender in LogManager.GetRepository().GetAppenders().OfType<FileAppender>())
{
// apply transformation to the filename
fileAppender.File = string.Format(fileAppender.File, state);
// notify the logging subsystem of the configuration change
fileAppender.ActivateOptions();
}
}
This sort of works as it does create the state folder and the log file in it, but it also creates a folder named:
logfiles/{0}/logfile.txt
Because it does this if 2 instances happen to run at the same exact time it could create an issue as both try to write to this file under {0} which they all do initially once the line LogManager.GetRepository().GetAppenders() is called. They just put header/footer but it does write to this one central place which is what I'm trying to avoid.
Is there any way to get the structure I'm looking for while avoiding this {0} with the way I'm doing it with string.Format()
Hook the state code commandline argument to a Log4net context property.
static void Main(string[] args)
{
string stateCode = args[0];
log4net.GlobalContext.Properties["stateCode"] = stateCode;
// ...
}
Include this context property in the output file path of your (Rolling)FileAppender configuration via %property{stateCode}.
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file type="log4net.Util.PatternString" value="logfiles\%property{stateCode}\logfile.log" />
<!-- More settings here -->
</parameters>
Doing so will create a subfolder with the name of the passed commandline argument.
I've got a legacy logging class. Its got a static Logger reference (named logger) and a bunch of static methods.
Each method takes a String input and writes it to System.out.println and to logger if logger is not null.
Its got a constructor that initializes logger. But this constructor only has package scope and I'm pretty sure its not being called anywhere. Therefore logger is always null and the class essentially only ever executes System.out.println
I want to change this so it can be used in a multi threaded application where each thread writes to its own unique FileAppender.
And that's where I'm stuck.
Basically, what I want to do is have this static class associated with a bunch of different log4j FileAppenders. Each FileAppender can be created by the Thread, and the file name can be derived from unique information known to the Thread.
What I can't figure out how to do is magically use Log4j to communicate that Thread's unique FileAppender to this legacy logging class.
Ideas? Hints? Suggestions?
Mark
It is possible to change the target log file name dynamically using a RoutingAppender and the ThreadContext map.
This can all be done with configuration (no need for custom code where threads create FileAppenders). In the RoutingAppender configuration you can specify a ThreadContext key you want to use to switch the target FileAppender. For example, you can use the string "ROUTINGKEY". Each thread puts a unique value in the ThreadContext map for key "ROUTINGKEY", and this value is used to select the Appender that the log event is routed to. You can even set it up to dynamically create log files that have the ROUTINGKEY value in the file name, so not all target log files need to be known in advance.
The FAQ page has a good example: http://logging.apache.org/log4j/2.x/faq.html#separate_log_files
For the MvcSiteMapProvider v4, I've written a custom sitemap url resolver by overriding SiteMapNodeUrlResolverBase.
But I don't know how to register it in the MvcSiteMapProviderRegistry to make sure that a node's Url is always resolved using my own SiteMapNodeUrlResolver.
I expected something like:
this.For<ISiteMapNodeUrlResolver>().Use<MyUrlResolver>();
But this doesn't work, how should I do this?
The SiteMapNodeUrlResolvers (along with visibility providers and dynamic node providers) use a strategy pattern so you can wire up multiple instances and then refer to each instance by name. That name is then used by the AppliesTo() method to determine which URL resolver to use for each node.
If you inherit from SiteMapNodeUrlResolverBase rather than implement ISiteMapNodeUrlResolver, the default implementation of AppliesTo() will work in most cases. Then, this line (which is already included in the module by default) will automatically wire up all SiteMapNodeUrlResolvers:
// Multiple implementations of strategy based extension points
CommonConventions.RegisterAllImplementationsOfInterface(
(interfaceType, implementationType) => this.For(interfaceType).Singleton().Use(implementationType),
multipleImplementationTypes,
allAssemblies,
excludeTypes,
"^Composite");
By default, it only scans MvcSiteMapProvider.dll and your MVC project. If you have your URL resolver defined in a separate assembly, you will need to modify the allAssemblies variable to ensure that it includes your custom assembly.
Once it is loaded, then you need to call it by name. The default implementation uses the "ShortAssemblyQualifiedName", which is the same string that you would normally use to refer to a type in a configuration file (as long as your assembly is not strong named).
<mvcSiteMapNode title="Home" action="Index" controller="Home" urlResolver="MyNamespace.MySiteMapNodeUrlResolver, MyAssembly" />
The urlResolver property/attribute must be set on every node you wish to override the default implementation on.
If you prefer, you can implement the AppliesTo() method yourself so you can shorten the amount of configuration that is required. Note this will only work when using an external DI container because the internal DI container uses the type names from the configuration to instantiate the objects.
public override bool AppliesTo(string providerName)
{
return "myUrlResolver".Equals(providerName, StringComparison.InvariantCulture);
}
<mvcSiteMapNode title="Home" action="Index" controller="Home" urlResolver="myUrlResolver" />
I have an API that uses log4j for logging. When I have used the API in my project, though log statements related to project printed with ontl method name, but log statements coming from API is printed full package name format.
In log4j.properties file I am using "%c" (lowercase).
How I can force all project log statements get printed only method name.
Lets say;
I have two classes Main.java and AlarmCategoryImpl.java
AlarmCategroryImpl.java is located on API class, Main.java is defined in my project class.
static Logger logger = Logger.getLogger(AlarmCategoryImpl.class);
static Logger logger = Logger.getLogger(Main.class);
and its log4j output.
2012-12-01/18:13:22.220/EET [INFO][Main->main] starting...
2012-12-01/18:13:22.447/EET [INFO][com.monitor.base.alarmmanagement.alarmconfigurationImpl.AlarmCategoryImpl->copyStructureRecursive] Copying AlarmCategoryImpl
%c means "category name", which is synonymous to "logger name". That means that %c will be expanded to the logger's name.
The logger name is not necessarily the fully-qualified class name. The logger name is the string that is passed in to Logger.getLogger(). Therefore, if you have a class named x.y.z.MyClass, and it has this:
private static final Logger logger = Logger.getLogger("hello");
Then log statements will be generated with hello expanded instead of %c.
That means that the classes in your API are using getLogger(), passing the class name as a parameter. That causes %c to be expanded to the fully-qualified class name when the logs print.
I'm guessing that your non-API classes (in other words, your own project's classes) don't pass-in any value to Logger.getLogger(), or perhaps they use the root logger. To be sure, paste here the line of your code that retrieves the Logger instance.
EDIT as per comment:
Well, is it possible that your Main class is inside the default package? (that is, it is not associated with any package)? If yes, then I don't see any problem.
[INFO][Main->main]: INFO is the level, Main is the class, main is the method.
[INFO][com.monitor.base.alarmmanagement.alarmconfigurationImpl.AlarmCategoryImpl->copyStructureRecursive]: INFO is the level, com.monitor.base.alarmmanagement.alarmconfigurationImpl.AlarmCategoryImpl is the class, copyStructureRecursive is the method.
We have a modular application where modules have their own log4j logs (i.e. communication log and error log). The appenders and categories for these are all configured in the core log4j XML, but not all modules are always installed.
The DailyRollingFileAppender creates its file regardless of use and that exposes the full set of modules although not present and as some of them are customer specific we'd like to hide logs not in use.
Is there a way to make DailyRollingFileAppender create its file on first use instead of automatically at startup?
I had the same problem, so I have extended the standard FileAppender class and I have created a new LazyFileAppender that is a FileAppender that lazily initialize the log file(creates it only when the first write operation happens).
The LazyFileAppender and some other additions to the standard log4j library can be found into a simple library that I have created : log4j-additions .
You can look at the source to develop your own extension or you can use it as is ...
In Log4j 2, both FileAppender and RollingFileAppender has the parameter "createOnDemand" which can be used to configure to create the log file only when a log event passed to the appender.
Example:
<RollingFile name="LogFile" fileName="test.log" filePattern="test-%i.log.gz" createOnDemand="true">
<Policies>
<SizeBasedTriggeringPolicy size="1MB"/>
</Policies>
<DefaultRolloverStrategy max="5"/>
</RollingFile>
More details here: https://logging.apache.org/log4j/2.x/manual/appenders.html#RollingRandomAccessFileAppender
The file appenders have no option to lazily create the log files - the setFile method automatically creates the file if it doesn't already exist: ostream = new FileOutputStream(fileName, append);
You'll have to extend the appender and overwrite the file initialisation code yourself to get the behaviour you're after.
Extend the standard FileAppender class was unsuccessful for me. So I have found an other solution using appenders programmatically to create log files on demand only (and with timestamp in the name file). I have written these two methods :
public void startLog() {
SimpleDateFormat sdf_long = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");
FileAppender fa = new FileAppender();
fa.setName("foo");
fa.setFile(sdf_long.format(new Date()) + ".log");
fa.setLayout(new PatternLayout("%d{HH:mm:ss.SSS} %m%n"));
fa.setThreshold(Level.DEBUG);
fa.setAppend(true);
fa.activateOptions();
Logger.getRootLogger().addAppender(fa);
}
public void stopLog() {
Logger.getRootLogger().getAppender("foo").close();
Logger.getRootLogger().removeAppender("foo");
}
My log4j.properties file only configures the console appender. When I want to start logging I call the startLog() method. When I want to log in an other file I call stopLog() first and then startLog() method.