The Current Direction of Agile
In the last couple of years, Agile adoption has increased quite a bit, letting a number of people experience and challenge different elements of the Agile processes. This increased exposure and usage has made some try out different solutions to different challenges than what was originally proposed by the people that came up with the Agile processes. This is a core part of Agile, to change the process over time and make it more effective. This article will focus on some of the recent trends within the Agile community by briefly describing some alternatives to today’s well known Agile processes. Particularly focusing on estimation, forecasting deliverables and the increased impact Lean manufacturing has had on the Agile community.
RelatedVendorContent
Agile Development: A Manager's Roadmap for Success
The Agile Project Manager
5 Ways to Ensure Application Performance
The Agile Checklist
Technical Skills for Agile Development Track @ QConSF 09
Related Sponsor
VersionOne is recognized by Agile practitioners as the leader in Agile project management tools. Companies such as Adobe, BBC, CNN, Dow, HP, IBM, Sony and 3M have turned to VersionOne to help deliver greater value to their customers.
Lean
I will not cover Lean specifically, but I will briefly cover some of the Lean principles, practices and tools which are relevant to the other processes and tools covered in this article. Let’s first look at a brief description of Lean from Wikipedia:
Lean software development is a translation of lean manufacturing principles and practices to the software development domain. Adapted from the Toyota Production System, a pro-lean subculture is emerging from within the Agile community.
Lean in software development originated in Mary and Tom Poppendieck’s book, Lean Software Development, where they cover 7 principles and 22 tools of Lean Software Development.
Below is a short list of terms often used in Lean that you should be familiar with reading this article:
- Waste
- Just-in-time (JIT)
- Work In Process (WIP)
- Pull based systems
Waste
Waste (a.k.a. muda), is about eliminating artifacts from your process that does not add value to your customer. Mary Poppendieck defines waste as:
Anything that depletes resources of time, effort, space, or money without adding customer value.
And value as:
Seen through the eyes of those who pay for, use, support, or derive value from our systems.
In Mary’s article, Principles of Lean Thinking (PDF) and also in the Poppendieck book mentioned earlier, she identifies seven wastes of software development. The wording of these wastes has changed a bit since they first were published and below I’ve listed the ones I saw her use in a presentation recently:
- Extra features
- Handovers
- Work in process
- Failure demand
- Task switching
- Delays
- Defects
(Limit) Work in process (WIP)
Limiting WIP is about having feature requests that have been initialized (specification, requirements gathering, etc.) to flow through the system in a continuous way. In other words, when your organization first starts to work on a feature request, limit how long it stays in process before going to release.
Just-in-time (JIT)
JIT is about reducing in-process inventory and avoid/discover bottlenecks in the process. JIT is often used as a mean to reduce WIP (described above). A typical example of inventory in software development is requirements going from specification to development or code going from development to test. JIT sets focus on improving return on investment (ROI), quality and efficiency. One of the processes/tools supporting JIT is Kanban, which I will talk about later in this article.
Pull based systems
The number one identified waste mentioned above is extra features. To pull instead of push is about responding to customer needs, instead of giving the customer what you think he will need (extra features). As with JIT, Kanban is one example of a tool supporting a pull based system.
Iteration Based Processes

Scrum and Extreme Programming (XP) are two well known Agile processes that utilize iterations or sprints as a central part of their planning and deliverable technique. In relation to estimation, you typically find that you estimate User Stories (XP) or Backlog Items (Scrum). In this article I will use User Story to describe both User Story and Backlog Item. User Stories are typically estimated in either Ideal Days or Story Points. Tasks are typically described in ideal hours. Mike Cohn, in his book: Agile Estimating and Planning, has covered the different methods and techniques for estimation and forecasting thoroughly.
The main idea is to have fixed length iterations of 1-2 weeks. Figure 1 shows three iterations from a backlog with stories of varying size. It also shows that the team has a velocity of 20 points, meaning their able to deliver 20 points worth of stories every iteration.
Knowing the team’s velocity is important to be able to forecast when a user story will be finished or predict which stories will be part of a release.
Equal Sized User Stories
When an Agile team first starts out they often start with Scrum or XP. This is the approach covered in most Agile books. A typical scenario for a fresh Agile team might go something like this:
The first planning meetings takes quite some time, with a lot of discussion and thinking to come up with the best estimates possible. First for each story, then for the tasks, and at the end decide on how much to commit to for the iteration. After a while (several iterations) when the team gets more experienced, they know almost by instinct how much they can commit to for a single iteration, and estimation of stories and tasks goes much quicker.

Some teams have taken this to another level and skipped the whole estimation process. Rather than spending time on coming up with estimates, they split their user stories into equal sized stories (see figure 2). The main difference is that you don’t estimate each and every story, but you have each story fit a fixed estimate or size.
By having these fixed sized stories, the team can commit to a certain number of stories for their iterations. Their velocity then becomes the amount of stories they are able to commit to, instead of the amount of Story Points or Ideal Days. This simplifies the planning process quite a lot.
Think about how you would find out how many stories (or which stories) a team will get done in a three months time, with a prioritized backlog. Using the team’s velocity (number of stories), multiply it with the number of iterations for the three month period and you have your forecast.
Fixed sized user stories eliminates waste by not spending time on doing estimates and allow the team to focus on the details of what’s important; implementing the stories.
Some argue that you still estimate, since you have to make all stories equal size. How do you do that without estimating? I find this to be about two things:
- Getting each and every story small enough.
- The average sized story is what is important.
For the first point it’s important to have as small stories as possible, something that adds value to the business and are deliverable. A two day story is much easier to spot and be correct on its size, than a bigger story that you might think take 8-10 days, but in reality takes 15.
For the second point it does not matter if one story or another is bigger or smaller as long as it’s not extreme deviations. Typically you find an exception from time to time, but all in all your velocity does not fluctuate very much.
Naked Planning
Naked Planning, coined by Arlo Belshee, takes a different approach where not only estimation is removed from the process but also iterations. In relation to estimation Arlo asked himself the question:
Are estimates necessary to decide the order of prioritization and make a guess at how long it’s going to take something to come live?
After talking to the business he found it was not and found another way of accomplishing the same thing.

Inspired by Lean practices, Arlo used a queue of Minimum Marketable Features (MMF’s). Using this queue the development team grabs the top most MMF and start working on it. When finished they grab the next one from the top and so on. Like Lean this is a pulled based system. The queue is managed by the business, always having the most important and valuable MMF at the top. The queue is kept to about seven items, which is about how many a person can keep in his head at once, and also a manageable amount of items to prioritize amongst.
To estimate when an MMF can be delivered they use historical data. When an MMF gets pulled from the queue it’s marked with the date, this is also done when the MMF is finished. Every three to five weeks they calculate the average of the duration, giving the business a number to work with in planning. If the size of the MMF’s put into the queue gets bigger or smaller, the duration estimate adjust itself after a short period of time.
Kanban
From Wikipedia:
Kanban (in kanji 看板 also in katakana カンバン, where kan, 看 / カン, means "visual," and ban, 板 / バン, means "card" or "board") is a concept related to lean and just-in-time (JIT) production.
This description can lead you to believe that Kanban is all about the task board most Agile team’s use. That is not the case. Kanban as a tool uses a white board to visualize the flow of “materials”, but it is not to be confused with the board itself.
Kanban is a way of controlling (limiting) WIP and keeping continuous flow. You use the whiteboard to visualize the different stages an item needs to pass through before being deployed. By monitoring this workflow it’s easier to discover bottlenecks that slows down your process. You typically see this by observing where elements queue up. Often a number is put on columns defining how many items are allowed to be “in process” at the same time. This number is specifically for limiting work in process.
A simple “Kanban” we often see on Agile teams have the following columns: ToDo, In Process and Done (see Figure 4).

Kanban takes this to a different level and visualize the workflow used by the organization and not just the development team (see Figure 5).

