Files
Common-VS2019/SacsStandardBase/Base/App/SaLogger.cs
T
2021-05-05 15:36:28 +01:00

209 lines
7.5 KiB
C#

using System;
using System.IO;
using Sarand.Net.Standard.Base.App;
using Sarand.Net.Standard.Base.Core;
using Sarand.Net.Standard.Base.IO;
using Sarand.Net.Standard.Base.Utils;
namespace Sarand.Net.Standard.Base.App
{
//=======================================================================
public static class SaLogger
{
//-------------------------------------------------------------------
// Data members.
private static String logDirectoryPath = String.Empty;
private static String logFilenamePrefix = String.Empty;
//-------------------------------------------------------------------
// Construction.
//-------------------------------------------------------------------
// Properties.
public static SaMessageLevel ConsoleLevel { get; set; } = SaMessageLevel.Debug;
public static SaMessageLevel LogFileLevel { get; set; } = SaMessageLevel.Info;
public static String LogFolder
{
get { return logDirectoryPath; }
set
{
logDirectoryPath = "";
if (FileUtil.ValidateDirectory(value, true))
logDirectoryPath = value;
}
}
public static bool LoggingToFile
{
get { return !StringUtil.IsEmpty(logDirectoryPath); }
}
public static String LogFilenamePrefix
{
get { return StringUtil.EmptyOnNull(logFilenamePrefix); }
set { logFilenamePrefix = value; }
}
public static bool IncludeHostInFileMessage { get; set; } = true;
public static bool IncludeUserInFileMessage { get; set; } = true;
//-------------------------------------------------------------------
// Logging.
// TODO - Enhanced logging:
// Include: System.Runtime.CompilerServices
// public static void Error (String message,
// [System.Runtime.CompilerServices.CallerFilePath] string file = "",
// [System.Runtime.CompilerServices.CallerMemberName] string member = "",
// [System.Runtime.CompilerServices.CallerLineNumber] int line = 0)
// {
// Debug.WriteLine("{0}_{1}({2}): {3}", Path.GetFileName(file), member, line, message);
// }
public static bool Error (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Error, logToFile);
return false;
}
public static bool Warn (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Warning, logToFile);
return false;
}
public static void Info (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Info, logToFile);
}
public static void Debug (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Debug, logToFile);
}
public static void Trace (String message, bool logToFile = false)
{
Log(message, SaMessageLevel.Trace, logToFile);
}
//-------------------------------------------------------------------
/// <summary>
/// Log message.
/// <para></para>
/// <para>Error and warning messages are forced to file unless logToFile is explicitly false</para>
/// </summary>
public static void Log (String message, SaMessageLevel level = SaMessageLevel.Info, bool? logToFile = null)
{
if (level == SaMessageLevel.None) return;
message = message.Trim();
WriteToConsole(FormatMessage(message, level), level);
if (LogToFile(level, logToFile))
WriteToLogFile(FormatMessage(message, level, true), level);
}
private static bool LogToFile (SaMessageLevel level, bool? logToFile)
{
switch (level)
{
case SaMessageLevel.Fatal:
case SaMessageLevel.Exception:
case SaMessageLevel.Error:
case SaMessageLevel.Warning:
return true;
}
return (logToFile != null && logToFile == true);
}
private static String FormatMessage (String message, SaMessageLevel level, bool forFile = false)
{
DateTime now = DateTime.Now;
if (forFile)
return
now.ToString("yyyyMMdd:HHmmss ") + GetLevelAbbrFile(level) + " " +
(IncludeHostInFileMessage ? SacsSystem.ComputerName.PadRight(12) + " " : "") +
(IncludeUserInFileMessage ? SacsSystem.UserName.PadRight(16) + " " : "") +
message;
return now.ToString("yyyyMMdd:HHmmss ") + GetLevelAbbrFile(level) + " " + message;
}
//-------------------------------------------------------------------
/// <summary>Message level abbreviation.</summary>
public static String GetLevelAbbr (SaMessageLevel level)
{
String abbr = SacsSystem.GetMessageLevelCode(level);
return (!StringUtil.IsEmpty(abbr) ? abbr : "UNK");
}
private static String GetLevelAbbrFile (SaMessageLevel level)
{
return GetLevelAbbr(level).PadRight(3, ' ').Substring(0, 3).ToUpper();
}
private static String GetLevelAbbrConsole (SaMessageLevel level)
{
// Visual Studio Output window colour coded snippets.
if (level == SaMessageLevel.Fatal) return "Error [Fatal]";
if (level == SaMessageLevel.Exception) return "Error [Exception]";
if (level == SaMessageLevel.Error) return "Error";
if (level == SaMessageLevel.Warning) return "Warning";
return GetLevelAbbr(level).PadRight(3, ' ').Substring(0, 3).ToUpper();
}
//-------------------------------------------------------------------
public static void WriteBlankLine (bool logToFile = false)
{
WriteToConsole("", SaMessageLevel.Error);
if (logToFile)
WriteToLogFile("", SaMessageLevel.Error);
}
public static void WriteBlankLines(int lineCount, bool logToFile = false)
{
for (int i = 0; i < Math.Min(10, lineCount); i++)
{
WriteToConsole("", SaMessageLevel.Error);
if (logToFile)
WriteToLogFile("", SaMessageLevel.Error);
}
}
//-------------------------------------------------------------------
private static void WriteToConsole (String message, SaMessageLevel level = SaMessageLevel.Info)
{
#if DEBUG
// NB There should be a way to colour error/warning messages.
// Ref. https://stackoverflow.com/questions/2426609/how-do-i-write-color-text-to-the-visual-studio-output-window-from-c
// Console output.
if (level.CompareTo(ConsoleLevel) <= 0)
{
// Use Debug.Write rather than Console.Write.
System.Diagnostics.Debug.WriteLine(message);
//System.Console.WriteLine(message));
}
#endif
}
// Ref. https://stackoverflow.com/questions/6195084/thread-safe-logging-class-implementation
private static readonly object _syncObject = new object();
private static void WriteToLogFile (String message, SaMessageLevel level = SaMessageLevel.Info)
{
// File output - skip if not logging to file or message level is higher than that for logging to file.
if (!LoggingToFile || level.CompareTo(LogFileLevel) > 0) return;
// Only one thread can own the lock, so other threads entering
// this method will wait here until lock is available.
lock (_syncObject)
{
String logFolder = GetLogFolder();
if (FileUtil.ValidateDirectory(logFolder, true))
{
String logPath = logFolder + Path.DirectorySeparatorChar + GetLogFilename();
if (!StringUtil.IsEmpty(message) || File.Exists(logPath))
{
using (StreamWriter file = File.AppendText(logPath))
file.WriteLine(message);
}
}
}
}
//-------------------------------------------------------------------
private static String GetLogFolder ()
{
return logDirectoryPath + Path.DirectorySeparatorChar + DateTime.Today.Format("yyyy") + Path.DirectorySeparatorChar + DateTime.Today.Format("yyyyMM");
}
private static String GetLogFilename ()
{
return FileUtil.FormatFilename(LogFilenamePrefix + (!StringUtil.IsEmpty(LogFilenamePrefix) ? "-" : "") + DateTime.Today.Format("yyyyMMdd") + ".log");
}
//-------------------------------------------------------------------
}
//=======================================================================
}