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





5.00/5 (2投票s)
了解如何通过与 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 包可在 NuGet 和 GitHub 上获取,并且可以轻松安装到您的 .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");
在这个代码中
- 创建
LogAnalyticsClient
实例。 - 使用
LogEntryAsync
发送单个日志条目。 - 使用
LogEntriesAsync
发送多个日志条目。
请记住在生产代码中正确处置客户端以释放资源。
步骤 4:在 Azure 中查询日志
发送日志后,您可以使用 Kusto 查询语言 (KQL) 在 Azure Log Analytics 中查询它们。例如
TuplesLog_CL
| where X_s contains "Test"
步骤 5:分析和处理日志数据
执行查询后,您将在 Azure Log Analytics 界面中看到显示的结果。您可以根据这些结果采取各种操作,例如设置新的警报规则或优化查询以进行更精确的日志分析。下面是此类结果的示例
在屏幕截图中,您可以看到与查询条件匹配的日志条目。您可以使用此数据监控应用程序行为、检测异常并实时响应关键事件。例如,您可以设置警报规则,在满足特定条件时通知您,从而确保您可以及时解决问题。
关键方法解释
CustomCloudLogger
依赖于两个关键方法才能有效运行:SendLogEntriesPrivateAsync
和 ValidatePropertyTypes
。
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();
}
在此方法中
- 它首先使用
ValidatePropertyTypes
方法验证每个实体中属性的类型。 - 然后,它通过将实体序列化为 JSON 并生成授权签名来准备要发送的数据。
- 设置标头,包括授权、日志类型和日期。
- 发出 HTTP POST 请求以将日志数据发送到 Azure。
- 它确保请求成功,如果失败则抛出异常。
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.");
}
}
}
此方法:
- 使用反射检索实体类型的所有属性。
- 检查每个属性以确保它是允许的类型之一(例如,string、bool、double、int、DateTime、DateOnly、Guid)。DateOnly 被检查为字符串,因为此类型在 .NET Standard 中不直接支持。它是 .NET 6 中引入的一种相对较新的类型。
- 如果属性不是允许的类型,则抛出异常。
这些方法对于确保发送到 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/*)的推送触发。它包含以下步骤
- Checkout code:此步骤使用
actions/checkout@v4
操作从存储库中检出代码。 - Setup .NET SDK:此步骤使用
actions/setup-dotnet@v4
操作安装指定版本的 .NET SDK (8.0.x)。 - Restore dependencies:此步骤运行
dotnet restore
命令以恢复项目的依赖项。 - Build Solution:此步骤运行
dotnet build
命令以在 Release 配置中构建解决方案,而无需再次恢复依赖项。 - Run Tests:此步骤运行
dotnet test
命令以执行测试。它使用 Python 脚本运行命令并为测试结果生成 trx 文件。使用 Python 而不是直接命令行执行的原因是,如果其中一个测试失败,该步骤不应失败。这将阻止执行下一步。 - Publish Test Results:此步骤使用
dorny/test-reporter@v1
操作发布测试结果。如果其中一个测试失败,则此步骤是最后一步。 - Upload NuGet packages as artifacts:此步骤使用
actions/upload-artifact@v4
操作将生成的 NuGet 包作为构建工件上传。 - 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");
}
在此测试中
WorkSpaceId
和SharedKey
从 GitHub Actions 流水线设置的环境变量中检索。- 使用这些环境变量创建
LogAnalyticsClient
实例。 SendMessageTest
方法创建包含当前日期、测试消息和严重性级别的日志条目。- 使用
LogEntryAsync
方法将日志条目发送到 Azure Log Analytics 工作区。
这些集成测试确保日志客户端可以成功与 Azure Log Analytics 通信,从而确保日志记录功能将在生产环境中正常工作。
关注点
使用 CustomCloudLogger 进行开发的一个有趣方面是它支持各种 .NET 版本和平台。包含适用于 .NET 6 及更高版本的 DateOnly
数据类型尤其值得注意,它提供了现代日期处理功能。此外,与 Azure 强大的日志分析工具集成,为开发人员提供了一个强大的日志记录解决方案,可随其应用程序扩展。
历史
这是 CustomCloudLogger 文章的初始版本。未来的更新将包括更详细的示例、性能基准以及企业应用程序中大规模日志记录的最佳实践。