Typically you can follow a feature on the Kanban board from the idea and all the way through the different phases in the organization to its deployment into the system. Henrik Kniberg has a nice cartoon like description of a small Kanban system that helps visualize this further.
Kanban does not utilize iterations, but often has regular release cycles. Having a release cycle of e.g. two weeks, you would put whatever has reached the “release state” into production. However, there is nothing preventing you from releasing finished content right into production, as long as your process support one-click deployment, which any Agile team should do.
One thing to note about Kanban: there is no perfect Kanban system that everyone should adapt. It will be different for every organization, because they are different by nature.
David Anderson is one person in the Agile community that has used and documented Kanban in software development. I encourage you to check out his writing and presentations on the subject.
Scrumban
Scrumban was coined by Corey Ladas. Based on his extensive Scrum experience he describes in his book, with the same name, how you can start adapting lean thinking to Scrum focusing on Kanban. Scrumban is interesting because it does not just explain what Kanban is, but takes you through the steps of moving from Scrum towards Kanban in a step-by-step manner. Scrumban is neither Scrum nor Kanban, but if you go all the way you end up with doing Kanban on an Agile team.
Conclusion
There are obviously other examples of where Agile is going not covered here, but I find that these tell a story of which direction Agile is going. Many teams have found iterations to be a limiting factor and looked for ways of removing them. Some wanted to scale the Agile process to not just include the development team and those working closely with that team, but to also include the rest of the organization. Others found that the amount of time spent on estimating was considered waste and found other ways of delivering software and still be able to satisfy the questions and demands from the business.
But all in all I think it shows that the software industry is maturing. Software teams see value in Agile processes, but they continuously want to improve it, which hopefully brings us to better and more effective processes as well as better quality software. Software development is still a young craft and we live in interesting times!
Posted in: programming software | Tags: agile direction of agile agile programming programming naked planning mmf accomplish kanbanEnabling Dynamic Data for Existing Projects
Dynamic Data features that shipped in the .NET Framework 3.5 SP1 brought new features such as the following:
· Field templates – These provide data-type-based templates for data-bound controls. Field templates provide a simpler way to customize the look of data controls than using template fields for each field.
· Validation – Dynamic Data lets you use attributes on data classes to specify validation for common scenarios like required fields, range checking, type checking, pattern matching using regular expressions, and custom validation. Validation is enforced by the data controls.
However, these features had the following requirements:
· The data-access layer had to be based on Entity Framework or LINQ to SQL.
· The only data source controls supported for these features were the EntityDataSource or LinqDataSource controls.
· The features required a Web project that had been created using the Dynamic Data or Dynamic Data Entities templates in order to have all the files that were required to support the feature.
A major goal of Dynamic Data support in ASP.NET 4 is to enable the new functionality of Dynamic Data for any ASP.NET application. The following example shows markup for controls that can take advantage of Dynamic Data functionality in an existing page.
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="True"
DataKeyNames="ProductID" DataSourceID="LinqDataSource1">
</asp:GridView>
<asp:LinqDataSource ID="LinqDataSource1" runat="server"
ContextTypeName="DataClassesDataContext" EnableDelete="True" EnableInsert="True"
EnableUpdate="True" TableName="Products">
</asp:LinqDataSource>
In the code for the page, the following code must be added in order to enable Dynamic Data support for these controls:
GridView1.EnableDynamicData(typeof(Product));
When the GridView control is in edit mode, Dynamic Data automatically validates that the data entered is in the proper format. If it is not, an error message is displayed.
This functionality also provides other benefits, such as being able to specify default values for insert mode. Without Dynamic Data, to implement a default value for a field, you must attach to an event, locate the control (using FindControl), and set its value. In ASP.NET 4, the EnableDynamicData call supports a second parameter that lets you pass default values for any field on the object, as shown in this example:
DetailsView1.EnableDynamicData(typeof(Product), new { ProductName = "DefaultName" });
Posted in: programming asp.net | Tags: asp.net 4.0 linqdatasource dynamic data .net 3.5 entitydatasourceVisual Studio 2010 Web Designer Improvements
The Web page designer in Visual Studio 2010 has been enhanced for greater CSS compatibility, includes additional support for HTML and ASP.NET markup snippets, and features a redesigned version of IntelliSense for JScript.
Improved CSS Compatibility
The Visual Web Developer designer in Visual Studio 2010 has been updated to improve CSS 2.1 standards compliance. The designer better preserves integrity of the HTML source and has greater overall robustness than in previous versions of Visual Studio. Under the hood, architectural improvements have also been made that will better enable future enhancements in rendering, layout, and serviceability.
HTML and JScript Snippets
In the HTML editor, IntelliSense auto-completes tag names. The IntelliSense Snippets feature auto-completes entire tags and more. In Visual Studio 2010, IntelliSense snippets are supported for JScript, alongside C# and Visual Basic, which were supported in earlier versions of Visual Studio.
Visual Studio 2010 includes over 200 snippets that help you auto-complete common ASP.NET and HTML tags, including required attributes (such as runat="server") and common attributes specific to a tag (such as ID, DataSourceID, ControlToValidate, and Text).
You can download additional snippets, or you can write your own snippets that encapsulate the blocks of markup that you or your team use for common tasks.
JScript IntelliSense Enhancements
In Visual 2010, JScript IntelliSense has been redesigned to provide an even richer editing experience. IntelliSense now recognizes objects that have been dynamically generated by methods such as registerNamespace and by similar techniques used by other JavaScript frameworks. Performance has been improved to analyze large libraries of script and to display IntelliSense with little or no processing delay. Compatibility has been dramatically increased to support nearly all third-party libraries and to support diverse coding styles. Documentation comments are now parsed as you type and are immediately leveraged by IntelliSense.
Posted in: programming software | Tags: .net 4.0 javascript visual studio 2010 html.editrofor web designer improvements html and jscript snippets css compatibility datasourceid controltovalidate jscript intellisense registernamespace intellisenseDynamic log file Names with log4net
If you needed to generate dynamic logfile names with log4net then you can use the following config
1: <appender name="RollingFileAppenderV1" type="log4net.Appender.RollingFileAppender">
2: <file type="log4net.Util.PatternString" value="F:\HornetFeed\%property{LogName}" />
3: <appendToFile value="true" />
4: <rollingStyle value="Size" />
5: <maxSizeRollBackups value="-1" />
6: <maximumFileSize value="5000KB" />
7: <staticLogFileName value="true" />
8: <countDirection value="1"/>
9: <layout type="log4net.Layout.PatternLayout">
10: <conversionPattern value="%m%n" />
11: </layout>
12: <filter type="log4net.Filter.PropertyFilter">
13: <Key value="Version" />
14: <StringToMatch value="1" />
15: </filter>
16: <filter type="log4net.Filter.DenyAllFilter" />
17: </appender>
Note the %property{LogName} this is a log4net Property which we can set at runtime using C# code.
1: log4net.GlobalContext.Properties["LogName"] = "file1.log";
Remember to set the GlobalContext Properties before instantiating the log4net logger. i.e. before this call:
1: log4net.ILog log = LogManager.GetLogger(typeof(Program));
Also note the “PropertyFilter”
1: <filter type="log4net.Filter.PropertyFilter">
2: <Key value="Version" />
3: <StringToMatch value="1" />
4: </filter>
This Property is also set dynamically at runtime using C# code. We can use ThreadContext to determine which logfile the log entry will be written to. Thus if you wanted that the RollingFileAppenderV1 was used for writing insure that the Thread calling the log.Warn method precedes the call with this following line
1: log4net.ThreadContext.Properties["Version"] = "1";
If you wanted to use RollingFileAppenderV1 always for logging all messages other than Exception messages then use the following
1: try
2: {
3: log4net.GlobalContext.Properties["Version"] = "1";
4: //Business logic with many log.Warn calls
5: }
6: catch (Exception ex)
7: {
8: LogError(ex);
9: }
10:
11: ///Helper method to log errors:
12: internal static void LogError(Exception ex)
13: {
14: string state = "1";
15: if(log4net.ThreadContext.Properties["Version"] != null)
16: state = log4net.ThreadContext.Properties["Version"].ToString();
17: log4net.ThreadContext.Properties["Version"] = "0";
18: logger.HandleException(ex, "Error");
19: log4net.ThreadContext.Properties["Version"] = state;
20: }
Note I am using GlobalContext and hence all threads will use this setting to write to log files. No need to set the TrheadContext.Properties on individual threads.
But I prefer to use ThreadContext so that we can control the setting on each thread level.
Also make sure the log4net XML config is set as follows:
1: <log4net>
2: // the following logs only those logs when the Property Version is set to 1 i.e.
3: // if Version =1 then log.Warn calls are logged to this dynamically named file
4: <appender name="RollingFileAppenderV1" type="log4net.Appender.RollingFileAppender">
5: <file type="log4net.Util.PatternString" value="F:\HornetFeed\%property{LogName}" />
6: <appendToFile value="true" />
7: <rollingStyle value="Size" />
8: <maxSizeRollBackups value="-1" />
9: <maximumFileSize value="5000KB" />
10: <staticLogFileName value="true" />
11: <countDirection value="1"/>
12: <layout type="log4net.Layout.PatternLayout">
13: <conversionPattern value="%m%n" />
14: </layout>
15: <filter type="log4net.Filter.PropertyFilter">
16: <Key value="Version" />
17: <StringToMatch value="1" />
18: </filter>
19: <filter type="log4net.Filter.DenyAllFilter" />
20: </appender>
21:
22: //The following matches and logs all events except Version=1 and Version=2 and strings containing CACHE_CALL_LOG
23:
24: <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
25: <file type="log4net.Util.PatternString" value="F:\logfiles\trace.log" />
26: <appendToFile value="true" />
27: <rollingStyle value="Size" />
28: <maxSizeRollBackups value="10" />
29: <maximumFileSize value="3000KB" />
30: <staticLogFileName value="true" />
31: <countDirection value="1"/>
32: <layout type="log4net.Layout.PatternLayout">
33: <conversionPattern value="%d [%t] %-5p %c [%x] - %m%n" />
34: </layout>
35: <filter type="log4net.Filter.PropertyFilter">
36: <Key value="Version" />
37: <StringToMatch value="1" />
38: <acceptOnMatch value="false" />
39: </filter>
40: <filter type="log4net.Filter.PropertyFilter">
41: <Key value="Version" />
42: <StringToMatch value="2" />
43: <acceptOnMatch value="false" />
44: </filter>
45: <filter type="log4net.Filter.StringMatchFilter">
46: <stringToMatch value="CACHE_CALL_LOG" />
47: <acceptOnMatch value="false" />
48: </filter>
49: </appender>Posted in: programming asp.net | Tags: log4net dynamic log file file name path log path
Log4Net, Log Event Context
There are times when your log messages need some context to be most useful. For example, if your application supports multiple concurrent clients, it would be helpful to know the client to which each log statement applies. If a piece of code is used by several components, it would be nice for the log messages to reflect which component is calling the code. log4net provides a very simple mechanism to accommodate such needs.
Open Visual Studio, create a new console project, and add a reference to the log4net assembly. Add the following code to your program.cs file:
- namespace Tutorial6_Context
- {
- class Program
- {
- private static log4net.ILog Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );
- static void Main( string[] args )
- {
- log4net.Config.XmlConfigurator.Configure();
- log4net.ThreadContext.Properties[ "myContext" ] = "Logging from Main";
- Log.Info( "this is an info message" );
- Console.ReadLine();
- }
- }
- }
namespace Tutorial6_Context
{ class Program { private static log4net.ILog Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType ); static void Main( string[] args ) { log4net.Config.XmlConfigurator.Configure(); log4net.ThreadContext.Properties[ "myContext" ] = "Logging from Main"; Log.Info( "this is an info message" ); Console.ReadLine(); } }
}
Note line 10, where a property named "myContext" is added to the ThreadContext static class and assigned a simple string value. Next add the following application configuration file to the project:
- <configuration>
- <configSections>
- <section name="log4net"
- type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
- </configSections>
- <log4net>
- <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
- <layout type="log4net.Layout.PatternLayout">
- <conversionPattern value="%logger (%property{myContext}) [%level]- %message%newline" />
- </layout>
- </appender>
- <root>
- <level value="ALL" />
- <appender-ref ref="ConsoleAppender" />
- </root>
- </log4net>
- </configuration>
<configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <log4net> <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%logger (%property{myContext}) [%level]- %message%newline" /> </layout> </appender> <root> <level value="ALL" /> <appender-ref ref="ConsoleAppender" /> </root> </log4net>
</configuration>
Line 11 contains the layout pattern for our log; the %property{myContext} format specifier will be replaced with the value of the "myContext" property at the time the message is logged.
Compile and run; the context value is reflected in the log output:
- Tutorial6_Context.Program (Logging from Main) [INFO]- this is an info message
Tutorial6_Context.Program (Logging from Main) [INFO]- this is an info message
Context Scopes
There are actually three logging contexts available in log4net. Like the logger architecture, contexts are organized into a hierarchy where properties in the more granular contexts override property values in less granular contexts.
Context
Description
log4net.GlobalContext
A global context shared across all application threads and app domains. If two threads set the same property on the GlobalContext, one value will overwrite the other.
log4net.ThreadContext
Any properties set in this context are scoped to the calling thread. In other words, in this context two threads can set the same property to different values without stomping on each other.
log4net.ThreadLogicalContext
This context behaves similarly to the ThreadContext, except that the scope is defined by logical thread boundaries. I'll be honest and say that I've never used the ThreadLogicalContext in my career, but if you're working with a custom thread pool algorithm or hosting the CLR, you may find some use for this one.
Calculated Context Values
Context property values don't have to be strings. You can set the value of a context property to any object reference; the value of the object's ToString method will be used to obtain the context property value when a logging event occurs. This can be useful when you need a context property to represent a calculated state at the time of each logging event. For instance, perhaps you want to track your application's use of the CPU:
- namespace Tutorial6_Context
- {
- public class CalcualtedContextProperty
- {
- public override string ToString()
- {
- return Process.GetCurrentProcess().TotalProcessorTime.ToString();
- }
- }
- }
namespace Tutorial6_Context
{ public class CalcualtedContextProperty { public override string ToString() { return Process.GetCurrentProcess().TotalProcessorTime.ToString(); } }
}
Set a context property value to an instance of this new class:
- namespace Tutorial6_Context
- {
- class Program
- {
- private static log4net.ILog Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );
- static void Main( string[] args )
- {
- log4net.Config.XmlConfigurator.Configure();
- log4net.ThreadContext.Properties[ "myContext" ] = new CalculatedContextProperty();
- Log.Info( "this is an info message" );
- Console.ReadLine();
- }
- }
- }
namespace Tutorial6_Context
{ class Program { private static log4net.ILog Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType ); static void Main( string[] args ) { log4net.Config.XmlConfigurator.Configure(); log4net.ThreadContext.Properties[ "myContext" ] = new CalculatedContextProperty(); Log.Info( "this is an info message" ); Console.ReadLine(); } }
}
The result is each log entry containing the application's total CPU time at the time the log entry was made:
- Tutorial6_Context.Program (00:00:00.2968750) [INFO]- this is an info message
Tutorial6_Context.Program (00:00:00.2968750) [INFO]- this is an info message
ThreadContext Stacks
ThreadContext and ThreadLogicalContext can store property values in stacks (available via the Stacks static property of each class). These stacks are very handy, as they allow you to track program states in the context of a log message:
- namespace Tutorial6_Context
- {
- class Program
- {
- static log4net.ILog Log = null;
- static void Main( string[] args )
- {
- log4net.Config.XmlConfigurator.Configure();
- Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );
- using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "outer" ) )
- {
- Log.Info( "this is the first message" );
- using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "inner" ) )
- {
- Log.Info( "this is the second message" );
- }
- }
- Log.Info( "this is the third message" );
- Console.ReadLine();
- }
- }
- }
namespace Tutorial6_Context
{ class Program { static log4net.ILog Log = null; static void Main( string[] args ) { log4net.Config.XmlConfigurator.Configure(); Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType ); using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "outer" ) ) { Log.Info( "this is the first message" ); using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "inner" ) ) { Log.Info( "this is the second message" ); } } Log.Info( "this is the third message" ); Console.ReadLine(); } }
}
Pushing a property value onto a stack returns an IDisposable that, when disposed, pops the property value off of the stack. The logging output reflects the state of the context stack at each logging event; the stack is represented as a space-delimited list, with newer items appearing later in the list:
Tutorial6_Context.Program (outer) [INFO]- this is the first message
- Tutorial6_Context.Program (outer inner) [INFO]- this is the second message
- Tutorial6_Context.Program ((null)) [INFO]- this is the third message
Tutorial6_Context.Program (outer) [INFO]- this is the first message Tutorial6_Context.Program (outer inner) [INFO]- this is the second message Tutorial6_Context.Program ((null)) [INFO]- this is the third message
Context stacks are incredibly useful for wrapping access to a shared piece of code. For example, in the following code two methods call the same utility routine; a marker is pushed onto the property stack before each call: namespace Tutorial6_Context
- {
- class Program
- {
- static log4net.ILog Log = null;
- static void Main( string[] args )
- {
- log4net.Config.XmlConfigurator.Configure();
- Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType );
- FirstAction();
- SecondAction();
- Console.ReadLine();
- }
- static void FirstAction()
- {
- using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "FirstAction" ) )
- {
- UtilityRoutine();
- }
- }
- static void SecondAction()
- {
- using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "SecondAction" ) )
- {
- UtilityRoutine();
- }
- }
- static void UtilityRoutine()
- {
- Log.Info( "this is an info message" );
- }
- }
- }
namespace Tutorial6_Context
{ class Program { static log4net.ILog Log = null; static void Main( string[] args ) { log4net.Config.XmlConfigurator.Configure(); Log = log4net.LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType ); FirstAction(); SecondAction(); Console.ReadLine(); } static void FirstAction() { using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "FirstAction" ) ) { UtilityRoutine(); } } static void SecondAction() { using( log4net.ThreadContext.Stacks[ "myContext" ].Push( "SecondAction" ) ) { UtilityRoutine(); } } static void UtilityRoutine() { Log.Info( "this is an info message" ); } }
}
Posted in: programming asp.net | Tags: log4net log event context