Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7672491
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 31, 20262026-05-31T16:17:51+00:00 2026-05-31T16:17:51+00:00

In a normal/synchronous/single-threaded console app, NDC.Push works fine for managing the ‘current item’ (potentially

  • 0

In a normal/synchronous/single-threaded console app, NDC.Push works fine for managing the ‘current item’ (potentially at multiple levels of nesting, but just 1 level for this example).

For example:

private static ILog s_logger = LogManager.GetLogger("Program");

static void Main(string[] args)
{
    BasicConfigurator.Configure();

    DoSomeWork("chunk 1");
    DoSomeWork("chunk 2");
    DoSomeWork("chunk 3");
}

static void DoSomeWork(string chunkName)
{
    using (NDC.Push(chunkName))
    {
        s_logger.Info("Starting to do work");
        Thread.Sleep(5000);
        s_logger.Info("Finishing work");
    }
}

This will result in the expect log output, showing a ‘chunk X’ NDC entry just to the right of ‘Program’ (the default pattern for the basic configurator)

232 [9] INFO Program chunk 1 – Starting to do work

5279 [9] INFO Program chunk 1 – Finishing work

5279 [9] INFO Program chunk 2 – Starting to do work

10292 [9] INFO Program chunk 2 – Finishing work

10292 [9] INFO Program chunk 3 – Starting to do work

15299 [9] INFO Program chunk 3 – Finishing work

However, I can’t figure out how to maintain that using ‘normal’ async methods.

For instance, trying to do this:

private static ILog s_logger = LogManager.GetLogger("Program");

static void Main(string[] args)
{
    BasicConfigurator.Configure();

    var task1 = DoSomeWork("chunk 1");
    var task2 = DoSomeWork("chunk 2");
    var task3 = DoSomeWork("chunk 3");

    Task.WaitAll(task1, task2, task3);
}

static async Task DoSomeWork(string chunkName)
{
    using (log4net.LogicalThreadContext.Stacks["NDC"].Push(chunkName))
    //using (log4net.ThreadContext.Stacks["NDC"].Push(chunkName))
    {
        s_logger.Info("Starting to do work");
        await Task.Delay(5000);
        s_logger.Info("Finishing work");
    }
}

Shows them all starting "normally", but when the task completes on a different thread, the stack is lost (I was hoping the log4net.LogicalThreadContext would be TPL-‘aware’ I guess).

234 [10] INFO Program chunk 1 – Starting to do work

265 [10] INFO Program chunk 2 – Starting to do work

265 [10] INFO Program chunk 3 – Starting to do work

5280 [7] INFO Program (null) – Finishing work

5280 [12] INFO Program (null) – Finishing work

5280 [12] INFO Program (null) – Finishing work

Outside of adding a new TaskContext (or the like) to log4net, is there a way of tracking this kind of activity?

The goal is really to do so with the async/await syntax sugar – either forcing some kind of thread affinity or doing things like keeping a concurrent dictionary around keyed by task are likely workable options, but I’m trying to keep as close to the synchronous version of the code as possible. 🙂

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-31T16:17:53+00:00Added an answer on May 31, 2026 at 4:17 pm

    There isn’t a good story for async logical call contexts right now.

    CallContext can’t be used for this. The logical CallContext doesn’t understand how async methods return early and resume later, so it will not always work correctly for code that uses simple parallelism such as Task.WhenAll.

    Update: CallContext was updated in .NET 4.5 RTW to correctly work with async methods.

    I looked into log4net; LogicalThreadContext is documented as using CallContext, but there was a bug that made it use the non-logical contexts (fixed in their SVN on Feb 2, 2012; the current 1.2.11 release does not include that fix). Even when it’s fixed, though, it’ll still not work with async (because the logical CallContext doesn’t work with async).

    When I need an async logical call context, I make a class that contains the context data and keep all of my async methods in a functional style as instance members of that class. This is certainly not an ideal solution, but it’s a dirty hack that works.

    In the meantime, please upvote the suggestion that Microsoft provide some mechanism for this.

    P.S. A concurrent dictionary keyed by Task won’t work, because async methods are not necessarily running tasks (i.e., in your example code, at the using statement, Task.CurrentId would be null because there is no task actually executing at that point).

    And thread affinity has its own problems, too. You actually end up needing a separate thread for each independent asynchronous operation. Bye-bye, scalability…

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

A normal urllib2 works fine: >>> import urllib2 >>> r = urllib2.urlopen(uhttp://bit.ly/4ovTZw) >>> r.geturl()
My normal work flow to create a new repository with subversion is to create
Sometimes normal FTP doesn't quite cut it... When you need to do secure FTP
In normal WebForms scenario, any root-relative URLs (e.g. ~/folder/file.txt) inside CSS files such as:
Under normal circumstances, a VB.NET application of mine can check the ClientName environmental variable
Under normal circumstances you cannot move a workflow designed in SharePoint Designer to another
In normal mode I can hit Ctrl + E which deletes the rest of
For normal (say Windows Forms) C# applications, to execute commands after a successful build
Generating normal columnar data in excel file is quite easy but does any one
A normal UserControl looks like this in XAML: <UserControl x:Class=mynamespace.foo ...namespaces...> <!-- content -->

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.