209 lines
7.5 KiB
C#
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");
|
|
}
|
|
|
|
//-------------------------------------------------------------------
|
|
}
|
|
//=======================================================================
|
|
}
|