Log.cs
author Thomas
Mon, 06 Apr 2020 10:11:47 +0200
changeset 3097 3d0bf28073c0
parent 2769 097906f9bcaf
child 2775 9d4516065cab
permissions -rw-r--r--
Merge with OUT-445

The Update now button itself is implemented. Other functionality regarding this feature is tracked in other tickets.
     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 sensitive text to the pEp for Outlook log only
   120         /// when sensitive data logging is enabled.
   121         /// </summary>
   122         /// <param name="text">The sensitive text to add to the log.</param>
   123         /// <param name="args">An object array that contains zero or more objects to format.</param>
   124         public static void SensitiveData(string text, params object[] args)
   125         {
   126             if ((text != null) &&
   127                 (Globals.ThisAddIn != null) &&
   128                 (Globals.ThisAddIn.Settings.IsSensitiveDataLoggingEnabled))
   129             {
   130                 try
   131                 {
   132                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |S| " + Log.Format(text, args));
   133                 }
   134                 catch { }
   135             }
   136         }
   137 
   138         /// <summary>
   139         /// Adds the given warning text to the pEp for Outlook log.
   140         /// </summary>
   141         /// <param name="text">The warning 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 Warning(string text, params object[] args)
   144         {
   145             if (text != null)
   146             {
   147                 try
   148                 {
   149                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |W| " + Log.Format(text, args));
   150                 }
   151                 catch { }
   152             }
   153         }
   154 
   155         /// <summary>
   156         /// Adds the given verbose text to the pEp for Outlook log only
   157         /// when verbose logging is enabled.
   158         /// </summary>
   159         /// <param name="text">The verbose text to add to the log.</param>
   160         /// <param name="args">An object array that contains zero or more objects to format.</param>
   161         public static void Verbose(string text, params object[] args)
   162         {
   163             if ((text != null) &&
   164                 (Globals.ThisAddIn?.Settings?.IsVerboseLoggingEnabled == true))
   165             {
   166                 try
   167                 {
   168                     Log.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " |V| " + Log.Format(text, args));
   169                 }
   170                 catch { }
   171             }
   172         }
   173 
   174         /// <summary>
   175         /// Writes the given text to the log file.
   176         /// </summary>
   177         /// <param name="text">The text to write to the log file.</param>
   178         private static void Write(string text)
   179         {
   180             try
   181             {
   182                 if (text != null)
   183                 {
   184                     lock (mutexLogFile)
   185                     {
   186                         if (Log.logWriter == null)
   187                         {
   188                             // If file exists, set line counter to file line count
   189                             if (File.Exists(Log.LogFilePath))
   190                             {
   191                                 try
   192                                 {
   193                                     var lines = File.ReadAllLines(Log.LogFilePath);
   194                                     Log.logLineCount = lines.Length;
   195                                 }
   196                                 catch { }
   197                             }
   198                             else
   199                             {
   200                                 try
   201                                 {
   202                                     Directory.CreateDirectory(Path.GetDirectoryName(Log.LogFilePath));
   203                                 }
   204                                 catch { }
   205                             }
   206 
   207                             // Open the file
   208                             Log.logWriter = File.AppendText(Log.LogFilePath);
   209                         }
   210 #if DEBUG
   211                         Debug.WriteLine(text);
   212 #endif
   213 
   214                         Log.logWriter.WriteLine(text);
   215                         Log.logLineCount++;
   216 
   217                         // If the maximum line count was reached, clean up the log file by removing the first lines.
   218                         if (Log.logLineCount >= Log.LOG_MAX_LINES)
   219                         {
   220                             Log.Archive();
   221                             Log.logLineCount = 0;
   222                         }
   223                     }
   224                 }
   225             }
   226             catch { }
   227 
   228             return;
   229         }
   230 
   231         /// <summary>
   232         /// Reads all lines of text from the log file.
   233         /// This will never return null.
   234         /// </summary>
   235         /// <returns>The lines of text in the log file.</returns>
   236         public static string Read()
   237         {
   238             string log = string.Empty;
   239             StreamReader logReader;
   240 
   241             try
   242             {
   243                 Log.Close();
   244 
   245                 lock (mutexLogFile)
   246                 {
   247                     if (File.Exists(Log.LogFilePath))
   248                     {
   249                         logReader = new StreamReader(Log.LogFilePath);
   250                         log = logReader.ReadToEnd();
   251 
   252                         logReader.Close();
   253                     }
   254                 }
   255             }
   256             catch { }
   257 
   258             // Don't return null
   259             return log ?? string.Empty;
   260         }
   261 
   262         /// <summary>
   263         /// Formats the input text and returns some information about
   264         /// potential exceptions that occur during formatting.
   265         /// </summary>
   266         /// <param name="text">A composite format string</param>
   267         /// <param name="args">An object array that contains zero or more objects to format.</param>
   268         /// <returns>A copy of format in which the format items have been replaced by the string representation 
   269         /// of the corresponding objects in args.</returns>
   270         public static string Format(string text, params object[] args)
   271         {
   272             string formattedText = null;
   273 
   274             try
   275             {
   276                 formattedText = string.Format(text, args);
   277             }
   278             catch (Exception ex)
   279             {
   280                 formattedText = "Error formatting error message. Text: " + text + ". Exception: " + ex.ToString();
   281             }
   282 
   283             return formattedText;
   284         }
   285 
   286         /// <summary>
   287         /// Closes and releases any open log file writer.
   288         /// </summary>
   289         public static void Close()
   290         {
   291             try
   292             {
   293                 lock (mutexLogFile)
   294                 {
   295                     if (Log.logWriter != null)
   296                     {
   297                         Log.logWriter.Flush();
   298                         Log.logWriter.Close();
   299                         Log.logWriter.Dispose();
   300                         Log.logWriter = null;
   301                     }
   302                 }
   303             }
   304             catch { }
   305 
   306             return;
   307         }
   308 
   309         /// <summary>
   310         /// Clears all logged text by deleting the file.
   311         /// </summary>
   312         public static void Clear()
   313         {
   314             try
   315             {
   316                 Log.Close();
   317 
   318                 lock (mutexLogFile)
   319                 {
   320                     if (File.Exists(Log.LogFilePath))
   321                     {
   322                         File.Delete(Log.LogFilePath);
   323                     }
   324                 }
   325             }
   326             catch { }
   327 
   328             return;
   329         }
   330 
   331         /// <summary>
   332         /// Rotates the log by saving a copy of the current log file to an archived file 
   333         /// and deleting older archived logs if necessary.
   334         /// </summary>
   335         public static void Archive()
   336         {
   337             int counter = 0;
   338             string[] logFiles = null;
   339             string archiveFile = null;
   340 
   341             // Create archive file with current date and timestamp
   342             try
   343             {
   344                 archiveFile = Path.Combine(Log.LogFileDirectory, LOG_FILE_ARCHIVE_PREFIX);
   345                 archiveFile += DateTime.Now.ToString(LOG_FILE_DATE_FORMAT);
   346                 archiveFile += ".txt";
   347 
   348                 while (File.Exists(archiveFile))
   349                 {
   350                     archiveFile = Path.Combine(Log.LogFileDirectory, LOG_FILE_ARCHIVE_PREFIX);
   351                     archiveFile += DateTime.Now.ToString(LOG_FILE_DATE_FORMAT);
   352                     archiveFile += ".txt";
   353 
   354                     // Just to be sure in case there is an error somewhere
   355                     if (counter++ > 100)
   356                     {
   357                         throw new Exception("The file already exists.");
   358                     }
   359                 }
   360             }
   361             catch (Exception ex)
   362             {
   363                 Log.Error("Archive: Could not archive log file. Error creating archive file. " + ex.ToString());
   364                 archiveFile = null;
   365             }
   366 
   367             // Move current log to archive log file
   368             if (archiveFile != null)
   369             {
   370                 lock (mutexLogFile)
   371                 {
   372                     Log.Close();
   373 
   374                     try
   375                     {
   376                         File.Move(Log.LogFilePath, archiveFile);
   377                     }
   378                     catch (Exception ex)
   379                     {
   380                         Log.Warning("Archive: Could not archive log file. " + ex.ToString());
   381                     }
   382 
   383                     Log.Clear();
   384                 }
   385             }
   386 
   387             // Delete older log files if necessary
   388             try
   389             {
   390                 logFiles = Directory.GetFiles(Log.LogFileDirectory);
   391                 logFiles = Array.FindAll(logFiles, a => a.ToUpperInvariant().Contains(LOG_FILE_ARCHIVE_PREFIX.ToUpperInvariant()));
   392             }
   393             catch
   394             {
   395                 logFiles = null;
   396             }
   397 
   398             if ((logFiles != null) &&
   399                 (logFiles.Length > LOG_MAX_FILES))
   400             {
   401                 Array.Sort(logFiles, StringComparer.Ordinal);
   402 
   403                 for (int i = 0; i < (logFiles.Length - LOG_MAX_FILES); i++)
   404                 {
   405                     try
   406                     {
   407                         File.Delete(logFiles[i]);
   408                     }
   409                     catch (Exception ex)
   410                     {
   411                         Log.Warning("Archive: Could not delete old log file. " + ex.ToString());
   412                     }
   413                 }
   414             }
   415         }
   416     }
   417 }