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