[]
        
(Showing Draft Content)

Logging

GcExcel supports logging which allows you to capture logs and resolve issues by finding out their root cause. You can store different log levels like debug, error, warning or information based on the configuration in JSON file.

Along with other external libraries, GcExcel uses Microsoft.Extensions.Logging library to implement the logging system. The logging information is supported for Json and Excel I/O and PDF exporting. Logging is disabled by default. To enable it, Workbook.LoggerFactory property needs to be set while initializing the application or website which is explained in detail in the next section.

Enable Logging

Follow the below steps to enable logging in GcExcel. The logs will be printed to console and saved to a file.

  1. Create a new console app and install the following nuget packages:

    • Microsoft.Extensions.Logging.Console

    • Microsoft.Extensions.Configuration.Json

    • Serilog.Extensions.Logging.File

    • If your project targets .NET Framework, Microsoft.Extensions.Logging.Abstractions also needs be installed.

  2. Write a method for creating logger factory as shown below:

    private static ILoggerFactory CreateLoggerFactory()
    {
        var builder = new ConfigurationBuilder().
            SetBasePath(Directory.GetCurrentDirectory()).
            AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
        var cfgRoot = builder.Build();
        var loggingCfg = cfgRoot.GetSection("Logging");
        var factory = LoggerFactory.Create(
            logBuilder => logBuilder.AddConfiguration(loggingCfg).AddConsole());
        factory.AddFile(loggingCfg);
        return factory;
    }
  3. Attach the logger factory to workbook as shown below:

    static void Main(string[] args)
    {
        using (var loggerFactory = CreateLoggerFactory())
        {
            Workbook.LoggerFactory = loggerFactory;
            var wb = new Workbook();
            try
            {
                wb.Save("test.pdf");
            }
            catch (Exception)
            { }
        }
    }
  4. Create a new json file in your project and name it as 'appsettings.json'. Set "Copy to output directory" to "Copy if newer" in properties window. If the file exists already, merge its content with existing file and appsettings.development.json. You can also refer Configure Serilog File Logs for more information about it.


  5. Add the following setting to appsettings.json file.

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information"
        },
        "PathFormat": "log-{Date}.txt"
      }
    }
  6. Run the project to observe that logs are printed to console and saved to log file 'log-yyyyMMdd.txt'.

Set Log Level

GcExcel allows you to capture four log levels, namely, debug, info, warn and error. Their priority order is:

debug < info < warn < error

You can configure which log level needs to be printed to the log file. After setting the log level, the logs with higher or equal priority are included in the printed logs while the logs with lower priority are ignored. For example, If you set the log level to "info", the info, warn and error logs are printed, while the "debug" logs are ignored.

The following setting in appsettings.json sets the default log level to "warning" and GcExcel log level to "Information".

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "GrapeCity.Documents.Excel": "Information"
    }
  }
}

Similarly, the following setting in appsettings.json sets the GcExcel log level of console output to "Information".

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    },
    "Console": {
      "LogLevel": {
        "GrapeCity.Documents.Excel": "Information"
      }
    }
  }
}

For more information, you can also refer to Logging in .NET Core and ASP.NET Core.

Save Log Records with Thread ID

You can also choose to print thread ID along with the logs in log files. This is particularly helpful to analyse the specific process or thread creating an issue. The appsettings file uses Microsoft.Extensions.Logging format to generate the logs.

Note: This example does not use serilog-settings-configuration because it usually reports type load errors.

