Skip to content
This repository has been archived by the owner on Feb 27, 2022. It is now read-only.

Made changes to allow passing number of frames to be skipped for calc… #8

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
push:
branches: [ "master" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ "master" ]
schedule:
- cron: '41 18 * * 4'

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write

strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

steps:
- name: Checkout repository
uses: actions/checkout@v3

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.

# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# queries: security-extended,security-and-quality


# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2

# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,29 @@ This can let you specify a filter on an appender in Log4Net. The example below,
</log4net>
```

#### Preserving Stacktrace information
Passing `3 (Default is 3)` for skipFrames, will load the relative number of frames from the call stack. This is required to be configurable to allow the caller class, method name, line number to be pulled. This is best used in scenarios where you have your own custom wrapper over Serilog or log4net.

e.g.

```csharp
var log = new LoggerConfiguration()
.WriteTo.Log4Net(skipFrames: 3)
.CreateLogger();
```
This change allows the correct values returned for log4net conversion template.

```xml
<conversionPattern value="[%utcdate{yyyy-MM-ddTHH:mm:ssZ}}][%level][%thread][%C(%M),%line][%message]%newline"/>
```
Output received in logs when skipFrames is 3
```txt
[2017-12-19T16:51:53Z}][INFO][1][Serilog.Sinks.Log4Net.Sample.Program(Main),39][SERILOG-custom property added for "ikson01" { firstname: "john", lastname: "doe" }]
```
Output received in logs when skipFrames is 0
```txt
[2017-12-19T16:53:12Z}][INFO][1][Serilog.Core.Sinks.SafeAggregateSink(Emit),0][SERILOG-custom property added for "ikson01" { firstname: "john", lastname: "doe" }]
```

[(More information.)](http://nblumhardt.com/2013/06/serilog-sinks-log4net/)

57 changes: 38 additions & 19 deletions Sample/Serilog.Sinks.Log4Net.Sample/App.config
Original file line number Diff line number Diff line change
@@ -1,25 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<!--<appender name="ConsoleAppender" type="log4net.Appender.ColoredConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[LOG4NET] %date (%thread) %-5level %logger - %message%newline" />
</layout>
</appender>
<root>
<level value="INFO" />
<appender-ref ref="ConsoleAppender" />
</root>
</log4net>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Serilog" publicKeyToken="24c2f752a8e58a10" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</appender>-->

<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C:\Logs\Log4Net.Sample.txt"/>
<appendToFile value="true"/>
<encoding value="utf-8"/>
<rollingStyle value="Size"/>
<maximumFileSize value="1MB"/>
<!--
1. If set to zero, then there will be no backup files and the log file will be truncated when it reaches MaxFileSize.
2. If a negative number is supplied then no deletions will be made. Note that this could result in very slow performance
as a large number of files are rolled over unless CountDirection is used.
-->
<maxSizeRollBackups value="10"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%utcdate{yyyy-MM-ddTHH:mm:ssZ}}][%level][%thread][%C(%M),%line][%message]%newline"/>
</layout>
</appender>


<root>
<level value="ALL" />
<appender-ref ref="LogFileAppender" />
</root>
</log4net>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Serilog" publicKeyToken="24c2f752a8e58a10" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
14 changes: 9 additions & 5 deletions Sample/Serilog.Sinks.Log4Net.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading.Tasks;
using log4net;
using log4net.Config;
using Serilog.Context;
using Serilog.Enrichers;

namespace Serilog.Sinks.Log4Net.Sample
Expand All @@ -22,17 +23,20 @@ private static void Main()
Log.Logger = new LoggerConfiguration()
.Enrich.With(new ThreadIdEnricher())
.WriteTo.ColoredConsole(outputTemplate: OutputTemplate)
.WriteTo.Log4Net()
.WriteTo.Log4Net(skipFrames: 3)
.CreateLogger();


var log4NetLogger = LogManager.GetLogger(typeof (Program));
var log4NetLogger = LogManager.GetLogger(typeof(Program));
var serilogLogger = Log.ForContext<Program>();

var username = Environment.UserName;

log4NetLogger.InfoFormat("Hello from log4net, running as {0}!", username);
serilogLogger.Information("Hello from Serilog, running as {Username}!", username);
log4NetLogger.InfoFormat("LOG4NET---Hello, running as {0}!", username);

serilogLogger.Information("SERILOG---Hello, running as {Username}!", username);

var p = new { firstname = "john", lastname = "doe" };
serilogLogger.Information("SERILOG-custom property added for {user} {@p}", username, p);

Console.ReadKey(true);
}
Expand Down
2 changes: 1 addition & 1 deletion Sample/Serilog.Sinks.Log4Net.Sample/packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.5" targetFramework="net451" />
<package id="log4net" version="2.0.10" targetFramework="net451" />
<package id="Serilog" version="2.2.1" targetFramework="net451" />
<package id="Serilog.Enrichers.Thread" version="2.0.0" targetFramework="net451" />
<package id="Serilog.Sinks.ColoredConsole" version="2.0.0" targetFramework="net451" />
Expand Down
19 changes: 12 additions & 7 deletions src/Serilog.Sinks.Log4Net/LoggerConfigurationLog4NetExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,30 @@ public static class LoggerConfigurationLog4NetExtensions
/// Adds a sink that writes log events as documents to log4net.
/// </summary>
/// <param name="loggerConfiguration">The logger configuration.</param>
/// <param name="defaultLoggerName">If events are logged using Serilog's <see cref="ILogger.ForContext{T}"/> method,
/// <param name="defaultLoggerName">If events are logged using Serilog's <see cref="ILogger.ForContext{T}" /> method,
/// the type name will be used as the logger name (in line with log4net's behaviour. If no context type is specified,
/// Serilog will use this value (which defaults to <code>"serilog"</code>) as the logger name.</param>
/// <param name="restrictedToMinimumLevel">The minimum log event level required in order to write an event to the sink.</param>
/// <param name="formatProvider">Supplies culture-specific formatting information, or null.</param>
/// <param name="supplyContextMessage">Whether to supply a marker context message to use during the log. See: https://logging.apache.org/log4net/release/manual/contexts.html#stacks </param>
/// <returns>Logger configuration, allowing configuration to continue.</returns>
/// <param name="supplyContextMessage">Whether to supply a marker context message to use during the log. See: https://logging.apache.org/log4net/release/manual/contexts.html#stacks</param>
/// <param name="skipFrames">The skip frames.</param>
/// <returns>
/// Logger configuration, allowing configuration to continue.
/// </returns>
/// <exception cref="ArgumentNullException">A required parameter is null.</exception>
public static LoggerConfiguration Log4Net(
this LoggerSinkConfiguration loggerConfiguration,
string defaultLoggerName = "serilog",
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
IFormatProvider formatProvider = null,
bool supplyContextMessage = false)
bool supplyContextMessage = false, int skipFrames = 3)
{
if (loggerConfiguration == null) throw new ArgumentNullException("loggerConfiguration");
if (defaultLoggerName == null) throw new ArgumentNullException("defaultLoggerName");
if (loggerConfiguration == null)
throw new ArgumentNullException("loggerConfiguration");
if (defaultLoggerName == null)
throw new ArgumentNullException("defaultLoggerName");

return loggerConfiguration.Sink(new Log4NetSink(defaultLoggerName, formatProvider, supplyContextMessage), restrictedToMinimumLevel);
return loggerConfiguration.Sink(new Log4NetSink(defaultLoggerName, formatProvider, supplyContextMessage, skipFrames), restrictedToMinimumLevel);
}

/// <summary>
Expand Down
36 changes: 26 additions & 10 deletions src/Serilog.Sinks.Log4Net/Sinks/Log4Net/Log4NetSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,32 +13,47 @@
// limitations under the License.

using System;
using System.Diagnostics;
using System.Security.AccessControl;
using Serilog.Core;
using Serilog.Debugging;
using Serilog.Events;
using log4net;
using log4net.Core;

namespace Serilog.Sinks.Log4Net
{
class Log4NetSink : ILogEventSink
{
class NullDisposable : IDisposable
{
public void Dispose(){}
public void Dispose() { }
}

private const string ContextMessage = "Serilog-Log4NetSink";
private const int DefaultSkipFrames = 3;
readonly string _defaultLoggerName;
readonly IFormatProvider _formatProvider;
private readonly bool _supplyContextMessage;
private readonly int _skipFrames = 3;

public Log4NetSink(string defaultLoggerName, IFormatProvider formatProvider = null, bool supplyContextMessage = false)
public Log4NetSink(string defaultLoggerName, IFormatProvider formatProvider = null, bool supplyContextMessage = false, int skipFrames = DefaultSkipFrames)
{
if (defaultLoggerName == null) throw new ArgumentNullException("defaultLoggerName");
if (defaultLoggerName == null)
throw new ArgumentNullException(nameof(defaultLoggerName));
_defaultLoggerName = defaultLoggerName;
_formatProvider = formatProvider;
_supplyContextMessage = supplyContextMessage;
if (_skipFrames < 0)
{
throw new ArgumentException(nameof(skipFrames));
}
_skipFrames = skipFrames;
}

private Type GetCallingType()
{
return new StackFrame(_skipFrames, false).GetMethod().DeclaringType;
}

public void Emit(LogEvent logEvent)
Expand All @@ -57,30 +72,31 @@ public void Emit(LogEvent logEvent)
var exception = logEvent.Exception;

var logger = LogManager.GetLogger(loggerName);

using (_supplyContextMessage ? ThreadContext.Stacks["NDC"].Push(ContextMessage) : new NullDisposable())
{
var type = GetCallingType();
switch (logEvent.Level)
{
case LogEventLevel.Verbose:
case LogEventLevel.Debug:
logger.Debug(message, exception);
logger.Logger.Log(type, Level.Debug, message, exception);
break;
case LogEventLevel.Information:
logger.Info(message, exception);
logger.Logger.Log(type, Level.Info, message, exception);
break;
case LogEventLevel.Warning:
logger.Warn(message, exception);
logger.Logger.Log(type, Level.Warn, message, exception);
break;
case LogEventLevel.Error:
logger.Error(message, exception);
logger.Logger.Log(type, Level.Error, message, exception);
break;
case LogEventLevel.Fatal:
logger.Fatal(message, exception);
logger.Logger.Log(type, Level.Fatal, message, exception);
break;
default:
SelfLog.WriteLine("Unexpected logging level, writing to log4net as Info");
logger.Info(message, exception);
logger.Logger.Log(type, Level.Info, message, exception);
break;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Serilog.Sinks.Log4Net/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.5" targetFramework="net45" />
<package id="log4net" version="2.0.10" targetFramework="net45" />
<package id="Serilog" version="2.2.1" targetFramework="net45" />
</packages>