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