Skip to content

Instantly share code, notes, and snippets.

@he-dev
Last active August 3, 2019 16:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save he-dev/c2fcbdd0a7415e4d1a49c19eea055cee to your computer and use it in GitHub Desktop.
Save he-dev/c2fcbdd0a7415e4d1a49c19eea055cee to your computer and use it in GitHub Desktop.
void Main()
{
var logger = new Logger(new LoggerPropertySetter(("logger", "demo")).InsertNext(new LoggerEcho()));
// Include to filter certain messages out.
//logger.UseFilter(l => !l["message"].Equals("tran-2-commit"));
logger.Log(l => l.Message("begin"));
using (var tran = logger.UseTransaction())
using (logger.UseStopwatch())
{
logger.Log(l => l.Message("tran-1-commit"));
//using (logger.UseScope())
logger.Log(l => l.Message("tran-2-commit"));
tran.Commit();
}
using (var tran = logger.UseTransaction())
{
logger.Log(l => l.Message("tran-1-rollback"));
logger.Log(l => l.Message("tran-2-rollback"));
tran.Rollback();
}
using (logger.UseScope())
{
logger.Log(l => l.Message("scope-1"));
using (logger.UseScope())
{
logger.Log(l => l.Message("scope-2"));
using (logger.UseScope())
{
logger.Log(l => l.Message("scope-3"));
}
}
}
logger.Log(l => l.Message("end"));
}
public class Log : Dictionary<string, object> { }
public class Logger
{
private readonly LoggerMiddleware _middleware;
public Logger(LoggerMiddleware middleware)
{
// Start with the first middleware in case this is already a chain.
_middleware = middleware.First();
}
public T Use<T>(T next) where T : LoggerMiddleware
{
// The last middleware is Echo so put the new one before.
return _middleware.Last().Previous.InsertNext(next);
}
public void Log(Log log)
{
_middleware.Invoke(log);
}
}
public abstract class LoggerMiddleware : IDisposable
{
public LoggerMiddleware Previous { get; private set; }
public LoggerMiddleware Next { get; private set; }
// Inserts a new middlewere after this one and returns the new one.
public T InsertNext<T>(T next) where T : LoggerMiddleware
{
(next.Previous, next.Next, Next) = (this, Next, next);
return next;
}
public abstract void Invoke(Log request);
// Removes itself from the middleware chain.
public virtual void Dispose()
{
if (Previous is null)
{
return;
}
(Previous.Next, Previous, Next) = (Next, null, null);
}
}
public class LoggerPropertySetter : LoggerMiddleware
{
private readonly IEnumerable<(string Name, object Value)> _properties;
public LoggerPropertySetter(IEnumerable<(string Name, object Value)> properties)
{
_properties = properties;
}
public LoggerPropertySetter(params (string Name, object Value)[] properties)
{
_properties = properties;
}
public override void Invoke(Log request)
{
foreach (var property in _properties)
{
request[property.Name] = property.Value;
}
Next?.Invoke(request);
}
}
public class LoggerStopwatch : LoggerMiddleware
{
private readonly Stopwatch _stopwatch;
public LoggerStopwatch()
{
_stopwatch = Stopwatch.StartNew();
}
public void Reset() => _stopwatch.Reset();
public override void Invoke(Log request)
{
request["elapsed"] = _stopwatch.Elapsed;
Next?.Invoke(request);
}
}
public class LoggerLambda : LoggerMiddleware
{
private readonly Action<Log> _transform;
public LoggerLambda(Action<Log> transform)
{
_transform = transform;
}
public override void Invoke(Log request)
{
_transform(request);
Next?.Invoke(request);
}
}
public class LoggerTransaction : LoggerMiddleware
{
private readonly Queue<Log> _buffer = new Queue<Log>();
public override void Invoke(Log request)
{
_buffer.Enqueue(request);
//Next?.Invoke(request); // <-- don't call Next until Commit
}
public void Commit()
{
while (_buffer.Any())
{
Next?.Invoke(_buffer.Dequeue());
}
}
public void Rollback()
{
_buffer.Clear();
}
}
public class LoggerFilter : LoggerMiddleware
{
public Func<Log, bool> CanLog { get; set; } = _ => true;
public override void Invoke(Log request)
{
if (CanLog(request))
{
Next?.Invoke(request);
}
}
}
public class LoggerScope : LoggerMiddleware
{
private static readonly AsyncLocal<Stack<object>> _current = new AsyncLocal<Stack<object>>
{
Value = new Stack<object>()
};
public LoggerScope()
{
Current.Push(Guid.NewGuid());
}
private static Stack<object> Current
{
get => _current.Value;
set => _current.Value = value;
}
public override void Invoke(Log request)
{
request["Scope"] = Current;
Next?.Invoke(request);
}
public override void Dispose()
{
if (Current.Any())
{
Current.Pop();
}
base.Dispose();
}
}
public class LoggerEcho : LoggerMiddleware
{
public override void Invoke(Log request)
{
request.Dump();
}
}
// Helpers
public static class LoggerMiddlewareExtensions
{
public static LoggerMiddleware First(this LoggerMiddleware middleware)
{
return middleware.Enumerate(m => m.Previous).Last();
}
public static LoggerMiddleware Last(this LoggerMiddleware middleware)
{
return middleware.Enumerate(m => m.Next).Last();
}
private static IEnumerable<LoggerMiddleware> Enumerate(this LoggerMiddleware middleware, Func<LoggerMiddleware, LoggerMiddleware> direction)
{
do
{
yield return middleware;
} while (!((middleware = direction(middleware)) is null));
}
}
public static class LogExtensions
{
public static Log Message(this Log log, string message)
{
log["message"] = message;
return log;
}
}
public static class LoggerExtensions
{
public static void Log(this Logger logger, Action<Log> transform)
{
using (logger.UseLambda(transform))
{
logger.Log(new Log());
}
}
}
public static class LoggerStopwatchHelper
{
public static LoggerStopwatch UseStopwatch(this Logger logger)
{
return logger.Use(new LoggerStopwatch());
}
}
public static class LoggerLambdaHelper
{
public static LoggerLambda UseLambda(this Logger logger, Action<Log> transform)
{
return logger.Use(new LoggerLambda(transform));
}
}
public static class LoggerTransactionHelper
{
public static LoggerTransaction UseTransaction(this Logger logger)
{
return logger.Use(new LoggerTransaction());
}
}
public static class LoggerFilterHelper
{
public static LoggerFilter UseFilter(this Logger logger, Func<Log, bool> canLog)
{
return logger.Use(new LoggerFilter { CanLog = canLog });
}
}
public static class LoggerScopeHelper
{
public static LoggerScope UseScope(this Logger logger)
{
return logger.Use(new LoggerScope());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment