Skip to content

Commit

Permalink
Adding support for global and thread context stacks for log4net
Browse files Browse the repository at this point in the history
Adding support for global and thread context stacks for log4net.
This is for Azure#287
  • Loading branch information
pattisapu01 committed Nov 17, 2018
1 parent 7fd49c7 commit c9c4d65
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 9 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -613,18 +613,21 @@ namespace AspNetCoreEventFlow
This input enables capturing diagnostic data sent to the [Log4net project](https://logging.apache.org/log4net/).

*Configuration example*
The Log4net input has one configuration, the Log4net Level:
The Log4net input has one required configuration, the Log4net Level and 2 optional configurations: globalContextName and logicalThreadContextName
```json
{
"type": "Log4net",
"logLevel": "Debug"
"logLevel": "Debug",
"globalContextName": "MyGlobalContext",
"logicalThreadContextName": "MyLogicalThreadContext"
}
```
| Field | Values/Types | Required | Description |
| :---- | :-------------- | :------: | :---------- |
| `type` | "Log4net" | Yes | Specifies the output type. For this output, it must be "Log4net". |
| `logLevel` | "Debug", "Info", "Warn", "Error", or "Fatal" | Yes | Specifies minimum [Log4net Level](https://logging.apache.org/log4net/log4net-1.2.11/release/sdk/log4net.Core.Level.html) for captured events. For example, if the level is `Warn`, the input will capture events with levels equal to `Warn`, `Error`, or `Fatal`. |

| `globalContextName` | "<User Defined>" | No | Allows clients to supply custom properties on the global context. These will be tagged to every message |
| `logicalThreadContextName` | "<User Defined>" | No | Allows clients to supply custom properties at the logical thread context. These will be tagged to every message |

*Example: instantiating a Log4net logger that uses EventFlow Log4net input*

Expand All @@ -649,7 +652,45 @@ namespace ConsoleApp2
}
}
```
*Example: instantiating a Log4net logger that uses EventFlow Log4net input with NDC thread and global contexts*

```csharp
using System;
using Microsoft.Diagnostics.EventFlow;
using log4net;

namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
using (DiagnosticPipeline pipeline = DiagnosticPipelineFactory.CreatePipeline(".\\eventFlowConfig.json"))
{
#region log4net input with stacks and global context
var logger = LogManager.GetLogger("EventFlowRepo", "MY_LOGGER_NAME");
GlobalContext.Properties["GlobalContext"] = "My Global Context";

using (ThreadContext.Stacks["NDC"].Push("Thread Context-1"))
{
using (ThreadContext.Stacks["NDC"].Push("Thread Context-1-1"))
{
using (LogicalThreadContext.Stacks["LogicalThreadContext"].Push("Logical Thread Context-1-1-1"))
{
logger.Debug("Hey! Listen!", new Exception("uhoh"));
}
logger.Info("From Thread Context 1-1");
}
logger.Info("From Thread Context 1");
}
#endregion

Console.ReadLine();
}
}
}
}
```
#### NLog

*Nuget package:* [**Microsoft.Diagnostics.EventFlow.Inputs.NLog**](https://www.nuget.org/packages/Microsoft.Diagnostics.EventFlow.Inputs.NLog/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,22 @@
<ItemGroup>
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Core\Microsoft.Diagnostics.EventFlow.Core.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Inputs.EventSource\Microsoft.Diagnostics.EventFlow.Inputs.EventSource.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Inputs.Log4net\Microsoft.Diagnostics.EventFlow.Inputs.Log4net.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Inputs.PerformanceCounter\Microsoft.Diagnostics.EventFlow.Inputs.PerformanceCounter.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Inputs.Trace\Microsoft.Diagnostics.EventFlow.Inputs.Trace.csproj" />
<ProjectReference Include="..\Microsoft.Diagnostics.EventFlow.Outputs.StdOutput\Microsoft.Diagnostics.EventFlow.Outputs.StdOutput.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="log4net" Version="2.0.8" />
<PackageReference Include="System.Net.Http" Version="4.3.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="1.1.1" />
</ItemGroup>

<ItemGroup>
<None Update="eventFlowConfig.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------
using log4net;
using System;
using System.Diagnostics;
using System.Net.Http;
Expand All @@ -12,8 +13,28 @@ class Program
{
static void Main(string[] args)
{
using (DiagnosticPipeline pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json"))
using (DiagnosticPipeline pipeline = DiagnosticPipelineFactory.CreatePipeline(".\\eventFlowConfig.json"))
{
#region log4net input with stacks and global context
var logger = LogManager.GetLogger("EventFlowRepo", "MY_LOGGER_NAME");
GlobalContext.Properties["GlobalContext"] = "My Global Context";

using (ThreadContext.Stacks["NDC"].Push("Thread Context-1"))
{
using (ThreadContext.Stacks["NDC"].Push("Thread Context-1-1"))
{
using (LogicalThreadContext.Stacks["LogicalThreadContext"].Push("Logical Thread Context-1-1-1"))
{
logger.Debug("Hey! Listen!", new Exception("uhoh"));
}
logger.Info("From Thread Context 1-1");
}
logger.Info("From Thread Context 1");
}
#endregion

Console.ReadLine();

// Build up the pipeline
Console.WriteLine("Pipeline is created.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
//}
]
},
{
"type": "Log4net",
"logLevel": "Debug",
"globalContextName": "GlobalContext",
"logicalThreadContextName": "LogicalThreadContext"
},
{
"type": "PerformanceCounter",
"counters": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@ public class Log4netConfiguration : ItemConfiguration
public string LogLevel { get; set; }

public Level Log4netLevel { get; set; }

public string GlobalContextName { get; set; }

public string LogicalThreadContextName { get; set; }
}
}
31 changes: 26 additions & 5 deletions src/Microsoft.Diagnostics.EventFlow.Inputs.Log4net/Log4netInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class Log4netInput : AppenderSkeleton, IObservable<EventData>, IDisposabl
private EventFlowSubject<EventData> subject;
private Log4netConfiguration _log4NetInputConfiguration;
private Hierarchy eventFlowRepo;

public Log4netInput(IConfiguration configuration, IHealthReporter healthReporter)
{
Requires.NotNull(healthReporter, nameof(healthReporter));
Expand Down Expand Up @@ -84,7 +84,7 @@ private void Initialize(Log4netConfiguration myLog4NetConfig, IHealthReporter my
eventFlowRepo = (Hierarchy) LogManager.GetRepository("EventFlowRepo");
}
}

protected override void Append(LoggingEvent loggingEvent)
{
if (loggingEvent == null || !IsEnabledFor(loggingEvent.Level))
Expand All @@ -106,14 +106,14 @@ private EventData ToEventData(LoggingEvent loggingEvent)
Timestamp = loggingEvent.TimeStamp,
Level = ToLogLevel[loggingEvent.Level],
Keywords = 0,
Payload = { { "Message", loggingEvent.MessageObject } }
Payload = { { "Message", $"{AttachGlobalContextProps()} {AttachThreadContextProps()} {AttachLogicalThreadContextProps()} {loggingEvent.MessageObject}" } }
};

if (loggingEvent.ExceptionObject != null)
{
eventData.Payload.Add("Exception", loggingEvent.ExceptionObject);
}

foreach (var key in loggingEvent.Properties.GetKeys())
{
try
Expand All @@ -129,6 +129,27 @@ private EventData ToEventData(LoggingEvent loggingEvent)
return eventData;
}

private string AttachGlobalContextProps()
{
return $"[{log4net.GlobalContext.Properties[this._log4NetInputConfiguration.GlobalContextName ?? "GlobalContext"]?.ToString()}]";
}

private string AttachThreadContextProps()
{
string result = null;
var keys = log4net.ThreadContext.Properties.GetKeys();
foreach (var item in keys)
{
result += $"[{log4net.ThreadContext.Properties[item]}]";
}
return result;
}

private string AttachLogicalThreadContextProps()
{
return $"[{log4net.LogicalThreadContext.Properties[this._log4NetInputConfiguration.LogicalThreadContextName ?? "LogicalThreadContext"]?.ToString()}]";
}

/// <inheritdoc/>
public virtual void Dispose()
{
Expand Down

0 comments on commit c9c4d65

Please sign in to comment.