65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Core 企业级微服务:将日志记录器作为微服务

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2021年8月21日

CPOL

7分钟阅读

viewsIcon

14893

downloadIcon

228

将日志记录系统作为您生态系统中的其他微服务来使用

背景

传统方法

在单一应用程序或由不同供应商提供的独立应用程序世界中,在系统中记录信息的主要模式是使用直接集成在应用程序中并单独配置的日志程序。

但在微服务生态系统中,我们可能有几十个微服务,每个微服务都需要复杂的配置和安装,才能使用标准方法进行日志记录,这给维护系统带来了许多问题。让我们看看其中一些问题。

  • 您需要使用所选的日志记录框架单独配置每个服务。
  • 每个微服务都需要访问存储日志的数据库。
  • 如果您想更新日志记录器,您需要单独更新每个微服务。

另一方面,企业中的微服务越来越倾向于安装在云端,使用容器,甚至在不同的操作系统上。

另一个关注点是安全性,如果您想将日志记录到数据库,您需要为每个服务提供对数据库系统的访问权限。

将日志记录器作为服务的方法

那么,如果微服务很好,为什么不使用相同的概念来创建我们的日志记录系统呢?将日志记录器作为服务使用为您带来了许多优势,首先,日志记录框架只安装在一个微服务中,其余的只需要使用某种网络协议将信息发送到日志记录器,从而最大程度地减少了数据库资源的暴露。

这种架构还提供了以下优势

首先,服务的更新只需要在一个服务中完成:日志记录微服务。如果您不更改与日志记录客户端的接口,您可以在一个服务中更新日志记录框架,甚至更改为其他框架,而无需触及系统的其余部分。

第二个巨大的胜利是,您只需要在这个单独的服务中配置对数据库的访问,而不是在所有服务中。

另一个好处是,您可以将数据库隐藏在您的内网深处,日志记录服务需要向公司内网内的微服务开放,提供一定级别的安全性,但您的数据库将位于一个与使用日志记录微服务的服务完全隔离的子网中。

最后,但同样重要的一个巨大价值是,由于日志记录器是一个服务,您无需关心客户端运行在什么之上,对您来说,它是一个访问您服务的人。

实现日志记录器作为服务

除了创建日志记录器作为服务的总体策略之外,我们还可以引入一些设计决策,使我们的服务比传统的服务框架更具灵活性。

每个微服务日志记录到其单独的表中

您可能会问日志记录器如何对数据库中来自不同服务的不同条目进行分类,在此实现中,我们决定将每个微服务记录到不同的表中,为了管理这一点,日志记录请求中有一个字段,其中包含每个服务的特定令牌。

令牌用于获取表的名称,链接此信息的数据位于我们应用程序的 appsettings.json 中(如果您使用的是 .NET 的早期版本,则在 web.config 中)。如果令牌存在,则选择该表,日志记录器将信息写入该表。

错误级别控制

在我们的方法中,我们尽可能保持服务的简单性,我们作为示例使用的实现不管理错误级别,然后我们将其委托给客户端 .dll(可以实现为 NuGet 包),您可以根据您的特定需求创建错误级别的管理,并根据设置级别决定信息何时应移至日志记录器或被拒绝。

这在客户端服务中引入了一定程度的配置,但在应用程序配置中管理设置字符串并不是什么大问题。

Using the Code

我们创建了一个作为开源在 Github 上可下载的日志记录器即服务的实现。我们在这里解释了代码的基本部分。最终,我们创建的日志记录器是一个能够记录到 SQL Server 数据库的简单服务。我们将其作为示例创建,但如果需要,该代码能够在生产环境中运行。

该服务的代码基本具有以下结构

  • 记录信息的服务
  • 内部日志,用于记录自身问题
  • 配置设置,用于为每个要使用的客户端添加令牌-表对
  • 数据库访问,我们在此不讨论,因为它很简单

该服务非常简单

/// <summary>
/// Enter the info in the log.
/// if error returns a http 500 internal server error 
/// and an HttpError based in the exception information
/// </summary>
/// <param name="request">
/// The information to be logged
/// </param>
/// <returns>null is ok</returns>
/// <exception cref="HttpError">If error return a httpError Class</exception>
[HttpPost("LogEntryAsync")]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(typeof(ErrorManager), StatusCodes.Status500InternalServerError)]
[ProducesResponseType(typeof(ErrorManager), StatusCodes.Status400BadRequest)]
public async Task<ActionResult> PostLogEntryAsync([FromBody] LogRequest request)
{
      await bo.PostLogEntryAsync(request);
      return Ok();
}

