Log.cs
author Thomas
Mon, 16 Sep 2019 15:26:05 +0200
branchsync
changeset 2768 d3b23af0c349
parent 2694 227c4be4d54d
child 2769 097906f9bcaf
permissions -rw-r--r--
OUT-573: Add possibility to define logs location via Registry
     1 ´╗┐using Microsoft.Win32;
     2 using System;
     3 using System.Diagnostics;
     4 using System.IO;
     5 
     6 namespace pEp
     7 {
     8     /// <summary>
     9     /// Class to handle all the logging of pEp for Outlook.
    10     /// </summary>
    11     internal class Log
    12     {
    13         private const string LOG_FILE_NAME                  = "log.txt";
    14         private const string LOG_FILE_DATE_FORMAT           = "yyyyMMddHHmmss";
    15         private const string LOG_FILE_ARCHIVE_PREFIX        = "log_";
    16         private const int    LOG_MAX_LINES                  = 20000;      // The maximum lines a log file can reach before the log gets rotated
    17         private const int    LOG_MAX_FILES                  = 10;      // The maximum count of log files before deletion of the oldest
    18 
    19         private static int              logLineCount        = 0;
    20         private static StreamWriter     logWriter           = null;
    21         private static readonly object  mutexLogFile        = new object();
    22         private static string           _LogFilePath        = null;
    23         private static string           _LogFileDirectory   = null;
    24 
    25         /**************************************************************
    26          * 
    27          * Methods
    28          * 
    29          *************************************************************/
    30 
    31         /// <summary>
    32         /// Gets the directory in which the log files are stored.
    33         /// </summary>
    34         public static string LogFileDirectory
    35         {
    36             get
    37             {
    38                 if (string.IsNullOrEmpty(Log._LogFileDirectory))
    39                 {
    40                     try
    41                     {
    42                         using (RegistryKey pEpKey = Registry.CurrentUser.OpenSubKey(Path.Combine(PEPSettings.REG_KEY_SOFTWARE, PEPSettings.REG_KEY_PEP, PEPSettings.REG_KEY_PEP_FOR_OUTLOOK)))
    43                         {
    44                             if (pEpKey?.GetValue(PEPSettings.REG_NAME_CUSTOM_LOCATION_LOGS) is string regValue)
    45                             {
    46                                 Log._LogFileDirectory = Environment.ExpandEnvironmentVariables(regValue);
    47                             }
    48                         }
    49 
    50                         if (string.IsNullOrEmpty(Log._LogFileDirectory))
    51                         {
    52                             Log._LogFileDirectory = Globals.PEPUserFolder;
    53                         }
    54                     }
    55                     catch (Exception ex)
    56                     {
    57                         Debug.Fail("LogFileDirectory: Error getting log file directory. " + ex.ToString());
    58                     }
    59                 }
    60 
    61                 return Log._LogFileDirectory;
    62             }
    63         }
    64 
    65         /// <summary>
    66         /// Gets the full path of the log file name.
    67         /// </summary>
    68         public static string LogFilePath
    69         {
    70             get
    71             {
    72                 if (string.IsNullOrEmpty(Log._LogFilePath))
    73                 {
    74                     Log._LogFilePath = Path.Combine(Log.LogFileDirectory, Log.LOG_FILE_NAME);
    75                 }
    76 
    77                 return Log._LogFilePath;
    78             }
    79         }
    80 
    81         /// <summary>
    82         /// Adds the given info text to the pEp for Outlook log.
    83         /// </summary>
    84         /// <param name="text">The text to add to the log.</param>
    85         /// <param name="args">An object array that contains zero or more objects to format.</param>
    86         public static void Info(string text, params object[] args)
    87         {
    88             if (text != null)
    89             {
    90                 try
    91                 {
    92                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " | " + Log.Format(text, args));
    93                 }
    94                 catch { }
    95             }
    96         }
    97 
    98         /// <summary>
    99         /// Adds the given error text to the pEp for Outlook log.
   100         /// </summary>
   101         /// <param name="text">The error text to add to the log.</param>
   102         /// <param name="args">An object array that contains zero or more objects to format.</param>
   103         public static void Error(string text, params object[] args)
   104         {
   105             if (text != null)
   106             {
   107                 try
   108                 {
   109                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |E| " + Log.Format(text, args));
   110                 }
   111                 catch { }
   112             }
   113         }
   114 
   115         /// <summary>
   116         /// Adds the given sensitive text to the pEp for Outlook log only
   117         /// when sensitive data logging is enabled.
   118         /// </summary>
   119         /// <param name="text">The sensitive text to add to the log.</param>
   120         /// <param name="args">An object array that contains zero or more objects to format.</param>
   121         public static void SensitiveData(string text, params object[] args)
   122         {
   123             if ((text != null) &&
   124                 (Globals.ThisAddIn != null) &&
   125                 (Globals.ThisAddIn.Settings.IsSensitiveDataLoggingEnabled))
   126             {
   127                 try
   128                 {
   129                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |S| " + Log.Format(text, args));
   130                 }
   131                 catch { }
   132             }
   133         }
   134 
   135         /// <summary>
   136         /// Adds the given warning text to the pEp for Outlook log.
   137         /// </summary>
   138         /// <param name="text">The warning text to add to the log.</param>
   139         /// <param name="args">An object array that contains zero or more objects to format.</param>
   140         public static void Warning(string text, params object[] args)
   141         {
   142             if (text != null)
   143             {
   144                 try
   145                 {
   146                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |W| " + Log.Format(text, args));
   147                 }
   148                 catch { }
   149             }
   150         }
   151 
   152         /// <summary>
   153         /// Adds the given verbose text to the pEp for Outlook log only
   154         /// when verbose logging is enabled.
   155         /// </summary>
   156         /// <param name="text">The verbose text to add to the log.</param>
   157         /// <param name="args">An object array that contains zero or more objects to format.</param>
   158         public static void Verbose(string text, params object[] args)
   159         {
   160             if ((text != null) &&
   161                 (Globals.ThisAddIn?.Settings?.IsVerboseLoggingEnabled == true))
   162             {
   163                 try
   164                 {
   165                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |V| " + Log.Format(text, args));
   166                 }
   167                 catch { }
   168             }
   169         }
   170 
   171         /// <summary>
   172         /// Writes the given text to the log file.
   173         /// </summary>
   174         /// <param name="text">The text to write to the log file.</param>
   175         private static void Write(string text)
   176         {
   177             try
   178             {
   179                 if (text != null)
   180                 {
   181                     lock (mutexLogFile)
   182                     {
   183                         if (Log.logWriter == null)
   184                         {
   185                             // If file exists, set line counter to file line count
   186                             if (File.Exists(Log.LogFilePath))
   187                             {
   188                                 try
   189                                 {
   190                                     var lines = File.ReadAllLines(Log.LogFilePath);
   191                                     Log.logLineCount = lines.Length;
   192                                 }
   193                                 catch { }
   194                             }
   195                             else
   196                             {
   197                                 try
   198                                 {
   199                                     Directory.CreateDirectory(Path.GetDirectoryName(Log.LogFilePath));
   200                                 }
   201                                 catch { }
   202                             }
   203 
   204                             // Open the file
   205                             Log.logWriter = File.AppendText(Log.LogFilePath);
   206                         }
   207 #if DEBUG
   208                         Debug.WriteLine(text);
   209 #endif
   210 
   211                         Log.logWriter.WriteLine(text);
   212                         Log.logLineCount++;
   213 
   214                         // If the maximum line count was reached, clean up the log file by removing the first lines.
   215                         if (Log.logLineCount >= Log.LOG_MAX_LINES)
   216                         {
   217                             Log.Archive();
   218                             Log.logLineCount = 0;
   219                         }
   220                     }
   221                 }
   222             }
   223             catch { }
   224 
   225             return;
   226         }
   227 
   228         /// <summary>
   229         /// Reads all lines of text from the log file.
   230         /// This will never return null.
   231         /// </summary>
   232         /// <returns>The lines of text in the log file.</returns>
   233         public static string Read()
   234         {
   235             string log = string.Empty;
   236             StreamReader logReader;
   237 
   238             try
   239             {
   240                 Log.Close();
   241 
   242                 lock (mutexLogFile)
   243                 {
   244                     if (File.Exists(Log.LogFilePath))
   245                     {
   246                         logReader = new StreamReader(Log.LogFilePath);
   247                         log = logReader.ReadToEnd();
   248 
   249                         logReader.Close();
   250                     }
   251                 }
   252             }
   253             catch { }
   254 
   255             // Don't return null
   256             return log ?? string.Empty;
   257         }
   258 
   259         /// <summary>
   260         /// Formats the input text and returns some information about
   261         /// potential exceptions that occur during formatting.
   262         /// </summary>
   263         /// <param name="text">A composite format string</param>
   264         /// <param name="args">An object array that contains zero or more objects to format.</param>
   265         /// <returns>A copy of format in which the format items have been replaced by the string representation 
   266         /// of the corresponding objects in args.</returns>
   267         public static string Format(string text, params object[] args)
   268         {
   269             string formattedText = null;
   270 
   271             try
   272             {
   273                 formattedText = string.Format(text, args);
   274             }
   275             catch (Exception ex)
   276             {
   277                 formattedText = "Error formatting error message. Text: " + text + ". Exception: " + ex.ToString();
   278             }
   279 
   280             return formattedText;
   281         }
   282 
   283         /// <summary>
   284         /// Closes and releases any open log file writer.
   285         /// </summary>
   286         public static void Close()
   287         {
   288             try
   289             {
   290                 lock (mutexLogFile)
   291                 {
   292                     if (Log.logWriter != null)
   293                     {
   294                         Log.logWriter.Flush();
   295                         Log.logWriter.Close();
   296                         Log.logWriter.Dispose();
   297                         Log.logWriter = null;
   298                     }
   299                 }
   300             }
   301             catch { }
   302 
   303             return;
   304         }
   305 
   306         /// <summary>
   307         /// Clears all logged text by deleting the file.
   308         /// </summary>
   309         public static void Clear()
   310         {
   311             try
   312             {
   313                 Log.Close();
   314 
   315                 lock (mutexLogFile)
   316                 {
   317                     if (File.Exists(Log.LogFilePath))
   318                     {
   319                         File.Delete(Log.LogFilePath);
   320                     }
   321                 }
   322             }
   323             catch { }
   324 
   325             return;
   326         }
   327 
   328         /// <summary>
   329         /// Rotates the log by saving a copy of the current log file to an archived file 
   330         /// and deleting older archived logs if necessary.
   331         /// </summary>
   332         public static void Archive()
   333         {
   334             int counter = 0;
   335             string[] logFiles = null;
   336             string archiveFile = null;
   337 
   338             // Create archive file with current date and timestamp
   339             try
   340             {
   341                 archiveFile = Path.Combine(Log.LogFileDirectory, LOG_FILE_ARCHIVE_PREFIX);
   342                 archiveFile += DateTime.Now.ToString(LOG_FILE_DATE_FORMAT);
   343                 archiveFile += ".txt";
   344 
   345                 while (File.Exists(archiveFile))
   346                 {
   347                     archiveFile = Path.Combine(Log.LogFileDirectory, LOG_FILE_ARCHIVE_PREFIX);
   348                     archiveFile += DateTime.Now.ToString(LOG_FILE_DATE_FORMAT);
   349                     archiveFile += ".txt";
   350 
   351                     // Just to be sure in case there is an error somewhere
   352                     if (counter++ > 100)
   353                     {
   354                         throw new Exception("The file already exists.");
   355                     }
   356                 }
   357             }
   358             catch (Exception ex)
   359             {
   360                 Log.Error("Archive: Could not archive log file. Error creating archive file. " + ex.ToString());
   361                 archiveFile = null;
   362             }
   363 
   364             // Move current log to archive log file
   365             if (archiveFile != null)
   366             {
   367                 lock (mutexLogFile)
   368                 {
   369                     Log.Close();
   370 
   371                     try
   372                     {
   373                         File.Move(Log.LogFilePath, archiveFile);
   374                     }
   375                     catch (Exception ex)
   376                     {
   377                         Log.Warning("Archive: Could not archive log file. " + ex.ToString());
   378                     }
   379 
   380                     Log.Clear();
   381                 }
   382             }
   383 
   384             // Delete older log files if necessary
   385             try
   386             {
   387                 logFiles = Directory.GetFiles(Log.LogFileDirectory);
   388                 logFiles = Array.FindAll(logFiles, a => a.ToUpperInvariant().Contains(LOG_FILE_ARCHIVE_PREFIX.ToUpperInvariant()));
   389             }
   390             catch
   391             {
   392                 logFiles = null;
   393             }
   394 
   395             if ((logFiles != null) &&
   396                 (logFiles.Length > LOG_MAX_FILES))
   397             {
   398                 Array.Sort(logFiles, StringComparer.Ordinal);
   399 
   400                 for (int i = 0; i < (logFiles.Length - LOG_MAX_FILES); i++)
   401                 {
   402                     try
   403                     {
   404                         File.Delete(logFiles[i]);
   405                     }
   406                     catch (Exception ex)
   407                     {
   408                         Log.Warning("Archive: Could not delete old log file. " + ex.ToString());
   409                     }
   410                 }
   411             }
   412         }
   413     }
   414 }