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

简化 .NET 应用程序日志记录:与 Azure Log Analytics 和 CI/CD 自动化无缝集成

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024 年 5 月 14 日

CPOL

6分钟阅读

viewsIcon

19801

了解如何通过与 Azure Log Analytics 集成并使用 GitHub Actions 自动化该过程来简化 .NET 应用程序日志记录。

引言

在本文中,我们将介绍 CustomCloudLogger,这是一个用于 .NET Standard 2.0 的多功能日志客户端,可简化将日志发送到 Azure Log Analytics 工作区的过程。此 NuGet 包对于使用各种 .NET 平台(包括 .NET Core、.NET Framework、Xamarin 和 Tizen)的开发人员特别有用。CustomCloudLogger 允许您创建自定义日志对象并将其直接发送到 Azure,使日志记录变得简单高效。此解决方案的灵感来自 LogAnalytics.Client(仅限 .NET Core/5/6)包,它提供比最常用包更灵活、更全面的日志记录机制。这是因为属性会自动转换为列。

背景

日志记录是应用程序开发的重要方面,它允许开发人员跟踪和诊断问题、监控应用程序性能并深入了解用户行为。Azure Log Analytics 是一个强大的工具,可聚合和分析来自各种来源的日志数据。CustomCloudLogger 项目旨在通过提供一种无缝的方式将日志发送到 Azure 工作区,无论您想查看哪些列、使用哪个 .NET 环境、使用哪个 .NET 版本或使用哪个操作系统,从而弥合 .NET 应用程序和 Azure Log Analytics 之间的鸿沟。

使用代码

CustomCloudLogger 包可在 NuGetGitHub 上获取,并且可以轻松安装到您的 .NET 项目中。以下是入门步骤

步骤 1:安装包

dotnet add package ConnectingApps.CustomCloudLogger

步骤 2:初始化 LogAnalyticsClient

使用您的 Azure 工作区 ID 和共享密钥创建 LogAnalyticsClient 实例

LogAnalyticsClient _client = new(WorkSpaceId, SharedKey);

步骤 3:创建和发送日志条目

您可以创建自定义日志条目并将其发送到 Azure Log Analytics,如下所示

var logEntries = new TryData[]
{
    new()
    {
        X = $"Test1 {Environment.MachineName}",
        Y = 1,
    },
    new()
    {
        X = $"Test2 {Environment.MachineName}",
        Y = 2,
    }
};
await _client.LogEntryAsync(logEntries.First(), "TuplesLog");
await _client.LogEntriesAsync(logEntries, "TuplesLog");

在这个代码中

  1. 创建 LogAnalyticsClient 实例。
  2. 使用 LogEntryAsync 发送单个日志条目。
  3. 使用 LogEntriesAsync 发送多个日志条目。

请记住在生产代码中正确处置客户端以释放资源。

步骤 4:在 Azure 中查询日志

发送日志后,您可以使用 Kusto 查询语言 (KQL) 在 Azure Log Analytics 中查询它们。例如

TuplesLog_CL
| where X_s contains "Test"

步骤 5:分析和处理日志数据

执行查询后,您将在 Azure Log Analytics 界面中看到显示的结果。您可以根据这些结果采取各种操作,例如设置新的警报规则或优化查询以进行更精确的日志分析。下面是此类结果的示例

Log Analytics Workspace

在屏幕截图中,您可以看到与查询条件匹配的日志条目。您可以使用此数据监控应用程序行为、检测异常并实时响应关键事件。例如,您可以设置警报规则,在满足特定条件时通知您,从而确保您可以及时解决问题。

关键方法解释

CustomCloudLogger 依赖于两个关键方法才能有效运行:SendLogEntriesPrivateAsyncValidatePropertyTypes

SendLogEntriesPrivateAsync

此方法处理将日志条目发送到 Azure 的核心功能。以下是其工作原理

private async Task SendLogEntriesPrivateAsync<t>(IReadOnlyList<t> entities, string logType, string? resourceId = null, string? timeGeneratedCustomFieldName = null)
{
    foreach (var entity in entities)
    {
        ValidatePropertyTypes(entity);
    }

    var dateTimeNow = DateTime.UtcNow.ToString("r", System.Globalization.CultureInfo.InvariantCulture);
    var entityAsJson = JsonSerializer.Serialize(entities, SerializeOptions);
    var authSignature = GetAuthSignature(entityAsJson, dateTimeNow);

    var headers = new Dictionary<string, string="">
    {
        {"Authorization", authSignature},
        {"Log-Type", logType},
        {"x-ms-date", dateTimeNow},
        {"time-generated-field", timeGeneratedCustomFieldName},
        {"x-ms-AzureResourceId", resourceId}
    };
    
    using var request = new HttpRequestMessage(HttpMethod.Post, this._requestBaseUrl);
    foreach (var header in headers.Where(h => !string.IsNullOrEmpty(h.Value)))
    {
        request.Headers.Add(header.Key, header.Value);
    }

    HttpContent httpContent = new StringContent(entityAsJson, Encoding.UTF8);
    httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");

    request.Content = httpContent;
    var response = await _httpClient.SendAsync(request).ConfigureAwait(false);
    response.EnsureSuccessStatusCode();
}

在此方法中

  1. 它首先使用 ValidatePropertyTypes 方法验证每个实体中属性的类型。
  2. 然后,它通过将实体序列化为 JSON 并生成授权签名来准备要发送的数据。
  3. 设置标头,包括授权、日志类型和日期。
  4. 发出 HTTP POST 请求以将日志数据发送到 Azure。
  5. 它确保请求成功,如果失败则抛出异常。

ValidatePropertyTypes

此方法确保正在记录的实体的属性是允许的类型

private void ValidatePropertyTypes<t>(T entity)
{
    // Retrieve all properties of the entity type using reflection
    var properties = entity!.GetType().GetProperties();

    // Validate each property's type
    foreach (var propertyInfo in properties)
    {
        if (!AllowedTypes.Contains(propertyInfo.PropertyType))
        {
            var typedString = propertyInfo.PropertyType.ToString();
            // Check for .NET 6+ types
            if (typedString.EndsWith("System.DateOnly") || typedString.EndsWith("System.DateOnly]"))
            {
                continue;
            }
            throw new ArgumentOutOfRangeException(
                $"Property '{propertyInfo.Name}' of entity with type '{entity.GetType()}' " +
                $"is not one of the valid properties:" +
                $" String, Boolean, Double, Integer, DateTime, DateOnly and Guid.");
        }
    }
}

此方法:

  1. 使用反射检索实体类型的所有属性。
  2. 检查每个属性以确保它是允许的类型之一(例如,string、bool、double、int、DateTime、DateOnly、Guid)。DateOnly 被检查为字符串,因为此类型在 .NET Standard 中不直接支持。它是 .NET 6 中引入的一种相对较新的类型。
  3. 如果属性不是允许的类型,则抛出异常。

这些方法对于确保发送到 Azure Log Analytics 的数据有效且格式正确至关重要,从而维护日志记录过程的完整性和可靠性。

GitHub Actions 流水线解释

CustomCloudLogger 项目使用 GitHub Actions 自动化构建和测试过程。下面是 GitHub Actions 流水线配置和每个步骤的解释

name: .NET CI

on:
  push:
    branches:
      - main
      - release
      - develop
      - feature/**
      - bugfix/**

jobs:
  build_and_test:
    name: Build and Test
    runs-on: ubuntu-22.04

    env:  # Setting environment variable at the job level
      WORKSPACE_ID: ${{ secrets.WORKSPACE_ID }}
      SHARED_KEY: ${{ secrets.SHARED_KEY }} 

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup .NET SDK
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x' 

      - name: Restore dependencies
        run: dotnet restore CustomCloudLogger.sln

      - name: Build Solution
        run: dotnet build CustomCloudLogger.sln --configuration Release --no-restore

      - name: Run Tests
        run: python -c "import os; os.system('dotnet test CustomCloudLogger.sln --configuration Release --no-build --verbosity normal  --logger trx');"  

      - name: Publish Test Results
        uses: dorny/test-reporter@v1
        with:
          name: 'Test Results'
          path: '**/TestResults/**/*.

