Last active
December 22, 2019 23:53
-
-
Save dbones/6571630 to your computer and use it in GitHub Desktop.
Building on my previous Gist, <https://gist.github.com/dbones/6568314>, this looks into a process worker to queue the tasks and run on them on another thread (you could easily use the Thread Queue instead)this offers a simple interface around the worker to allow for an easy swap at any time
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
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace TestConfuring | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var worker = new ProcessWorker(); | |
Log log = new Log(new ConfigLog(), worker); | |
ILogger logger = log.GetLogger("root"); | |
logger.Append("test1", LogLevel.Fatal); | |
logger.Append("test2", LogLevel.Fatal); | |
logger.Append("test3", LogLevel.Error); | |
logger.Append("test4", LogLevel.Fatal); | |
logger.Append("test5", LogLevel.Infomation); | |
logger.Append("test6", LogLevel.Debug); | |
logger.Append("test7", LogLevel.Trace); | |
logger.Append("test8", LogLevel.Warning); | |
logger.Append("test9", LogLevel.Fatal); | |
logger.Append("test10", LogLevel.Fatal); | |
worker.Dispose(); | |
Console.ReadLine(); | |
} | |
} | |
public interface IProcessWorker | |
{ | |
void QueueWork(Action task); | |
} | |
class ProcessWorker : IProcessWorker, IDisposable | |
{ | |
private readonly BlockingCollection<Action> _tasks; | |
private volatile bool _enabled; | |
private readonly Task _backgroundWorker; | |
public ProcessWorker() | |
{ | |
_backgroundWorker = new Task(DoWork); | |
_enabled = true; | |
_tasks = new BlockingCollection<Action>(new ConcurrentQueue<Action>()); | |
_backgroundWorker.Start(); | |
} | |
public void QueueWork(Action action) | |
{ | |
_tasks.Add(action); | |
} | |
private void DoWork() | |
{ | |
while (_enabled || _tasks.Any()) | |
{ | |
Action action; | |
while (_tasks.TryTake(out action)) | |
{ | |
Execute(action); | |
} | |
Thread.Sleep(new TimeSpan(0,0,1)); | |
} | |
} | |
protected virtual void Execute(Action action) | |
{ | |
//really try to get the actions done in a | |
//parrellel way | |
//var t = new Task(action); | |
//t.Start(); | |
action(); //logger just needs this as it will keep the items in order. | |
} | |
public void Dispose() | |
{ | |
_enabled = false; | |
_backgroundWorker.Wait(); //ensure all tasks are run first | |
} | |
} | |
/// <summary> | |
/// Setup the Configuration | |
/// </summary> | |
public interface IConfigure | |
{ | |
bool CanHandle(object item); | |
void Configure(object item); | |
} | |
/// <summary> | |
/// Setup the Configuration | |
/// </summary> | |
/// <typeparam name="T">tie the config down to a Configuration class</typeparam> | |
public interface IConfigure<in T> : IConfigure where T : IRequireConfiguring | |
{ | |
void Configure(T item); | |
} | |
public abstract class ConfigureBase<T> : IConfigure<T> where T : IRequireConfiguring | |
{ | |
public abstract void Configure(T item); | |
public virtual bool CanHandle(object item) | |
{ | |
return item is T; | |
} | |
public void Configure(object item) | |
{ | |
Configure((T)item); | |
} | |
} | |
/// <summary> | |
/// A Configuration class (this interface is used to tag the config class) | |
/// </summary> | |
public interface IRequireConfiguring | |
{ | |
} | |
public class Log | |
{ | |
private readonly IProcessWorker _worker; | |
private readonly LoggingConfiguration _config; | |
private readonly Dictionary<string, IAppender> _appenders; | |
private readonly Dictionary<string, ILogger> _loggers; | |
public Log(IConfigure configLog, IProcessWorker worker) | |
{ | |
_worker = worker; | |
_appenders = new Dictionary<string, IAppender>(); | |
_loggers = new Dictionary<string, ILogger>(); | |
_config = new LoggingConfiguration(); | |
configLog.Configure(_config); | |
foreach (var appenderConfiguration in _config.Appenders) | |
{ | |
var createMe = appenderConfiguration.AppenderType; | |
var appender = (IAppender)Activator.CreateInstance(createMe); | |
appenderConfiguration.Configure(appender); | |
_appenders.Add(appender.Name, appender); | |
} | |
foreach (var loggerConfiguration in _config.Loggers) | |
{ | |
var logger = new Logger( | |
loggerConfiguration.Name, | |
loggerConfiguration.LogLevel, | |
loggerConfiguration.Appenders.Select(x=> _appenders[x]), | |
worker); | |
_loggers.Add(logger.Name, logger); | |
} | |
} | |
public ILogger GetLogger(string name) | |
{ | |
return _loggers[name]; | |
} | |
} | |
/// <summary> | |
/// An exmaple of the Configuration being setup via code, you could easily do this via XML | |
/// </summary> | |
public class ConfigLog : ConfigureBase<LoggingConfiguration> | |
{ | |
public override void Configure(LoggingConfiguration item) | |
{ | |
item.AddAppender<FileAppenderConfiguration>( | |
config => | |
{ | |
config.Batch = 10; | |
config.Name = "file"; | |
config.LogFolder = ""; //current folder | |
}); | |
item.AddAppender<ConsoleAppenderConfiguration>( | |
config => | |
{ | |
config.Name = "console"; | |
config.AppendColor(LogLevel.Fatal, ConsoleColor.Red, ConsoleColor.Black); | |
config.AppendColor(LogLevel.Warning, ConsoleColor.Blue, ConsoleColor.Black); | |
}); | |
item.AddLogger( | |
config => | |
{ | |
config.Name = "root"; | |
config.LogLevel = LogLevel.Fatal; | |
config.AddAppender("file"); | |
config.AddAppender("console"); | |
}); | |
} | |
} | |
/// <summary> | |
/// This is a Config class, it holds all the data required! (note it does not set itself up) | |
/// </summary> | |
public class LoggingConfiguration : IRequireConfiguring | |
{ | |
private readonly IDictionary<string, IAppenderConfiguration> _appenders; | |
private readonly List<LoggerConfiguration> _loggers; | |
public LoggingConfiguration() | |
{ | |
_appenders = new Dictionary<string, IAppenderConfiguration>(); | |
_loggers = new List<LoggerConfiguration>(); | |
} | |
public IEnumerable<IAppenderConfiguration> Appenders { get { return _appenders.Values; } } | |
public IEnumerable<LoggerConfiguration> Loggers { get { return _loggers; } } | |
public IAppenderConfiguration GetAppenderConfiguration(string name) | |
{ | |
return _appenders[name]; | |
} | |
public void AddAppender<T>(Action<T> config) where T : IAppenderConfiguration, new() | |
{ | |
var appenderConfig = new T(); | |
config(appenderConfig); | |
_appenders.Add(appenderConfig.Name, appenderConfig); | |
} | |
public void AddAppender<T>(Func<T> config) where T : IAppenderConfiguration | |
{ | |
var appenderConfig = config(); | |
_appenders.Add(appenderConfig.Name, appenderConfig); | |
} | |
public void AddLogger(Action<LoggerConfiguration> config) | |
{ | |
var logCongfig = new LoggerConfiguration(); | |
config(logCongfig); | |
_loggers.Add(logCongfig); | |
} | |
} | |
public interface IAppenderConfiguration | |
{ | |
string Name { get; set; } | |
Type AppenderType { get; } | |
void Configure(IAppender appender); | |
} | |
public interface IAppenderConfiguration<in T> : IAppenderConfiguration where T : IAppender | |
{ | |
void Configure(T appender); | |
} | |
public abstract class AppenderConfigBase<T> : IAppenderConfiguration<T> where T : IAppender | |
{ | |
public abstract void Configure(T appender); | |
public string Name { get; set; } | |
public Type AppenderType { get { return typeof (T); } } | |
public void Configure(IAppender appender) | |
{ | |
appender.Name = Name; | |
Configure((T)appender); | |
} | |
} | |
public class LoggerConfiguration | |
{ | |
private readonly HashSet<string> _appenders = new HashSet<string>(); | |
public string Name { get; set; } | |
public LogLevel LogLevel { get; set; } | |
public IEnumerable<string> Appenders { get { return _appenders; }} | |
public void AddAppender(string name) | |
{ | |
_appenders.Add(name); | |
} | |
} | |
[Flags] | |
public enum LogLevel | |
{ | |
Trace = 1, //000001 | |
Debug = 3, //000011 | |
Infomation = 7, //000111 | |
Warning = 15, //001111 | |
Error = 31, //011111 | |
Fatal = 63 //111111 | |
} | |
public interface ILogger | |
{ | |
void Append(string message, LogLevel logLevel); | |
} | |
public class Logger : ILogger | |
{ | |
private readonly IProcessWorker _worker; | |
private readonly List<IAppender> _appenders; | |
public Logger(string name, LogLevel logLevel, IEnumerable<IAppender> appenders, IProcessWorker worker) | |
{ | |
_worker = worker; | |
Name = name; | |
LogLevel = logLevel; | |
_appenders = new List<IAppender>(appenders); | |
} | |
public LogLevel LogLevel { get; private set; } | |
public string Name { get; private set; } | |
public void Append(string message, LogLevel logLevel) | |
{ | |
if (logLevel > LogLevel) | |
{ | |
return; | |
} | |
//Action<IAppender, string, LogLevel> log = (appender, msg, level) => appender.Append(msg, level); | |
//should be async and ignore execptions | |
foreach (var appender in _appenders) | |
{ | |
IAppender appender1 = appender; | |
_worker.QueueWork(() => appender1.Append(message, logLevel)); | |
} | |
} | |
} | |
public interface IAppender | |
{ | |
void Append(string message, LogLevel logLevel); | |
string Name { get; set; } | |
} | |
public abstract class AppenderBase : IAppender | |
{ | |
public abstract void Append(string message, LogLevel logLevel); | |
public string Name { get; set; } | |
} | |
public class ConsoleAppender : AppenderBase | |
{ | |
public ConsoleAppenderConfiguration Configuration { get; set; } | |
public override void Append(string message, LogLevel logLevel) | |
{ | |
var consoleLogColor = Configuration.GetConsoleLogColor(logLevel); | |
WriteLine(message, consoleLogColor); | |
} | |
private void WriteLine(string message, ConsoleLogColor consoleLogColor) | |
{ | |
var foregroundColor = Console.ForegroundColor; | |
var backgroundColor = Console.BackgroundColor; | |
if (consoleLogColor != null) | |
{ | |
Console.ForegroundColor = consoleLogColor.Text; | |
Console.BackgroundColor = consoleLogColor.Background; | |
} | |
Console.WriteLine(message); | |
Console.ForegroundColor = foregroundColor; | |
Console.BackgroundColor = backgroundColor; | |
} | |
} | |
public class ConsoleAppenderConfiguration : AppenderConfigBase<ConsoleAppender> | |
{ | |
private readonly IDictionary<LogLevel, ConsoleLogColor> _consoleLogColors = new Dictionary<LogLevel, ConsoleLogColor>(); | |
public void AppendColor(LogLevel logLevel, ConsoleColor text, ConsoleColor background) | |
{ | |
ConsoleLogColor consoleLogColor; | |
if (_consoleLogColors.TryGetValue(logLevel, out consoleLogColor)) | |
{ | |
consoleLogColor.Background = background; | |
consoleLogColor.Text = text; | |
} | |
else | |
{ | |
consoleLogColor = new ConsoleLogColor {LogLevel = logLevel, Background = background, Text = text}; | |
_consoleLogColors.Add(consoleLogColor.LogLevel, consoleLogColor); | |
} | |
} | |
public IEnumerable<ConsoleLogColor> ConsoleLogColors {get { return _consoleLogColors.Values; }} | |
public ConsoleLogColor GetConsoleLogColor(LogLevel logLevel) | |
{ | |
ConsoleLogColor consoleLogColor = null; | |
_consoleLogColors.TryGetValue(logLevel, out consoleLogColor); | |
return consoleLogColor; | |
} | |
public override void Configure(ConsoleAppender appender) | |
{ | |
appender.Configuration = this; | |
} | |
} | |
public class ConsoleLogColor | |
{ | |
public LogLevel LogLevel { get; set; } | |
public ConsoleColor Text { get; set; } | |
public ConsoleColor Background { get; set; } | |
} | |
public class FileAppender : AppenderBase | |
{ | |
Queue<FileEntry> _toAddToLogItems = new Queue<FileEntry>(); | |
public FileAppenderConfiguration Configuration { get; set; } | |
public override void Append(string message, LogLevel logLevel) | |
{ | |
_toAddToLogItems.Enqueue(new FileEntry(){Message = message, LogLevel = logLevel}); | |
if (_toAddToLogItems.Count != Configuration.Batch) | |
{ | |
return; | |
} | |
var path = string.IsNullOrWhiteSpace(Configuration.LogFolder) | |
? Directory.GetCurrentDirectory() | |
: Configuration.LogFolder; | |
var file = Path.Combine(path, "log.txt"); | |
FileStream stream = File.Exists(file) | |
? File.Open(file, FileMode.Append, FileAccess.Write) | |
: File.Create(file); | |
using (var writer = new StreamWriter(stream)) | |
{ | |
while (_toAddToLogItems.Count > 0) | |
{ | |
var item = _toAddToLogItems.Dequeue(); | |
writer.WriteLine(item.Message); | |
} | |
} | |
} | |
} | |
public class FileEntry | |
{ | |
public string Message { get; set; } | |
public LogLevel LogLevel { get; set; } | |
} | |
public class FileAppenderConfiguration : AppenderConfigBase<FileAppender> | |
{ | |
public string LogFolder { get; set; } | |
public int Batch { get; set; } | |
public override void Configure(FileAppender appender) | |
{ | |
appender.Configuration = this; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment