Skip to content

Commit 56edca6

Browse files
authored
Merge pull request #180 from apache/Feature/177-fix-ThreadName-again
#177 use ManagedThreadId for long running tasks
2 parents 97f9d95 + 4fd21b1 commit 56edca6

File tree

9 files changed

+84
-48
lines changed

9 files changed

+84
-48
lines changed

doc/RELEASING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ release version 2.0.123:
3232
- pom.xml: update version
3333
4. Sign release artifacts (zips & nupkg) under `build/artifacts`
3434
- eg `gpg --armor --output log4net-2.0.123.nupkg.asc --detach-sig log4net-2.0.123.nupkg`
35-
- there is an accompanying `sign-log4net-libraries.sh` which you could invoke if you cd
35+
- there is an accompanying `sign-log4net-libraries.sh/ps1` under scripts which you could invoke if you cd
3636
into the `build/artifacts` folder
3737
- I build on Windows and sign on Linux as my build machine belongs to my company
3838
and I don't want to store keys there. Always protect your keys fervently!

scripts/build-preview.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dotnet build -c Release '-p:GeneratePackages=true' $PSScriptRoot/../src/log4net/log4net.csproj
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/build-preview.ps1

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/log4net.Tests/Core/LoggingEventTest.cs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,31 @@
1717
//
1818
#endregion
1919

20-
// net8 deprecates BinaryFormatter used in testing.
21-
#if NET462_OR_GREATER
22-
23-
using log4net.Core;
24-
using log4net.Util;
25-
using NUnit.Framework;
2620
using System;
2721
using System.Globalization;
2822
using System.IO;
23+
using System.Reflection;
2924
using System.Runtime.Serialization.Formatters.Binary;
25+
using log4net.Core;
26+
using log4net.Util;
27+
using NUnit.Framework;
3028

3129
namespace log4net.Tests.Core;
3230

3331
[TestFixture]
3432
public sealed class LoggingEventTest
3533
{
34+
// net8 deprecates BinaryFormatter used in testing.
35+
#if NET462_OR_GREATER
3636
private static readonly DateTime localTime
37-
= new DateTime(2000, 7, 1, 0, 0, 0, 0, CultureInfo.InvariantCulture.Calendar, DateTimeKind.Local);
37+
= new(2000, 7, 1, 0, 0, 0, 0, CultureInfo.InvariantCulture.Calendar, DateTimeKind.Local);
3838

3939
[Test]
4040
public void SerializeDeserialize_BinaryFormatter()
4141
{
4242
Utils.InconclusiveOnMono();
43-
var timestamp = localTime.ToUniversalTime();
44-
var ev = new LoggingEvent(new LoggingEventData
43+
DateTime timestamp = localTime.ToUniversalTime();
44+
LoggingEvent ev = new(new()
4545
{
4646
LoggerName = "aLogger",
4747
Level = Level.Log4Net_Debug,
@@ -56,11 +56,11 @@ public void SerializeDeserialize_BinaryFormatter()
5656
Properties = new PropertiesDictionary { ["foo"] = "bar" },
5757
});
5858

59-
var formatter = new BinaryFormatter();
60-
using var stream = new MemoryStream();
59+
BinaryFormatter formatter = new();
60+
using MemoryStream stream = new();
6161
formatter.Serialize(stream, ev);
6262
stream.Position = 0;
63-
var ev2 = (LoggingEvent)formatter.Deserialize(stream);
63+
LoggingEvent ev2 = (LoggingEvent)formatter.Deserialize(stream);
6464

6565
Assert.AreEqual("aLogger", ev2.LoggerName);
6666
Assert.AreEqual(Level.Log4Net_Debug, ev2.Level);
@@ -90,8 +90,8 @@ public void DeserializeV2()
9090
{
9191
Utils.InconclusiveOnMono();
9292
const string datPath = @"..\..\..\..\integration-testing\log4net2-SerializeEvent\SerializeV2Event.dat";
93-
using var stream = File.OpenRead(datPath);
94-
var formatter = new BinaryFormatter();
93+
using Stream stream = File.OpenRead(datPath);
94+
BinaryFormatter formatter = new();
9595
LoggingEvent ev = (LoggingEvent)formatter.Deserialize(stream);
9696
Assert.IsNotNull(ev);
9797

@@ -114,5 +114,28 @@ public void DeserializeV2()
114114
Assert.AreEqual("bar", ev.Properties["foo"]);
115115
Assert.AreEqual(localTime.ToUniversalTime(), ev.TimeStampUtc);
116116
}
117-
}
118-
#endif // NET462_OR_GREATER
117+
#endif // NET462_OR_GREATER
118+
119+
/// <summary>
120+
/// Tests <see cref="LoggingEvent.ReviseThreadName"/>
121+
/// </summary>
122+
[Test]
123+
public void ReviseThreadNameTest()
124+
{
125+
Assert.AreEqual("PoolBoy", ReviseThreadName("PoolBoy"));
126+
AssertIsCurrentThreadId(ReviseThreadName(null));
127+
AssertIsCurrentThreadId(ReviseThreadName(string.Empty));
128+
AssertIsCurrentThreadId(ReviseThreadName(".NET ThreadPool Worker"));
129+
AssertIsCurrentThreadId(ReviseThreadName(".NET TP Worker"));
130+
AssertIsCurrentThreadId(ReviseThreadName(".NET Long Running Task"));
131+
132+
static string ReviseThreadName(string? name)
133+
{
134+
return (string)typeof(LoggingEvent).GetMethod(nameof(ReviseThreadName),
135+
BindingFlags.Static | BindingFlags.NonPublic)!.Invoke(null, [name])!;
136+
}
137+
138+
static void AssertIsCurrentThreadId(string name)
139+
=> Assert.AreEqual(name, SystemInfo.CurrentThreadId.ToString(CultureInfo.InvariantCulture));
140+
}
141+
}

src/log4net.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "log4net2-SerializeEvent", "
4040
EndProject
4141
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".doc", ".doc", "{33D80AD3-8048-4220-8BB0-38E3BDE5FCF4}"
4242
ProjectSection(SolutionItems) = preProject
43-
build-preview.ps1 = build-preview.ps1
4443
..\doc\BUILDING.md = ..\doc\BUILDING.md
4544
site\xdoc\release\config-examples.xml = site\xdoc\release\config-examples.xml
4645
site\xdoc\download_log4net.xml = site\xdoc\download_log4net.xml
@@ -50,6 +49,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".doc", ".doc", "{33D80AD3-8
5049
..\doc\RELEASING.md = ..\doc\RELEASING.md
5150
EndProjectSection
5251
EndProject
52+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".scripts", ".scripts", "{050E0D0D-D916-42FD-AE58-32965336D357}"
53+
ProjectSection(SolutionItems) = preProject
54+
..\scripts\build-preview.ps1 = ..\scripts\build-preview.ps1
55+
..\scripts\install-dotnet-sdk.ps1 = ..\scripts\install-dotnet-sdk.ps1
56+
..\scripts\sign-log4net-libraries.ps1 = ..\scripts\sign-log4net-libraries.ps1
57+
..\scripts\sign-log4net-libraries.sh = ..\scripts\sign-log4net-libraries.sh
58+
EndProjectSection
59+
EndProject
5360
Global
5461
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5562
Debug|Any CPU = Debug|Any CPU

src/log4net/Core/LoggingEvent.cs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -648,40 +648,46 @@ public string? ThreadName
648648
{
649649
if (m_data.ThreadName is null && m_cacheUpdatable)
650650
{
651-
// '.NET ThreadPool Worker' appears as a default thread name in the .NET 6-7 thread pool.
652-
// '.NET TP Worker' is the default thread name in the .NET 8+ thread pool.
653-
// Prefer the numeric thread ID instead.
654-
if (Thread.CurrentThread.Name is string { Length: > 0 } threadName
655-
&& threadName is not ".NET TP Worker" or ".NET ThreadPool Worker")
656-
{
657-
m_data.ThreadName = threadName;
658-
}
659-
else
660-
{
661-
// The thread name is not available or unsuitable. Therefore we
662-
// go to the AppDomain to get the ID of the
663-
// current thread. (Why don't Threads know their own ID?)
664-
try
665-
{
666-
m_data.ThreadName = SystemInfo.CurrentThreadId.ToString(NumberFormatInfo.InvariantInfo);
667-
}
668-
catch (SecurityException)
669-
{
670-
// This security exception will occur if the caller does not have
671-
// some undefined set of SecurityPermission flags.
672-
LogLog.Debug(declaringType,
673-
"Security exception while trying to get current thread ID. Error Ignored. Empty thread name.");
674-
675-
// As a last resort use the hash code of the Thread object
676-
m_data.ThreadName = Thread.CurrentThread.GetHashCode().ToString(CultureInfo.InvariantCulture);
677-
}
678-
}
651+
m_data.ThreadName = ReviseThreadName(Thread.CurrentThread.Name);
679652
}
680653

681654
return m_data.ThreadName;
682655
}
683656
}
684657