要记录的对象如下

/// <summary>
/// The Log Request Class
/// </summary>
public class LogRequest
{
     /// <summary>
     /// Used to link the error to the user with the log 
     /// logged
     /// </summary>
     [DataType(DataType.DateTime)]
     public DateTime? CreatedDate { get; set; }
 
     /// <summary>
     /// The name of the machine where log is emitted
     /// </summary>
     [Required(AllowEmptyStrings = false)]
     [StringLength(maximumLength: 255, MinimumLength = 5)]
     public string MachineName { get; set; }
 
     /// <summary>
     /// The register Level
     /// </summary>
     [Required]
     [StringLength(50, MinimumLength = 5)]
     public string Level { get; set; }
 
     /// <summary>
     /// The name of the logger, 
     /// this is the token used to localized the table
     /// </summary>
     [Required(AllowEmptyStrings = false)]
     [StringLength(maximumLength: 255, MinimumLength = 5)]
     public string Logger { get; set; }
 
     /// <summary>
     /// The message to be show
     /// </summary>
     [Required(AllowEmptyStrings = true)]
     public string Message { get; set; }
 
     /// <summary>
     /// The exception Message to be logged
     /// </summary>
     public string Exception { get; set; }
}

请求对象中值得注意的地方是

  • CreatedTime:可选。为什么?我们可以让日志记录器输入日志发生时的日期和时间,但您也可以从您的服务中设置日期时间。当您想将用户友好的消息与存储在日志记录器中的技术信息关联起来时,这特别有用。
  • MachineName:此字段允许您输入发送日志请求的机器名称。这在微服务中非常有用,当您可能有不同的微服务实例时,可以确切地知道哪台机器发送了信息。
  • Level:这是输入日志信息的级别。通常,值包括 ERROR FATALINFO 等。这是一个开放字段。其想法是您可以使用一个配置错误级别的客户端。例如,当用户使用 NuGet 包时,您可以在特定的客户端中设置错误级别,并使每个客户端的级别不同。
  • Logger:此字段发送令牌以识别使用该服务的客户端。它与 appSettings.json 结合使用,以确定您希望将信息记录到哪个表中。
  • MessageException:使用这两个字段输入您想要记录的信息。

内部日志

这用于在其适当的表中记录错误,因为令牌不正确,请求对象格式错误,数据库中缺少表等。内部日志记录器有其自身的设置配置。

内部日志记录器还以防止应用程序中错误循环的形式完成。

配置内部日志记录器的参数很简单,如以下代码所示

{
  "InternalLogSettings": {
    "Table": "ServeLog",
    "_Comment": "Log Level Values: DEBUG, ERROR",
    "Level": "DEBUG",
    "_Comment1": "Time Zone Examples: UTC, Eastern Standard Time",
    "TimeZone": "UTC"
  }
}

您可以选择不同的时区来输入日志注释。

另一个重要的事情是如何将令牌与 Table 名称匹配。这可以通过使用 appSettings.json 文件轻松完成。

{
  "Tables": {
    "TESTTOKEN": "TestLog",
    "YOURTOKEN": "YourTableLog" 
  }
}

第一个条目是用于内部日志记录器,您可以根据您拥有的客户端数量,添加更多条目以记录到不同的表中。在示例中,我们添加了一个额外的条目。

关注点

  • 当您拥有数十个微服务的生态系统时,安装在每个服务中的传统日志记录器可能会很麻烦。您需要单独配置每个服务,而且如果您要将日志记录到数据库中,还需要做大量的数据库工作。
  • 另一方面,如果您创建一个功能是日志记录的微服务,那么该日志记录器是您环境中唯一的其他微服务,可以通过 HTTP 协议像其他服务一样访问。通过这种配置,配置和数据库访问等问题可以大大简化。
  • 我们在这里解释了一个示例,该示例允许您将每个服务记录到不同的表中,还可以使用软件客户端个性化您的日志记录器级别。
  • 您可以在我们的示例中看到一个功能齐全、可用于生产的服务。您可以从 GIT 下载,或在下面的评论区联系我们。该代码作为开源项目提供。 
  • 您可以在 https://youtu.be/3O1DymP8XOo 观看与本文相关的视频。
  • 此处提供了示例的副本 这里

历史

  • 2021年8月21日
    • 第一版
    • 添加了缺失的链接和代码
© . All rights reserved.