JLogger6——当你愿意写最好的

目录

介绍

日志记录变得简单

设置记录器

代码中的日志记录示例

ILogger选项

让我们看一下日志

演示应用概览

即将推出的功能

结论

背景

使用演示代码

兴趣点


介绍

JLogger6是一个作为.NET 6库组件的单一实例组件,可与支持.NET 6的任何.NET项目一起使用。

JLogger具有以下特点:

  • 多线程使用——作为单例,它可以从任何线程访问,并使用锁定技术来确保没有冲突。
  • 高吞吐量——如果日志由多个线程同时使用,则日志写入不会停止调用线程。JLogger使用先进先出(FIFO)队列,其中日志写入放在队列中,并在后台同时写入到单独线程中的文件。该WriteDebugLog命令获取参数,创建日志数据,将其放入队列中。这些步骤都没有阻塞。
  • 发送电子邮件——调试日志写入可以选择发送电子邮件(需要SMTP配置数据)
  • 多种日志条目类型——有多种日志条目类型可供选择。它们中的每一个的含义取决于编写代码的用户。某些日志类型是为组件保留的,在处理日志条目时将被忽略。这些详述如下。
  • 每天新建日志文件——午夜后,将创建一个新的日志文件,以便命名日志文件以显示日志处于活动状态的日期和时间跨度。
  • 日志保留——日志将在指定的天数后自动删除,除非指定零,在这种情况下不会删除任何日志文件。
  • 制表符分隔的日志文件——日志作为制表符分隔的文件写入。这样就可以在Excel等程序中打开文件进行分析。
  • (可选)将日志写入数据库——当用户希望将日志写入数据库时,此选项可用于SQL Server。提供了用于创建DBLog表和用于插入日志记录和对记录执行日志保留的存储过程的脚本。
  • (可选)将日志文件存储在Azure文件存储中——通过指定Azure存储,当日志关闭时,日志文件将复制到该位置并在本地删除。

日志记录变得简单

多年来,我尝试了几个日志记录包。其中一些非常好,大多数在某些情况下很有用。我无意贬低他们中的任何一个。您可能会发现其他软件包更适合您想要的。当我在演示应用中描述此NuGet包的用法时,你可能会发现你想要的简单性、多功能性、可伸缩性和性能。

设置记录器

记录器中的关键项之一是LOG_TYPE枚举。这提供了一种指定条目类型的日志条目的方法,以及从调用代码中选择是否创建日志条目的简单方法。这允许在运行时更改记录的内容和未记录的内容。这将在后面的部分中变得明显。

这些代码行用于说明JLogger的用法。变化比文档所能显示的要多,但这显示了JLogger的完全功能使用。

首先,引用库的using

using Jeff.Jones.JLogger6;
using Jeff.Jones.JHelpers6;

下面是为要启用的调试日志选项设置类范围变量的示例。对于开发、QA、生产和故障排除生产,您设置的内容可能有所不同。

程序的此全局值通常存储在某个配置数据位置。

LOG_TYPE m_DebugLogOptions = LOG_TYPE.Error | LOG_TYPE.Informational |
                             LOG_TYPE.ShowTimeOnly | LOG_TYPE.Warning |
                             LOG_TYPE.HideThreadID | 
                             LOG_TYPE.ShowModuleMethodAndLineNumber |
                             LOG_TYPE.System | LOG_TYPE.SendEmail;

下一步是在需要记录器之前尽早设置用于配置Logger的变量,通常在程序启动代码中。

Boolean response = false;
String filePath = CommonHelpers.CurDir + @"\";
String fileNamePrefix = "MyLog";

// This value applies to both debug files and to DB log entries.
Int32 daysToRetainLogs = 30;

// Setting the Logger data so it knows how to build a log file, and
// how long to keep them. The initial debug log options is set here,
// and can be changed programmatically at anytime in the
// Logger.Instance.DebugLogOptions property.
response = Logger.Instance.SetLogData
           (filePath, fileNamePrefix, daysToRetainLogs, logOptions, "");

如果使用数据库存储日志,请使用此代码作为示例,而不是前面的代码。

注意您可以使用“useDBLogging = false”进行设置,它将使用上述SetLogData方法中指定的文件。

这些行显示如何设置基于DB的日志记录。DBLog表的T-SQL脚本和两个存储过程必须在要在其中包含日志条目的数据库上执行。

如果使用Windows身份验证访问数据库,请确保Windows帐户在SQL Server上具有必要的权限,并且可以将DBUserNameDBPassword保留为“”。内部数据库连接根据SetDBConfiguration()传入的值构造正确的连接字符串。

