Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context Data and DI support #5

Open
wants to merge 3 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
6 changes: 3 additions & 3 deletions Felfel.Logging/Felfel.Logging.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<PackageProjectUrl>https://github.com/felfel/logging-dotnet</PackageProjectUrl>
<RepositoryUrl>https://github.com/felfel/logging-dotnet</RepositoryUrl>
<PackageIconUrl>http://gravatar.com/avatar/2ac36575f81dd95ffbebc8a6eb0315e5?s=400</PackageIconUrl>
<Version>1.0.15</Version>
<AssemblyVersion>1.0.15.0</AssemblyVersion>
<FileVersion>1.0.15.0</FileVersion>
<Version>1.0.19</Version>
<AssemblyVersion>1.0.19.0</AssemblyVersion>
<FileVersion>1.0.19.0</FileVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand Down
40 changes: 40 additions & 0 deletions Felfel.Logging/ILogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
namespace Felfel.Logging
{

/// <summary>
/// Primary façade for structured logging.
/// </summary>
/// <typeparam name="T">Used to infer the <see cref="ILogger.Context"/>.</typeparam>
public interface ILogger<T> : ILogger
{
}


/// <summary>
/// Primary façade for structured logging.
/// </summary>
public interface ILogger
{
/// <summary>
/// Logger context, which will be part of the serialized data unless explicitly
/// set through the <see cref="LogEntry.Context"/> property. Identifies messages
/// that belong together (along with the <see cref="LogEntry.PayloadType"/> at finer
/// granularity).
/// </summary>
string Context { get; }

/// <summary>
/// Concatenates the the <see cref="Context"/> and the
/// <see cref="LogEntry.PayloadType"/> of a log entry
/// in order to ensure a qualified (and unique) payload type name.
/// </summary>
bool PrefixPayloadType { get; set; }

/// <summary>
/// Schedules a new log entry for logging. You probably should use one of the convenience
/// overloads such as <c>Warn</c>, <c>Info</c> or <c>Debug</c> that can be found
/// in <see cref="LoggerExtensions"/>.
/// </summary>
void Log(LogEntry entry);
}
}
6 changes: 6 additions & 0 deletions Felfel.Logging/LogEntry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

namespace Felfel.Logging
{
Expand Down Expand Up @@ -55,5 +56,10 @@ public class LogEntry
/// Optional exception information, if any.
/// </summary>
public Exception Exception { get; set; }

/// <summary>
/// Allows framework level enrichment of log entires with additional properties.
/// </summary>
public Dictionary<string, object> ContextData { get; } = new Dictionary<string, object>();
}
}
8 changes: 8 additions & 0 deletions Felfel.Logging/LogEntryDto.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Felfel.Logging
Expand Down Expand Up @@ -70,6 +71,13 @@ public class LogEntryDto
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public ExceptionInfo ExceptionInfo { get; set; }

/// <summary>
/// Optional exception information, if any. Not rendered in JSON if no
/// exception was registered.
/// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<string, object> ContextData { get; set; }
}


Expand Down
3 changes: 2 additions & 1 deletion Felfel.Logging/LogEntryParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public static LogEntryDto ParseLogEntry(LogEntry entry, string appName, string e
Message = String.IsNullOrEmpty(message) ? null : message,
PayloadType = entry.PayloadType,
Payload = payload,
ExceptionInfo = exceptionInfo
ExceptionInfo = exceptionInfo,
ContextData = entry.ContextData.Count == 0 ? null : entry.ContextData
};
}
}
Expand Down
46 changes: 36 additions & 10 deletions Felfel.Logging/LogEntrySink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,30 @@ protected virtual LogEntryDto ExtractLogEntry(LogEvent logEvent)

if (logEntry == null)
{
//something went wrong
//no log entry to unwrap - assume just a regular Serilog message that didn't come through the custom API
var logLevel = ParseLevel(logEvent.Level);
logEntry = new LogEntry
{
TimestampOverride = logEvent.Timestamp,
LogLevel = LogLevel.Fatal,
LogLevel = logLevel,
Message = logEvent.RenderMessage(),
Exception = logEvent.Exception,
Context = $"{nameof(HttpSink)}.Error",
Payload = new
{
Error = "Could not unwrap log entry.",
Message = logEvent.RenderMessage(),
Level = logEvent.Level.ToString()
}
Context = $"{AppName}.{logLevel}"
};
}