658+
/// <summary>
659+
/// Returns a 'meaningful' name for the thread (or its Id)
660+
/// </summary>
661+
/// <param name="threadName">Name</param>
662+
/// <returns>Meaningful name</returns>
663+
private static string ReviseThreadName(string? threadName)
664+
{
665+
// '.NET ThreadPool Worker' appears as a default thread name in the .NET 6-7 thread pool.
666+
// '.NET TP Worker' is the default thread name in the .NET 8+ thread pool.
667+
// '.NET Long Running Task' is used for long running tasks
668+
// Prefer the numeric thread ID instead.
669+
if (threadName is string { Length: > 0 } name
670+
&& !name.StartsWith(".NET ", StringComparison.Ordinal))
671+
{
672+
return name;
673+
}
674+
// The thread name is not available or unsuitable, therefore we use the ManagedThreadId.
675+
try
676+
{
677+
return SystemInfo.CurrentThreadId.ToString(NumberFormatInfo.InvariantInfo);
678+
}
679+
catch (SecurityException)
680+
{
681+
// This security exception will occur if the caller does not have
682+
// some undefined set of SecurityPermission flags.
683+
LogLog.Debug(declaringType,
684+
"Security exception while trying to get current thread ID. Error Ignored. Empty thread name.");
685+
686+
// As a last resort use the hash code of the Thread object
687+
return Thread.CurrentThread.GetHashCode().ToString(CultureInfo.InvariantCulture);
688+
}
689+
}
690+
685691
/// <summary>
686692
/// Gets the name of the current user.
687693
/// </summary>
@@ -885,7 +891,7 @@ public PropertiesDictionary Properties
885891
return m_data.Properties;
886892
}
887893

888-
m_eventProperties ??= new PropertiesDictionary();
894+
m_eventProperties ??= new();
889895
return m_eventProperties;
890896
}
891897
}

0 commit comments

Comments
 (0)