diff --git a/GBase.Logging/ConsoleLogger.cs b/GBase.Logging/ConsoleLogger.cs new file mode 100644 index 0000000..f285376 --- /dev/null +++ b/GBase.Logging/ConsoleLogger.cs @@ -0,0 +1,33 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.Threading.Tasks; +using GBase.Logging.Interfaces; + +namespace GBase.Logging +{ + /// + /// An that writes log messages to the + /// + public class ConsoleLogger : ILogger + { + /// + /// Write the given to the + /// + /// The + public async Task Write(ILogMessage message) + { + await Task.Run(() => Console.Write(message.ToString())); + } + + /// + /// the + /// + public async ValueTask DisposeAsync() + { + await Task.Run(Console.Clear); + } + } +} \ No newline at end of file diff --git a/GBase.Logging/FileLogger.cs b/GBase.Logging/FileLogger.cs new file mode 100644 index 0000000..f7b1ab7 --- /dev/null +++ b/GBase.Logging/FileLogger.cs @@ -0,0 +1,82 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using GBase.Logging.Interfaces; + +namespace GBase.Logging +{ + /// + /// An that writes log messages to a set file + /// + public class FileLogger : ILogger + { + private readonly StreamWriter _fileWriter; + private readonly Timer _timer; + + private readonly SemaphoreSlim _lockObject = new SemaphoreSlim(1); + + /// + /// Constructor for + /// + /// The directory of the LogFile + /// The path of the LogFile + public FileLogger(string filePath, string fileName) + { + Directory.CreateDirectory(filePath); + _fileWriter = new StreamWriter(Path.Combine(filePath, fileName), true); + _timer = new Timer(TimerCallback, null, 0, 1000); + } + + /// + /// Write the given to the file + /// + /// The + public async Task Write(ILogMessage message) + { + await _lockObject.WaitAsync(); + try + { + await _fileWriter.WriteAsync(message.ToString()); + } + finally + { + _lockObject.Release(); + } + } + + private async void TimerCallback(object state) + { + await Flush(); + } + + private async Task Flush() + { + await _lockObject.WaitAsync(); + try + { + await _fileWriter.FlushAsync(); + } + finally + { + _lockObject.Release(); + } + } + + /// + /// the + /// + public async ValueTask DisposeAsync() + { + _timer.Dispose(); + + await Flush(); + _fileWriter.Dispose(); + + _lockObject.Dispose(); + } + } +} \ No newline at end of file diff --git a/GBase.Logging/GBase.Logging.csproj b/GBase.Logging/GBase.Logging.csproj new file mode 100644 index 0000000..d4c395e --- /dev/null +++ b/GBase.Logging/GBase.Logging.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.1 + + + diff --git a/GBase.Logging/Interfaces/ILog.cs b/GBase.Logging/Interfaces/ILog.cs new file mode 100644 index 0000000..e7d6f11 --- /dev/null +++ b/GBase.Logging/Interfaces/ILog.cs @@ -0,0 +1,38 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; + +namespace GBase.Logging.Interfaces +{ + /// + /// The main logging interface + /// + public interface ILog : IAsyncDisposable + { + /// + /// Initialize the + /// + /// True if successful, false if not + bool InitializeLog(); + + /// + /// Set the of the current instance + /// + /// The + void SetLogLevel(LogLevels logLevel); + + /// + /// Add an to the + /// + /// The + void AddLogger(ILogger logger); + + /// + /// Remove an from the + /// + /// The + void RemoveLogger(ILogger logger); + } +} \ No newline at end of file diff --git a/GBase.Logging/Interfaces/ILogComponent.cs b/GBase.Logging/Interfaces/ILogComponent.cs new file mode 100644 index 0000000..3bc9f1d --- /dev/null +++ b/GBase.Logging/Interfaces/ILogComponent.cs @@ -0,0 +1,17 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +namespace GBase.Logging.Interfaces +{ + /// + /// The + /// + public interface ILogComponent + { + /// + /// The + /// + string Component { get; } + } +} \ No newline at end of file diff --git a/GBase.Logging/Interfaces/ILogMessage.cs b/GBase.Logging/Interfaces/ILogMessage.cs new file mode 100644 index 0000000..c8b1502 --- /dev/null +++ b/GBase.Logging/Interfaces/ILogMessage.cs @@ -0,0 +1,34 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; + +namespace GBase.Logging.Interfaces +{ + /// + /// An interface that is used to handle log messages + /// + public interface ILogMessage + { + /// + /// The of the + /// + LogLevels LogLevel { get; set; } + + /// + /// The of the + /// + DateTime Timestamp { get; set; } + + /// + /// The of the + /// + ILogComponent Component { get; set; } + + /// + /// The of the + /// + string Message { get; set; } + } +} \ No newline at end of file diff --git a/GBase.Logging/Interfaces/ILogger.cs b/GBase.Logging/Interfaces/ILogger.cs new file mode 100644 index 0000000..8377ef8 --- /dev/null +++ b/GBase.Logging/Interfaces/ILogger.cs @@ -0,0 +1,21 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.Threading.Tasks; + +namespace GBase.Logging.Interfaces +{ + /// + /// Base class for loggers + /// + public interface ILogger : IAsyncDisposable + { + /// + /// Write the given + /// + /// The + Task Write(ILogMessage message); + } +} \ No newline at end of file diff --git a/GBase.Logging/Log.cs b/GBase.Logging/Log.cs new file mode 100644 index 0000000..fe4ce4f --- /dev/null +++ b/GBase.Logging/Log.cs @@ -0,0 +1,201 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using GBase.Logging.Interfaces; + +namespace GBase.Logging +{ + /// + /// The main logging class + /// + public class Log : ILog + { + /// + /// Constructor for + /// + public Log() + { + Loggers = new List(); + } + + /// + /// of s that are subscribed to this + /// + protected static List Loggers { get; private set; } + + /// + /// The of this logging instance + /// + protected static LogLevels LogLevel { get; private set; } //TODO: Move LogLevel to ILogger? Allows to set logLevel separately for each ILogger + + /// + /// Initialize the + /// + /// True if successful, false if not + public bool InitializeLog() + { + return true; + } + + /// + /// Set the of the current instance + /// + /// The + public void SetLogLevel(LogLevels logLevel) + { + LogLevel = logLevel; + LogLevelChanged?.Invoke(this, LogLevel); + } + + /// + /// Add an to the + /// + /// The + public void AddLogger(ILogger logger) + { + Loggers.Add(logger); + } + + /// + /// Remove an from the + /// + /// The + public void RemoveLogger(ILogger logger) + { + Loggers.Remove(logger); + } + + /// + /// Write a given to the set s + /// + /// The + /// The of the caller + public static async Task Write(Exception ex) + { + ILogComponent component = GetDefaultComponentFromType(); + + await Write(component, LogLevels.Error, ex.Message); + await Write(component, LogLevels.Error, ""); + await Write(component, LogLevels.Error, ex.StackTrace); + } + + /// + /// Write a given to the set s + /// + /// The + /// The + public static async Task Write(ILogComponent component, Exception ex) + { + await Write(component, LogLevels.Error, ex.Message); + await Write(component, LogLevels.Error, ""); + await Write(component, LogLevels.Error, ex.StackTrace); + } + + /// + /// Write a given to the set s + /// + /// The + /// The of the caller + public static async Task Write(AggregateException ex) + { + ILogComponent component = GetDefaultComponentFromType(); + + await Write(component, LogLevels.Error, ex.Message); + await Write(component, LogLevels.Error, ""); + await Write(component, LogLevels.Error, ex.StackTrace); + await Write(component, LogLevels.Error, ""); + + foreach (var innerException in ex.InnerExceptions) + { + await Write(component, innerException); + await Write(component, LogLevels.Error, ""); + } + } + + /// + /// Write a given with the to the set s + /// + /// + /// The of the caller + public static async Task Write(string line) + { + ILogComponent component = GetDefaultComponentFromType(); + await Write(component, LogLevels.Default, line); + } + + /// + /// Write a given with the of the calling and the to the set s + /// + /// The + /// The given + /// The of the caller + public static async Task Write(LogLevels logLevel, string line) + { + ILogComponent component = GetDefaultComponentFromType(); + await Write(component, logLevel, line); + } + + /// + /// Write a given with the and the to the set s + /// + /// The + /// The + /// The given + public static async Task Write(ILogComponent component, LogLevels logLevel, string line) + { + if (logLevel > LogLevel) //logLevel of the message can't be higher than the set LogLevel + return; + + ILogMessage message = new LogMessage() + { + LogLevel = logLevel, + Timestamp = DateTime.Now, + Component = component, + Message = line + }; + + foreach (var logger in Loggers) + { + await logger.Write(message); + } + } + + /// + /// Get the default for the given + /// + /// The + /// + protected static ILogComponent GetDefaultComponentFromType() + { + Assembly assembly = Assembly.GetAssembly(typeof(T)); + LogComponentAttribute attribute = assembly.GetCustomAttribute(); + return attribute ?? new LogComponentAttribute("UNKNOWN"); + } + + /// + /// the + /// + public async ValueTask DisposeAsync() + { + foreach (var logger in Loggers) + { + await logger.DisposeAsync(); + } + + Loggers.Clear(); + LogLevel = LogLevels.None; + } + + + /// + /// Event that is fired when is called + /// is the newly set + /// + public static event EventHandler LogLevelChanged; + } +} \ No newline at end of file diff --git a/GBase.Logging/LogComponentAttribute.cs b/GBase.Logging/LogComponentAttribute.cs new file mode 100644 index 0000000..9941fec --- /dev/null +++ b/GBase.Logging/LogComponentAttribute.cs @@ -0,0 +1,39 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using GBase.Logging.Interfaces; + +namespace GBase.Logging +{ + /// + /// The attribute + /// + [AttributeUsage(AttributeTargets.Assembly)] + public class LogComponentAttribute : Attribute, ILogComponent + { + /// + /// constructor + /// + /// The + public LogComponentAttribute(string component) + { + Component = component; + } + + /// + /// The + /// + public string Component { get; private set; } + + /// + /// Returns the of this + /// + /// The + public override string ToString() + { + return Component; + } + } +} \ No newline at end of file diff --git a/GBase.Logging/LogLevels.cs b/GBase.Logging/LogLevels.cs new file mode 100644 index 0000000..9fa567d --- /dev/null +++ b/GBase.Logging/LogLevels.cs @@ -0,0 +1,37 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +namespace GBase.Logging +{ + /// + /// The available + /// + public enum LogLevels + { + /// + /// No logging + /// + None, + + /// + /// Error logging + /// + Error, + + /// + /// Default logging + /// + Default, + + /// + /// Advanced logging + /// + Advanced, + + /// + /// Debug logging + /// + Debug + } +} \ No newline at end of file diff --git a/GBase.Logging/LogMessage.cs b/GBase.Logging/LogMessage.cs new file mode 100644 index 0000000..d0592cd --- /dev/null +++ b/GBase.Logging/LogMessage.cs @@ -0,0 +1,44 @@ +// Author: Simon Gockner +// Created: 2020-02-08 +// Copyright(c) 2020 SimonG. All Rights Reserved. + +using System; +using GBase.Logging.Interfaces; + +namespace GBase.Logging +{ + /// + /// Implementation of + /// + public class LogMessage : ILogMessage + { + /// + /// The of the + /// + public LogLevels LogLevel { get; set; } + + /// + /// The of the + /// + public DateTime Timestamp { get; set; } + + /// + /// The of the + /// + public ILogComponent Component { get; set; } + + /// + /// The of the + /// + public string Message { get; set; } + + /// + /// Builds the out of all parts and returns it as a + /// + /// The as a + public override string ToString() + { + return $"{Timestamp:u}: [{Component}] {Message}\n"; + } + } +} \ No newline at end of file