//extract all other properties and add them to the context object
var props = logEvent.Properties.Where(p => !p.Key.Equals(Logger.EntryPropertyName));
foreach (var prop in props)
{
var scalarValue = prop.Value as ScalarValue;
if (scalarValue != null)
{
//insert (override duplicate keys)
logEntry.ContextData[prop.Key] = scalarValue.Value;
}
}

var dto = LogEntryParser.ParseLogEntry(logEntry, AppName, Environment);
return dto;
}
Expand All @@ -81,6 +89,24 @@ protected virtual LogEntryDto ExtractLogEntry(LogEvent logEvent)
}
}

private LogLevel ParseLevel(LogEventLevel level)
{
switch (level)
{
case LogEventLevel.Debug:
case LogEventLevel.Verbose:
return LogLevel.Debug;
case LogEventLevel.Information:
return LogLevel.Info;
case LogEventLevel.Warning:
return LogLevel.Warning;
case LogEventLevel.Error:
return LogLevel.Error;
default:
return LogLevel.Fatal;
}
}


/// <summary>
/// Wraps an exception that occurred during logging into
Expand All @@ -100,4 +126,4 @@ protected virtual LogEntryDto ProcessLoggingException(Exception e)
};
}
}
}
}
32 changes: 24 additions & 8 deletions Felfel.Logging/Logger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@

namespace Felfel.Logging
{
public class Logger<T> : Logger, ILogger<T>
{
/// <summary>
/// Creates a new logger instance using the type name
/// as the <see cref="ILogger.Context"/>.
/// </summary>
/// <param name="prefixPayloadType">If true, the <see cref="LogEntry.PayloadType"/>
/// will be automatically prefixed with the logger's <see cref="ILogger.Context"/> in order
/// to create a qualified payload name, which reduces the risk of potential
/// duplicates across app/service.
/// </param>
public Logger(bool prefixPayloadType = true) : base(typeof(T).Name, prefixPayloadType)
{
}
}

/// <summary>
/// Primary façade for structured logging.
/// <remarks>Having static builder methods is not a good practice, but will do for now given that
/// we do not leverate dependency injection on our legacy infrastructure. You probably should not
/// duplicate this pattern.</remarks>
/// </summary>
public class Logger
public class Logger : ILogger
{
internal const string EntryPropertyName = nameof(LogEntry);

Expand All @@ -31,9 +47,9 @@ public class Logger
/// <see cref="LogEntry.PayloadType"/> of a log entry
/// in order to ensure a qualified (and unique) payload type name.
/// </summary>
public bool PrefixPayloadType { get; }
public bool PrefixPayloadType { get; set; }

private Logger(string context, bool prefixPayloadType)
protected Logger(string context, bool prefixPayloadType)
{
Context = context;
PrefixPayloadType = prefixPayloadType;
Expand All @@ -50,9 +66,9 @@ private Logger(string context, bool prefixPayloadType)
/// to create a qualified payload name, which reduces the risk of potential
/// duplicates across app/service.
/// </param>
public static Logger Create<T>(bool prefixPayloadType = true)
public static ILogger<T> Create<T>(bool prefixPayloadType = true)
{
return Create(typeof(T), prefixPayloadType);
return new Logger<T>(prefixPayloadType);
}

/// <summary>
Expand All @@ -67,9 +83,9 @@ public static Logger Create<T>(bool prefixPayloadType = true)
/// to create a qualified payload name, which reduces the risk of potential
/// duplicates across app/service.
/// </param>
public static Logger Create(Type contextType, bool prefixPayloadType = true)
public static ILogger Create(Type contextType, bool prefixPayloadType = true)
{
return Create(contextType.Name);
return Create(contextType.Name, prefixPayloadType);
}

/// <summary>
Expand All @@ -83,7 +99,7 @@ public static Logger Create(Type contextType, bool prefixPayloadType = true)
/// to create a qualified payload name, which reduces the risk of potential
/// duplicates across app/service.
/// </param>
public static Logger Create(string context = "", bool prefixPayloadType = true)
public static ILogger Create(string context = "", bool prefixPayloadType = true)
{
return new Logger(context, prefixPayloadType);
}
Expand Down
43 changes: 21 additions & 22 deletions Felfel.Logging/LoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ namespace Felfel.Logging
/// </summary>
public static class LoggerExtensions
{
private static void WriteEntry(Logger logger, LogLevel level, string payloadType, object data,
Exception exception, string message)
private static void WriteEntry(ILogger logger, LogLevel level, string payloadType, object data, Exception exception, string message)
{
LogEntry entry = new LogEntry
{
Expand All @@ -22,102 +21,102 @@ private static void WriteEntry(Logger logger, LogLevel level, string payloadType
logger.Log(entry);
}

public static void Debug(this Logger logger, string message)
public static void Debug(this ILogger logger, string message)
{
WriteEntry(logger, LogLevel.Debug, null, null, null, message);
}

public static void Debug(this Logger logger, string payloadType, object data, string message = null)
public static void Debug(this ILogger logger, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Debug, payloadType, data, null, message);
}

public static void Debug(this Logger logger, Exception exception, string message = null)
public static void Debug(this ILogger logger, Exception exception, string message = null)
{
WriteEntry(logger, LogLevel.Debug, "", null, exception, message);
}

public static void Debug(this Logger logger, Exception exception, string payloadType, object data, string message = null)
public static void Debug(this ILogger logger, Exception exception, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Debug, payloadType, data, exception, message);
}

public static void Information(this Logger logger, string message)
public static void Information(this ILogger logger, string message)
{
WriteEntry(logger, LogLevel.Info, null, null, null, message);
}

public static void Information(this Logger logger, string payloadType, object data, string message = null)
public static void Information(this ILogger logger, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Info, payloadType, data, null, message);
}

public static void Information(this Logger logger, Exception exception, string message = null)
public static void Information(this ILogger logger, Exception exception, string message = null)
{
WriteEntry(logger, LogLevel.Info, "", null, exception, message);
}

public static void Information(this Logger logger, Exception exception, string payloadType, object data, string message = null)
public static void Information(this ILogger logger, Exception exception, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Info, payloadType, data, exception, message);
}

public static void Warning(this Logger logger, string message)
public static void Warning(this ILogger logger, string message)
{
WriteEntry(logger, LogLevel.Warning, null, null, null, message);
}

public static void Warning(this Logger logger, string payloadType, object data, string message = null)
public static void Warning(this ILogger logger, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Warning, payloadType, data, null, message);
}

public static void Warning(this Logger logger, Exception exception, string message = null)
public static void Warning(this ILogger logger, Exception exception, string message = null)
{
WriteEntry(logger, LogLevel.Warning, "", null, exception, message);
}

public static void Warning(this Logger logger, Exception exception, string payloadType, object data, string message = null)
public static void Warning(this ILogger logger, Exception exception, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Warning, payloadType, data, exception, message);
}

public static void Error(this Logger logger, string message)
public static void Error(this ILogger logger, string message)
{
WriteEntry(logger, LogLevel.Error, null, null, null, message);
}

public static void Error(this Logger logger, string payloadType, object data, string message = null)
public static void Error(this ILogger logger, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Error, payloadType, data, null, message);
}

public static void Error(this Logger logger, Exception exception, string message = null)
public static void Error(this ILogger logger, Exception exception, string message = null)
{
WriteEntry(logger, LogLevel.Error, "", null, exception, message);
}

public static void Error(this Logger logger, Exception exception, string payloadType, object data, string message = null)
public static void Error(this ILogger logger, Exception exception, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Error, payloadType, data, exception, message);
}

public static void Fatal(this Logger logger, string message)
public static void Fatal(this ILogger logger, string message)
{
WriteEntry(logger, LogLevel.Fatal, null, null, null, message);
}

public static void Fatal(this Logger logger, string payloadType, object data, string message = null)
public static void Fatal(this ILogger logger, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Fatal, payloadType, data, null, message);
}

public static void Fatal(this Logger logger, Exception exception, string message = null)
public static void Fatal(this ILogger logger, Exception exception, string message = null)
{
WriteEntry(logger, LogLevel.Fatal, "", null, exception, message);
}

public static void Fatal(this Logger logger, Exception exception, string payloadType, object data, string message = null)
public static void Fatal(this ILogger logger, Exception exception, string payloadType, object data, string message = null)
{
WriteEntry(logger, LogLevel.Fatal, payloadType, data, exception, message);
}
Expand Down