I have some trouble.
My application could be divided to 3 logical parts (import, processing and export). There are some parts of code which are used in several parts of my application. How can I determine which part of code called my log4net object?
What is best practice to log info in parts of code which are called from several places in the application?
I want to turn on and off the ability to log parts of my application from a config file.
If I turn off logging for the processing part of my app, how could I log info in the export part of my app when both of them use one method, in which I initialize my logger object?
You could add a separate logger for each section of your app that you want to log and then turn them off and on as needed. They would all be independent from one another and this can all be setup via the config.
By setting the additivity property to false, the loggers will all be independent of one another. Here's an example of the config portion:
<logger name="Logger1" additivity="false">
<level value="INFO" />
<appender-ref ref="Logger1File" />
</logger>
To use it in your code, reference it like this:
private static ILog _Logger1= LogManager.GetLogger("Logger1");
Anything you log to Logger1 will be separate from any other logger, including the root one.
log4net provides contexts for this purpose. I would suggest using a context stack like this:
using(log4net.ThreadContext.Stacks["Part"].Push("Import"))
log.Info("Message during importing");
using(log4net.ThreadContext.Stacks["Part"].Push("Processing"))
log.Info("Message during processing");
using(log4net.ThreadContext.Stacks["Part"].Push("Export"))
log.Info("Message during exporting");
The value on the stack can be shown in the logs by including %property{Part} in a PatternLayout.
Related
In my play application, I have provided a logback.xml. The configuration for controller AnswerController is <logger name="controllers.AnswerController" level="TRACE" additivity="false">
<appender-ref ref="STDOUT"/> </logger>
But when I run my test case, I don't see any logs. Do I need to configure logback explicitly? I am using custom application loader.
Why are the logs now showing?
I added the LoggerConfigurator to my code and it seem to work (still early day to confirm if this will create some side effect. Though I don't the flow of why this makes things work (BTW, I do the same for my customer application loader as well)
class AnswerControllerUnitSpec extends PlaySpec with BeforeAndAfterAll with BeforeAndAfterEach with OneAppPerSuiteWithComponents{
override def beforeEach() = {
println("------------new test -----------------")
}
LoggerConfigurator(context.environment.classLoader).foreach {
_.configure(context.environment, context.initialConfiguration, Map.empty)
}
...
}
I am writing a new micro service using gRPC. Traditionally, when logging I used to populate the trace Id in MDC of logback and the logback configuration directly takes care of printing the trace id in all the log statements. With gRPC I am making use of Context for setting the trace id, but couldn't figure out a way to directly log them using the logback config file.
I have figured that Context is the right place to do this from this link How to intercept the headers from in call to one service and insert it to another request in gRPC-java?
Find below the pattern that I use to print values from MDC of logback in java projects.
<Pattern>%date{dd-MM-yyyy;HH:mm:ss.SSS}|[%mdc{CLIENT-ID}]|[%mdc{REQ-ID}]|[%thread] %-5level %logger{36} - %msg%n
</Pattern>
Is there a way to print the values from the Context into log statements directly like above? Is this even the right way to think about logging trace id in the logs when dealing with gRPC
Yes, you are on the right track by using the context. The way to do it is to write a custom layout (subclass LayoutBase<ILoggingEvent>) that will query the context and write it in the log. The code to query the context is:
Span span = ContextUtils.getValue(Context.current());
then to convert it to a string:
span.getContext().getTraceId().toLowerBase16()
When I use logback as logging framework, I add logger configuration on runtime for debugging. For example set "org.springframework" to debug level.
after I find the data I need I want to remove this configuration without restarting the application.
How can I do that?
You have two options depending upon how you want to modify the log level.
First option - using logback.xml
Logback xml provides an option to periodically scan the configuration. By setting that, you ask the logger framework to periodically update its in-memory setting to sync with the XML configuration. When you want to enable debug mode, XML configuration will look as below:
<configuration scan="true" scanPeriod="30 seconds">
<!-- Appender configurations here -->
<logger name="org.springframework" level="debug" additivity="false">
<appender-ref ref="YOUR_APPENDER_NAME"/>
</logger>
<!-- Other logger configurations (including root) go here -->
</configuration>
After you complete your task, you can either delete that logger entry or set the level to ERROR to stop logging for that package.
Second option - In-memory
If you don't want to touch your logback.xml, but somehow handle it in runtime (say, using some REST APIs of you application), you may get the specific logger instance and set the level to DEBUG. Once you complete your activity, you may set the level to 'null' so that the level is inherited from the parent. Sample code will be as below:
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
.....
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
Logger logger = context.getLogger("org.springframework");
logger.setLevel((Level.toLevel("DEBUG"));
// Do your task
logger.setLevel(null); // When we do this, level will be inherited from parent.
logback has an option that allows periodic rescanning of the configuration file to look for changes.
<configuration scan="true">
...
</configuration>
I have the following three components defined in the Caste-Windsor XML configuration for my application:
<component id="StringFactory"
service="IStringFactory, MyApp"
type="DefaultStringFactory, MyApp"
lifestyle="singleton"
/>
<component id="TheString"
type="System.String"
factoryId="StringFactory"
factoryCreate="CreateString"
>
<parameters>
<name>SomeString</name>
</parameters>
</component>
<component id="TheTarget"
service="ITarget, MyApp"
type="TheTarget, MyApp"
lifestyle="transient"
>
<parameters>
<aString>${TheString}</aString>
</parameters>
</component>
And the following facility defined:
<facility id="factory.support"
type="Castle.Facilities.FactorySupport.FactorySupportFacility, Castle.MicroKernel"
/>
When I run the application and set a breakpoint in the constructor of the TheObject class, the value passed in as the aString parameter is "${TheString}" when I expect it to resolve to the value of the component with that name.
Also, I have a breakpoint in the StringFactory constructor and CreateString method, neither of which are hit. I know the configuration is being used as other components are resolving correctly.
What am I missing or doing wrong here?
UPDATE
In light of the huge tangient this topic has taken, I've refactored the code above to remove anything to do with connection strings. The original intent of this post was about injecting a property with the value returned from a method on another object. Somehow that point was lost in a discussion about why I'm using XML versus code-based configuration and if this is a good way to inject a connection string.
The above approach is far from an original idea and it was pulled from several other discussions on this topic and our requirements are what they are. I'd like help understanding why the configuration as it is in place (whether the right approach or not) isn't working as expected.
I did verify that the first two components are being instantiated correctly. When I call Container.Resolve("TheString"), I get the correct value back. For whatever reason, The parameter syntax is not working correctly.
Any ideas?
While not a definitive solution to what I need to do in my application, I believe I've figured out what is wrong with the code. Or at least I've found a way to make it work which hints at the original problem.
I replaced the String type for TheString with a custom class. That's it. Once I did that, everything worked fine.
My guess is that it has something to do with the fact that I was trying to use a ValueType (primitive) as a component. I guess Castle doesn't support it.
So, knowing that's the case, I can now move on to figuring out if this approach is really going to work or if we need to change direction.
UPDATE
For the sake of completeness, I thought I'd go ahead and explain what I did to solve my problem AND satisfy my requirements.
As before, I have access to my configuration settings through an IConfigurationService defined as:
<component id="ConfigurationService"
service="MyApp.IConfigurationService, MyApp"
type="MyApp.RuntimeConfigurationService, MyApp"
lifestyle="singleton"
/>
This is automatically injected into my (new) IConnectionFactory which is responsible for generating IDbConnection objects based on the connection strings defined in the application's configuration file. The factory is declared as:
<component id="ConnectionFactory"
service="MyApp.Factories.IConnectionFactory, MyApp"
type="MyApp.Factories.DefaultConnectionFactory, MyApp"
lifestyle="singleton"
/>
In order to resolve what connection is used by my repository, I declare each connection as a component using the ConnectionFactory to create each instance:
<component id="MyDbConnection"
type="System.Data.IDbConnection,
System.Data, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
factoryId="ConnectionFactory"
factoryCreate="CreateConnection"
lifestyle="transient"
>
<parameters>
<connectionStringName>MyDB</connectionStringName>
</parameters>
</component>
Notice the fully described reference to System.Data. I found this is necessary whenever referencing assemblies in the GAC.
Finally, my repository is defined as:
<component id="MyRepository"
service="MyApp.Repositories.IMyRepository, MyApp"
type="MyApp.Sql.SqlMyRepository, MyApp.Sql"
lifestyle="transient"
>
<parameters>
<connection>${MyDbConnection}</connection>
</parameters>
</component>
Now everything resolves correctly and I don't have ANY hard-coded strings compiled into my code. No connection string names, app setting keys or whatever. The app is completely reconfigurable from the XML files which is a requirement I must satisfy. Plus, other devs that will be working with the solution can manage the actual connection strings in the way they are used to. Win-win.
Hope this helps anyone else that runs into a similar scenario.
You don't really need XML registrations here, since you probably don't need to swap components or change the method used without recompiling. Writing a configurable app does not imply having to use XML registrations.
The problem with this particular XML registration you posted is that the connection string is a parameter, but it's treated like a service.
Doing this with code registrations is much easier, e.g.:
var container = new WindsorContainer();
container.Register(Component.For<IConfigurationService>().ImplementedBy<RuntimeConfigurationService>());
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
.LifeStyle.Transient
.DynamicParameters((k, d) => {
var cfg = k.Resolve<IConfigurationService>();
d["connectionString"] = cfg.GetConnectionString();
k.ReleaseComponent(cfg);
}));
Or if you don't want to depend on IConfigurationService, you could do something like:
container.Register(Component.For<ITheRepository>().ImplementedBy<TheRepository>()
.LifeStyle.Transient
.DependsOn(Property.ForKey("connectionString")
.Is(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["connName"]].ConnectionString))
Is it possible to set the logger from configuration. I have a web app
using a framework. The framework is extensible and has the logger.
When I log, currently, the logger is set to the framework class.
Is it possible that I can configure my web app and set the logger for the web app to
loggerForWebApp and the logger for a console app (which is using the
same framework) to loggerForConsoleApp?
In addition to the root logger (which must always be there) you can have named loggers with their own appender-refs and levels.
For instance, you could have something like this:
<root>
....
</root>
<logger name="loggerForWebApp">
<level value="WARN" />
<appender-ref ... />
</logger>
<logger name="loggerForConsoleApp">
<level value="WARN" />
<appender-ref ... />
</logger>
In code, you would summon these loggers by their name:
var log = LogManager.GetLogger("loggerForWebApp");
Most definitely, and this is one of the great things about log4net: it can log out to a wide range of loggers.
For examples of the appenders, see here. Probably the most common one in use is the RollingFileAppender, but the ConsoleAppender can be very handy for console applications. Alternatively the TraceAppender can write out to the standard .NET trace listeners for further redirection (or display in the debug Output window in Visual Studio).
To create your own, implement IAppender.
details to follow