Follow the below steps to save logs with thread ID:

  1. Install the following nuget packages:

    • Microsoft.Extensions.Configuration.Json

    • Microsoft.Extensions.Logging

    • Microsoft.Extensions.Logging.Configuration

    • Serilog.Enrichers.Thread

    • Serilog.Extensions.Logging

    • Serilog.Sinks.RollingFil

    • If your project targets .NET Framework, Microsoft.Extensions.Logging.Abstractions also needs to be installed

  2. Write methods for creating a logger factory as shown below:

    private static ILoggerFactory CreateLoggerFactory()
    {
        var appSettings = new ConfigurationBuilder().
        SetBasePath(Directory.GetCurrentDirectory()).
        AddJsonFile("appsettings.json", optional: true, reloadOnChange: true).
        Build();
        var loggingSection = appSettings.GetSection("Logging");
        var serilogConfig = new LoggerConfiguration();
        ConfigureSerilog(loggingSection, serilogConfig);
        var factory = new LoggerFactory();
        factory.AddSerilog(serilogConfig.CreateLogger());
        return factory;
    }
    
    private static void ConfigureSerilog(IConfigurationSection loggingSection,
                                         LoggerConfiguration loggerCfg)
    {
        loggerCfg.Enrich.WithThreadId();
        string pathFormat = loggingSection["PathFormat"];
        string outputTemplate = loggingSection["OutputTemplate"];
        loggerCfg.WriteTo.RollingFile(pathFormat, outputTemplate: outputTemplate);
        ConfigureMinLevel(loggerCfg.MinimumLevel, loggingSection);
    }
    
    private static void ConfigureMinLevel(LoggerMinimumLevelConfiguration minimumLevel,
                                          IConfigurationSection loggingCfgSection)
    {
        var logLevelSection = loggingCfgSection.GetSection("LogLevel");
        int pathLength = logLevelSection.Path.Length;
        foreach (var logItem in logLevelSection.AsEnumerable())
        {
            if (logItem.Key.Length <= pathLength)
            {
                continue;
            }
            string name = logItem.Key.Substring(pathLength + 1);
            if (Enum.TryParse(logItem.Value, ignoreCase: true, out LogLevel level))
            {
                var serilogLevel = (LogEventLevel)level;
                if (name == "Default")
                {
                    minimumLevel.Is(serilogLevel);
                }
                else
                {
                    minimumLevel.Override(name, serilogLevel);
                }
            }
        }
    }
  3. Attach the logger factory to workbook as shown below:

    static void Main(string[] args)
    {
        static void Main()
        {
            using (ILoggerFactory loggerFactory = CreateLoggerFactory())
            {
                Workbook.LoggerFactory = loggerFactory;
                Workbook wb = new Workbook();
                try
                {
                    wb.Save("test.pdf")
                }
                catch(Exception)
                { }
            }
        }
    }
  4. Create a new json file in your project and name it as 'appsettings.json'. Set "Copy to output directory" to "Copy if newer" in properties window. If the file exists already, merge its content with existing file and appsettings.development.json.

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning",
          "GrapeCity.Documents.Excel": "Debug"
        },
        "PathFormat": "log-{Date}.txt",
        "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} (Thread {ThreadId}) [{Level}] {Message}{NewLine}{Exception}"
      }
    }

    Here,"LogLevel" section has the same schema as Microsoft.Extensions.Logging."PathFormat" section specifies the name of log file."OutputTemplate" section specifies the format of each log record.

  5. Run the project. Logs will be saved to log-yyyyMMdd.txt and the sample output will look like below:

    2020-09-10 11:44:30.566 +08:00 (Thread 1) [Debug] Save pdf of the workbook.
     2020-09-10 11:44:30.615 +08:00 (Thread 1) [Debug] Paginate Start(Workbook)
     2020-09-10 11:44:31.104 +08:00 (Thread 1) [Debug] GetDigitWidthOfDefaultStyle GraphicsType: Pdf
     2020-09-10 11:44:31.108 +08:00 (Thread 1) [Debug] GetDigitWidthOfDefaultStyle DefaultFont: "FontName = Calibri
     FontSize = 11
     Bold = False
     Italic = False
     ScreenWidth = 0
     PDFWidth = 5.5751953125
     "
     2020-09-10 11:44:31.161 +08:00 (Thread 1) [Debug] Count of print ranges: 0
     2020-09-10 11:44:31.161 +08:00 (Thread 1) [Debug] Paginate End(Workbook)
     2020-09-10 11:44:31.164 +08:00 (Thread 1) [Error] There is no content to print.