Boolean response = false;
String serverInstanceName = "MyComputer.SQL2020";
String dbUserName = "";
String dbPassword = "";
Boolean useWindowsAuthentication = true;
Boolean useDBLogging = true;
String databaseName = "myData";

response = Logger.Instance.SetDBConfiguration(serverInstanceName,
                                              dbUserName,
                                              dbPassword,
                                              useWindowsAuthentication,
                                              useDBLogging,
                                              databaseName);

提供了三个数据库脚本,这些脚本必须在目标SQL Server上运行。

  • DBLog.sql——创建DBLog表和主键索引。
  • spDebugLogDelete.sql——删除早于特定日期的记录。请参阅其使用的DataAccessLayer.ProcessLogRetention()方法。
  • spDebugLogInsert.sql——插入日志记录。请参阅其使用的DataAccessLayer.WriteDBLog()方法。

如果记录到文件,并且你想要使用Azure文件存储,则需要添加此配置。出于性能原因,日志文件在打开时在本地使用(由于网络开销,一次通过网络写入一行到Azure文件会慢得多)。关闭日志后,日志文件将复制到指定的Azure文件存储。日志保留在Azure文件存储上运行,而不是在本地运行,因为本地日志文件在复制到Azure文件存储后会被删除。

// Optional configuration for Azure file storage
String resourceID = "<AZURE_CONNECTION_STRING>";
String fileShareName = "<AZURE_FILE_SHARE_NAME>";
String directoryName = "<AZURE_DIRECTORY_NAME>";
response = Logger.Instance.SetAzureConfiguration
           (resourceID, fileShareName, directoryName, true);

无论日志的位置如何,其中一个选项是发送电子邮件。这是允许对指定它的日志条目使用电子邮件的配置。单独启用不会发送电子邮件。有关此内容的更多信息,请参阅下面的代码部分:

// Email setup.
// Note that the Debug Log Options must have the LOG_TYPES.SendEmail flag in order for a 
// given log entry to send an email.  
// If that flag is not in the log options bitset, then adding to the flags for a 
// log entry will not send email.  Both must be present in the log options and the 
// log entry for the email to be sent.
Int32 smtpPort = 587;  // Or whatever port your email server uses.
Boolean useSSL = true;

List<String\ sendToAddresses = new List<String>();
sendToAddresses.Add("MyBuddy@somewhere.net");
sendToAddresses.Add("John.Smith@anywhere.net");

response = Logger.Instance.SetEmailData("smtp.mymailserver.net",
                                        "logonEmailAddress@work.net",
                                        "logonEmailPassword",
                                        smtpPort,
                                        sendToAddresses,
                                        "emailFromAddress@work.net",
                                        "emailReplyToAddress@work.net",
                                        useSSL);

// This is an example of how to use the LOG_TYPES.SendMail flag when writing to the log
// so that an email is sent.
if ((m_DebugLogOptions & LOG_TYPES.Error) == LOG_TYPES.Error)
{
   Logger.Instance.WriteDebugLog(LOG_TYPES.Error & LOG_TYPES.SendEmail,
                                 exUnhandled,
                                "Optional Specific message if desired");
}

如果不需要发送电子邮件,则使用相同的日志条目:

if ((m_DebugLogOptions & LOG_TYPES.Error) == LOG_TYPES.Error)
{
   Logger.Instance.WriteDebugLog(LOG_TYPES.Error,
             exUnhandled,
             "Optional Specific message if desired");
}

配置只需执行一次。但是,为了与动态目标保持一致,可以在运行时将Logger.DebugLogOptions属性设置为所需的任何位集。例如,如果要在不重新启动系统的情况下增加日志记录,只需更新配置文件中的调试日志选项值以打开更多日志记录位,并让监视配置文件的任何进程更新Logger.DebugLogOptions属性。现在将记录更多内容,您可以将日志记录量减少到所需的正常水平。

代码中的日志记录示例

需要注意的一件事是调用日志记录方法之前的位集比较。如果未打开该位,则不会执行记录某些内容的代码。因此,除非使用更多日志条目,否则添加更多日志条目不会影响性能。因此,可以对性能和流程等内容进行编码,但除非打开,否则不会影响性能。此方法允许在代码中设计许多通用性以进行调试和分析,而不会影响性能。