trx'
          reporter: 'dotnet-trx'

      - name: Upload NuGet packages as artifacts
        uses: actions/upload-artifact@v4
        with:
          name: nuget-packages
          path: '**/*.nupkg'  

      - name: Publish to NuGet
        if: github.ref == 'refs/heads/main'
        run: dotnet nuget push "ConnectingApps.CustomCloudLogger/bin/Release/*.nupkg" --api-key ${{ secrets.NUGET_KEY }} --source https://api.nuget.org/v3/index.json       

此流水线由对特定分支(main、release、develop、feature/*、bugfix/*)的推送触发。它包含以下步骤

  1. Checkout code:此步骤使用 actions/checkout@v4 操作从存储库中检出代码。
  2. Setup .NET SDK:此步骤使用 actions/setup-dotnet@v4 操作安装指定版本的 .NET SDK (8.0.x)。
  3. Restore dependencies:此步骤运行 dotnet restore 命令以恢复项目的依赖项。
  4. Build Solution:此步骤运行 dotnet build 命令以在 Release 配置中构建解决方案,而无需再次恢复依赖项。
  5. Run Tests:此步骤运行 dotnet test 命令以执行测试。它使用 Python 脚本运行命令并为测试结果生成 trx 文件。使用 Python 而不是直接命令行执行的原因是,如果其中一个测试失败,该步骤不应失败。这将阻止执行下一步。
  6. Publish Test Results:此步骤使用 dorny/test-reporter@v1 操作发布测试结果。如果其中一个测试失败,则此步骤是最后一步。
  7. Upload NuGet packages as artifacts:此步骤使用 actions/upload-artifact@v4 操作将生成的 NuGet 包作为构建工件上传。
  8. Publish to NuGet:如果分支是 main,则此步骤运行 dotnet nuget push 命令将包发布到 NuGet.org。

流水线使用机密来安全地管理敏感信息

  • WORKSPACE_ID:Azure Log Analytics 工作区的 ID。
  • SHARED_KEY:用于通过 Azure Log Analytics 工作区进行身份验证的共享密钥。
  • NUGET_KEY:用于将包发布到 NuGet.org 的 API 密钥。

这些机密安全地存储在 GitHub 存储库的设置中,并使用 ${{ secrets.[SECRET_NAME] }} 语法在流水线中引用。

集成测试

CustomCloudLogger 项目包括集成测试,以确保日志记录功能按预期与 Azure Log Analytics 配合使用。GitHub Actions 流水线设置了必要的环境变量,然后在集成测试中使用这些变量。下面是集成测试的相关源代码

private static readonly string WorkSpaceId;
private static readonly string SharedKey;
private readonly LogAnalyticsClient _client = new(WorkSpaceId, SharedKey);

static LogAnalyticsClientTest()
{
    WorkSpaceId = Environment.GetEnvironmentVariable("WORKSPACE_ID")!;
    SharedKey = Environment.GetEnvironmentVariable("SHARED_KEY")!;
    WorkSpaceId.Should().NotBeNullOrEmpty();
    SharedKey.Should().NotBeNullOrEmpty();
}

[Fact]
public async Task SendMessageTest()
{
    var logEntry = new 
    {
        Date = DateTime.UtcNow,
        Message = $"Test log message System Text from {Environment.MachineName}",
        Severity = "Info"
    };

    await _client.LogEntryAsync(logEntry, "TestLog");
}

在此测试中

  1. WorkSpaceIdSharedKey 从 GitHub Actions 流水线设置的环境变量中检索。
  2. 使用这些环境变量创建 LogAnalyticsClient 实例。
  3. SendMessageTest 方法创建包含当前日期、测试消息和严重性级别的日志条目。
  4. 使用 LogEntryAsync 方法将日志条目发送到 Azure Log Analytics 工作区。

这些集成测试确保日志客户端可以成功与 Azure Log Analytics 通信,从而确保日志记录功能将在生产环境中正常工作。

关注点

使用 CustomCloudLogger 进行开发的一个有趣方面是它支持各种 .NET 版本和平台。包含适用于 .NET 6 及更高版本的 DateOnly 数据类型尤其值得注意,它提供了现代日期处理功能。此外,与 Azure 强大的日志分析工具集成,为开发人员提供了一个强大的日志记录解决方案,可随其应用程序扩展。

历史

这是 CustomCloudLogger 文章的初始版本。未来的更新将包括更详细的示例、性能基准以及企业应用程序中大规模日志记录的最佳实践。

© . All rights reserved.