-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit cacaab1
Showing
18 changed files
with
3,939 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
bin | ||
obj | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.Logging.Abstractions.Internal; | ||
|
||
namespace sttz.ConsoleLogger | ||
{ | ||
|
||
public class ConsoleLogger : ILogger | ||
{ | ||
public Func<string, LogLevel, bool> Filter | ||
{ | ||
get { return _filter; } | ||
set { | ||
if (value == null) { | ||
throw new ArgumentNullException(nameof(value)); | ||
} | ||
_filter = value; | ||
} | ||
} | ||
Func<string, LogLevel, bool> _filter; | ||
|
||
public string Name { get; } | ||
|
||
internal IExternalScopeProvider ScopeProvider { get; set; } | ||
|
||
public ConsoleLogger(string name, Func<string, LogLevel, bool> filter, bool includeScopes) | ||
: this(name, filter, includeScopes ? new LoggerExternalScopeProvider() : null) | ||
{ | ||
} | ||
|
||
internal ConsoleLogger(string name, Func<string, LogLevel, bool> filter, IExternalScopeProvider scopeProvider) | ||
{ | ||
if (name == null) throw new ArgumentNullException(nameof(name)); | ||
|
||
Name = name; | ||
Filter = filter ?? ((category, logLevel) => true); | ||
ScopeProvider = scopeProvider; | ||
} | ||
|
||
public IDisposable BeginScope<TState>(TState state) | ||
{ | ||
return ScopeProvider?.Push(state) ?? NullScope.Instance; | ||
} | ||
|
||
public bool IsEnabled(LogLevel logLevel) | ||
{ | ||
if (logLevel == LogLevel.None) { | ||
return false; | ||
} | ||
|
||
return Filter(Name, logLevel); | ||
} | ||
|
||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | ||
{ | ||
if (!IsEnabled(logLevel)) return; | ||
if (formatter == null) throw new ArgumentNullException(nameof(formatter)); | ||
|
||
var message = formatter(state, exception); | ||
if (!string.IsNullOrEmpty(message) || exception != null) { | ||
var builder = recycledBuilder; | ||
if (builder == null) { | ||
builder = new StringBuilder(); | ||
} | ||
|
||
var levelColor = GetLogLevelColor(logLevel); | ||
if (levelColor != null) { | ||
builder.Append("<"); | ||
builder.Append(levelColor); | ||
builder.Append(">"); | ||
} | ||
|
||
var levelPrefix = GetLogLevelPrefix(logLevel); | ||
if (levelPrefix != null) { | ||
builder.Append(levelPrefix); | ||
} | ||
|
||
if (!string.IsNullOrEmpty(message)) { | ||
builder.Append(message); | ||
} else if (exception != null) { | ||
builder.Append(exception.ToString()); | ||
} | ||
|
||
if (levelColor != null) { | ||
builder.Append("</"); | ||
builder.Append(levelColor); | ||
builder.Append(">"); | ||
} | ||
|
||
if (builder.Length > 0) { | ||
var input = builder.ToString(); | ||
var colored = ParseColorString(input); | ||
WriteColorLine(colored); | ||
} | ||
|
||
builder.Clear(); | ||
if (builder.Capacity > 1024) { | ||
builder.Capacity = 1024; | ||
} | ||
recycledBuilder = builder; | ||
} | ||
} | ||
|
||
static StringBuilder recycledBuilder; | ||
|
||
static string GetLogLevelPrefix(LogLevel logLevel) | ||
{ | ||
switch (logLevel) { | ||
case LogLevel.Trace: | ||
return "TRACE: "; | ||
case LogLevel.Debug: | ||
return "DEBUG: "; | ||
case LogLevel.Information: | ||
return null; | ||
case LogLevel.Warning: | ||
return "WARN: "; | ||
case LogLevel.Error: | ||
return "ERROR: "; | ||
case LogLevel.Critical: | ||
return "ERROR: "; | ||
default: | ||
throw new ArgumentOutOfRangeException(nameof(logLevel)); | ||
} | ||
} | ||
|
||
static string GetLogLevelColor(LogLevel logLevel) | ||
{ | ||
switch (logLevel) { | ||
case LogLevel.Trace: | ||
return "gray"; | ||
case LogLevel.Debug: | ||
return "gray"; | ||
case LogLevel.Information: | ||
return null; | ||
case LogLevel.Warning: | ||
return "yellow"; | ||
case LogLevel.Error: | ||
return "red"; | ||
case LogLevel.Critical: | ||
return "red"; | ||
default: | ||
throw new ArgumentOutOfRangeException(nameof(logLevel)); | ||
} | ||
} | ||
|
||
static void WriteColorLine(IEnumerable<ColorString> input) | ||
{ | ||
foreach (var fragment in input) { | ||
if (fragment.fgColor != null) Console.ForegroundColor = fragment.fgColor.Value; | ||
if (fragment.bgColor != null) Console.BackgroundColor = fragment.bgColor.Value; | ||
Console.Write(fragment.text); | ||
} | ||
Console.WriteLine(); | ||
Console.ResetColor(); | ||
} | ||
|
||
struct ColorString | ||
{ | ||
public string text; | ||
public ConsoleColor? fgColor; | ||
public ConsoleColor? bgColor; | ||
} | ||
|
||
static Regex ColorTagRegex = new Regex(@"<(\/?)(\w+)(?: bg=(\w+))?>"); | ||
|
||
static IEnumerable<ColorString> ParseColorString(string input) | ||
{ | ||
if (string.IsNullOrEmpty(input)) { | ||
return new ColorString[] { new ColorString() { text = "" } }; | ||
} | ||
|
||
var matches = ColorTagRegex.Matches(input); | ||
if (matches.Count == 0) { | ||
return new ColorString[] { new ColorString() { text = input } }; | ||
} | ||
|
||
// test: <red bg=white>hello <blue>world</blue></red>! | ||
var colors = new List<ColorString>(); | ||
var currentColors = new ColorString(); | ||
colors.Add(currentColors); | ||
|
||
var pos = 0; | ||
var result = new List<ColorString>(matches.Count); | ||
foreach (Match match in matches) { | ||
if (match.Index > pos) { | ||
result.Add(new ColorString() { | ||
text = input.Substring(pos, match.Index - pos), | ||
fgColor = currentColors.fgColor, | ||
bgColor = currentColors.bgColor | ||
}); | ||
} | ||
|
||
if (match.Groups[1].Length == 0) { | ||
currentColors = new ColorString() { | ||
fgColor = ParseColor(match.Groups[2].Value) ?? currentColors.fgColor, | ||
bgColor = ParseColor(match.Groups[3].Value) ?? currentColors.bgColor | ||
}; | ||
colors.Add(currentColors); | ||
} else { | ||
if (colors.Count == 1) { | ||
throw new ArgumentException($"End console color tag </{match.Groups[2].Value}> before any opening tags"); | ||
} | ||
var color = ParseColor(match.Groups[2].Value); | ||
var current = colors[colors.Count - 1].fgColor; | ||
if (colors[colors.Count - 1].fgColor != color) { | ||
throw new ArgumentException($"Umatched console color tag: Expected {current}, got {color}"); | ||
} | ||
colors.RemoveAt(colors.Count - 1); | ||
currentColors = colors[colors.Count - 1]; | ||
} | ||
|
||
pos = match.Index + match.Length; | ||
} | ||
|
||
if (pos < input.Length) { | ||
result.Add(new ColorString() { | ||
text = input.Substring(pos), | ||
fgColor = currentColors.fgColor, | ||
bgColor = currentColors.bgColor | ||
}); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
static ConsoleColor? ParseColor(string input) | ||
{ | ||
switch (input.ToLower()) { | ||
case "black": | ||
return ConsoleColor.Black; | ||
case "darkblue": | ||
return ConsoleColor.DarkBlue; | ||
case "darkgreen": | ||
return ConsoleColor.DarkGreen; | ||
case "darkcyan": | ||
return ConsoleColor.DarkCyan; | ||
case "darkred": | ||
return ConsoleColor.DarkRed; | ||
case "darkmagenta": | ||
return ConsoleColor.DarkMagenta; | ||
case "darkyellow": | ||
return ConsoleColor.DarkYellow; | ||
case "gray": | ||
return ConsoleColor.Gray; | ||
case "darkgray": | ||
return ConsoleColor.DarkGray; | ||
case "blue": | ||
return ConsoleColor.Blue; | ||
case "green": | ||
return ConsoleColor.Green; | ||
case "cyan": | ||
return ConsoleColor.Cyan; | ||
case "red": | ||
return ConsoleColor.Red; | ||
case "magenta": | ||
return ConsoleColor.Magenta; | ||
case "yellow": | ||
return ConsoleColor.Yellow; | ||
case "white": | ||
return ConsoleColor.White; | ||
case "inherit": | ||
return null; | ||
case "": | ||
return null; | ||
default: | ||
throw new ArgumentException("Invalid console color name: " + input); | ||
} | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
using System; | ||
using Microsoft.Extensions.Logging; | ||
|
||
namespace sttz.ConsoleLogger | ||
{ | ||
|
||
[ProviderAlias("Console")] | ||
public class ConsoleLoggerProvider : ILoggerProvider, ISupportExternalScope | ||
{ | ||
static readonly Func<string, LogLevel, bool> trueFilter = (cat, level) => true; | ||
|
||
Func<string, LogLevel, bool> filter; | ||
bool includeScopes; | ||
IExternalScopeProvider scopeProvider; | ||
|
||
public ConsoleLoggerProvider(Func<string, LogLevel, bool> filter = null, bool includeScopes = false) | ||
{ | ||
this.filter = filter ?? trueFilter; | ||
this.includeScopes = includeScopes; | ||
} | ||
|
||
public ILogger CreateLogger(string categoryName) | ||
{ | ||
return new ConsoleLogger(categoryName, filter, includeScopes ? scopeProvider : null); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
// NOP | ||
} | ||
|
||
public void SetScopeProvider(IExternalScopeProvider scopeProvider) | ||
{ | ||
this.scopeProvider = scopeProvider; | ||
} | ||
} | ||
|
||
public static class ConsoleLoggerExtensions | ||
{ | ||
public static ILoggerFactory AddConsole(this ILoggerFactory factory) | ||
{ | ||
return factory.AddConsole(includeScopes: false); | ||
} | ||
|
||
public static ILoggerFactory AddConsole(this ILoggerFactory factory, bool includeScopes) | ||
{ | ||
factory.AddConsole((n, l) => l >= LogLevel.Information, includeScopes); | ||
return factory; | ||
} | ||
|
||
public static ILoggerFactory AddConsole(this ILoggerFactory factory, LogLevel minLevel) | ||
{ | ||
factory.AddConsole(minLevel, includeScopes: false); | ||
return factory; | ||
} | ||
|
||
public static ILoggerFactory AddConsole( | ||
this ILoggerFactory factory, | ||
LogLevel minLevel, | ||
bool includeScopes) | ||
{ | ||
factory.AddConsole((category, logLevel) => logLevel >= minLevel, includeScopes); | ||
return factory; | ||
} | ||
|
||
public static ILoggerFactory AddConsole( | ||
this ILoggerFactory factory, | ||
Func<string, LogLevel, bool> filter) | ||
{ | ||
factory.AddConsole(filter, includeScopes: false); | ||
return factory; | ||
} | ||
|
||
public static ILoggerFactory AddConsole( | ||
this ILoggerFactory factory, | ||
Func<string, LogLevel, bool> filter, | ||
bool includeScopes) | ||
{ | ||
factory.AddProvider(new ConsoleLoggerProvider(filter, includeScopes)); | ||
return factory; | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.