// Example of use in a method
void SomeMethod()
{
  // Use of the Flow LOG_TYPE shows in the log when a method was entered,
  // and exited. Useful for debugging, QA, and development. The Flow bit
  // mask is usually turned off in production to reduce log size.
  if ((m_DebugLogOptions & LOG_TYPE.Flow) == LOG_EXCEPTION_TYPE.Flow)
  {
    Logger.Instance.WriteToDebugLog(LOG_TYPE.Flow, "1st line in method", "");
  }
  // This variable notes when the method started.
  DateTime methodStart = DateTime.Now;
  try
  {
    // Do some work here
    // This is an example of logging used during
    // process flow. The bitmask used here does not
    // have to be "Informational", and may be turned
    // off in production.
    Logger.Instance.WriteToDebugLog(LOG_TYPE.Informational,
                                    "Primary message",
                                    "Optional detail message");
    // Do some more work
  }
  catch (Exception exUnhandled)
  {
    // Capture some runtime data that may be useful in debugging.
    exUnhandled.Data.Add("SomeName", "SomeValue");
    if ((m_DebugLogOptions & LOG_TYPE.Error) == LOG_TYPE.Error)
    {
      Logger.Instance.WriteToDebugLog(LOG_TYPE.Error,
                                      exUnhandled,
                                      "Optional detail message");
    }
  }
  finally
  {
    if ((m_DebugLogOptions & LOG_TYPE.Performance) == LOG_TYPE.Performance)
    {
      TimeSpan elapsedTime = DateTime.Now - methodStart;
      Logger.Instance.WriteToDebugLog(LOG_TYPE.Performance,
                                      String.Format("END; 
                                      elapsed time = [{0:mm} mins, 
                                      {0:ss} secs, {0:fff} msecs].", objElapsedTime));
    }
    // Capture the flow for exiting the method.
    if ((m_DebugLogOptions & LOG_TYPE.Flow) == LOG_EXCEPTION_TYPE.Flow)
    {
      Logger.Instance.WriteToDebugLog(LOG_TYPE.Flow, "Exiting method", "");
    }
  }
} // END of method 

日志记录代码的大部分使用都可以复制和粘贴,从而缩短开发时间。

ILogger选项

为了在使用该ILogger接口的.NET应用程序中使用JLogger6,只需获取对Logger.Instance对象接口ILogger的引用。

LogLevel日志类型的JLogger6转换:

  • LogLevel.Critical添加到LOG_TYPE.Fatal调试日志选项
  • LogLevel.Debug添加LOG_TYPE.System到调试日志选项
  • LogLevel.Error添加LOG_TYPE.Error到调试日志选项
  • LogLevel.Information添加LOG_TYPE.Informational到调试日志选项
  • LogLevel.Warning添加LOG_TYPE.Warning到调试日志选项
  • LogLevel.Trace添加LOG_TYPE.Flow到调试日志选项

这些是ILogger方法以及它们如何使用基础Logger实例。

void Log<TState>(LogLevel logLevel, EventId eventId, 
     TState state, Exception exception, Func<TState, Exception, string> formatter)

以以下方式写入日志:

WriteDebugLog(logType, exception, $"EventID = {eventId.ToString()}; 
              State = {state.ToString()}");

bool IsEnabled(LogLevel logLevel)

检查调试日志选项位集以查看转换后的位是否已启用。

IDisposable BeginScope<TState>(TState state)

启动日志并返回对Logger.Instance对象的IDisposable引用。

让我们看一下日志

这是前几行的示例。当日志启动时,Logger会自动拍摄许多系统值的快照,这些值已被证明在以后的诊断和分析中很有用。

这些列是:

  1. 时间(或Date/Time,取决于LOG_TYPE标志,ShowTimeOnly)。这提供了低至毫秒的时间。通常,日志在午夜关闭并启动新日志,因此通常使用该ShowTimeOnly标志。
  2. 日志类型——条目的日志类型(与日志条目一起使用的LOG_TYPE值的名称)
  3. 消息——日志条目的主消息
  4. 添加信息——附加的,通常更详细的,解释Message的信息。
  5. 异常数据——异常数据集合的名称/值对。明智地使用此Exception类功能对于节省故障排除和诊断时间至关重要。
  6. 堆栈信息——来自异常实例的堆栈信息
  7. 模块——发生日志条目的模块的名称
  8. 方法——发生日志条目的方法的名称
  9. 行号——发生错误的行号
  10. 线程 ID——.NET线程ID(如果提供)

日志文件也可以直接在Excel(或任何解释制表符分隔列的电子表格)中打开。Excel允许比记事本等文本编辑器更复杂的搜索和分析。

如果无法写入日志文件或日志数据库表,则Logger会创建一个紧急日志文件,也以制表符分隔。它是直接编写的(而不是通过队列)并提供基本信息。

无论如何,Logger尝试确保写入日志条目。

演示应用概览

演示程序是一个.NET 6 Windows窗体应用程序。与Logger的正常实现不同,Logger实际上在运行测试按钮调用的代码中配置、运行和关闭。本演示的目的是展示如何在各种配置中使用Logger

日志文件配置

Azure文件存储配置

数据库配置

可以从 GitHub - MSBassSinger/LoggingDemo6: Demo for JLogger 6 下载代码,以便您可以根据需要逐步执行并对其进行测试。

即将推出的功能

我正在开发下一个版本,以支持文件日志和数据库中的用户定义字段。该概念是允许在配置日志时定义和/或创建用户定义的字段,因此,随着时间的推移,它们可能会针对任何给定的应用程序而更改。

此外,我正在研究数据库日志存储的选项,以添加审核表,以便在DBLogAudit表中记录从DBLog表中删除的记录。对于必须保留日志记录和对日志记录执行的操作的用户,可能需要此选项。

结论

我想创建一个具有更简单、更一致的设置和使用记录器。我想提供广泛的日志类型,而不必返回并重新编码,因此打开和关闭位可以满足这一点。我希望记录器能够以高吞吐量的日志条目处理多个线程和任务,而不会影响性能。

依赖注入(DI)纯粹主义者可能反对使用单例。但是,DI是一个设计概念,旨在应用于在另一个对象中创建的、影响业务规则的对象。对于记录器,它不是在对象中创建的,它不会影响业务规则。因此,使用单例记录器并不违反DI的原始目的。正如对象可以通过构造函数、方法或属性注入依赖项一样,引入外部对象(Logger实例)与引入单一实例一样有效。在所有三种情况下,都会注入(推送或拉取)而不是创建对Logger实例的引用。

一些开发人员已经有了最喜欢的记录器。其他人只是用最少的工作拿最简单的东西。但是,如果您愿意查看JLogger6中是否有价值,并希望充分利用日志记录,我希望您能给这个记录器一个好机会。

背景

40多年来,我一直在多个操作系统和多种语言上开发软件。早在Windows之前。早在Linux之前。日志记录的几个一致性之一是需要日志记录以衡量性能。记录错误、警告和其他信息,所有这些都使解决生产、QA和开发应用程序问题的工作变得不那么痛苦。我编写了此组件,以便我可以在任何场景中使用日志记录,这些场景仅针对我需要的内容进行配置,而不会减慢应用程序执行速度。

使用演示代码

GitHub存储库中提取演示项目。该代码注释良好,并演示了如何配置和使用JLogger6。该演示是在Visual Studio 2022中用C#编写的。演示代码面向.NET 6JLogger6.NET 6为目标。

兴趣点

我想创建一个易于使用、易于设置的日志记录组件,并且使用位比较,在不需要时会跳过对日志的方法调用。当我遇到数百到数千个任务/线程尝试写入日志文件的情况时,我决定使用排队方法来写入日志文件。它大大减慢了UI的速度。更改为该体系结构消除了该问题。

https://www.codeproject.com/Articles/5163318/JLogger6-When-You-Care-to-Write-the-Very-Best

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
mysql 是一个流行的关系型数据库管理系统,而 log4j 则是一个 Java 应用程序中常用的日志记录工具。log4jlogger_org.apache.log4j.Logger 是一个 log4j 框架中的 Logger 类,用于在应用程序中记录日志Logger 实例被用于记录应用程序中的各种事件。它们可以配置为将这些事件记录到不同的目的地,如控制台、文件或数据库中。Logger 可以分级别记录日志,包括 DEBUG、INFO、WARN、ERROR 和 FATAL 五个级别,可以根据需要进行配置。 在使用 log4jlogger_org.apache.log4j.Logger 记录日志时,需要首先创建一个 Logger 实例。这可以通过调用 LogManager 类的 getLogger() 方法来实现,例如: ``` import org.apache.log4j.Logger; public class MyClass { private static final Logger logger = LogManager.getLogger(MyClass.class); public void doSomething() { logger.info("Doing something..."); } } ``` 在这个例子中,我们使用 getLogger() 方法创建了一个名为 "MyClass" 的 Logger 实例。然后,在 doSomething() 方法中,我们使用 logger 实例记录了一条 INFO 级别的日志消息。 需要注意的是,Logger 实例是按照包名来命名的。因此,在上面的例子中,Logger 实例的完整名称是 "com.example.MyClass"。此外,Logger 实例通常被声明为 private static final 类型,以便在整个类中都可用。 总之,log4jlogger_org.apache.log4j.Logger 是 log4j 框架中的一个重要组件,用于在应用程序中记录日志。通过使用 Logger 实例,可以轻松地记录各种事件,并将它们记录到不同的目的地。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值