目录
介绍
本文介绍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应用程序和Azure Log Analytics之间的差距,而不管你想要查看哪些列、使用什么.NET环境、使用什么.NET版本或你使用的操作系统。
使用代码
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/*)触发。它包括以下步骤:
- 结帐代码:此步骤使用该actions/checkout@v4操作从存储库中签出代码。
- 安装.NET SDK:此步骤使用该actions/setup-dotnet@v4操作安装指定版本的.NET SDK(8.0.x) 。
- 恢复依赖项:此步骤运行dotnet restore命令以还原项目的依赖项。
- 构建解决方案:此步骤运行dotnet build命令以在发布配置中生成解决方案,而无需再次还原依赖项。
- 运行测试:此步骤运行dotnet test命令以执行测试。它使用Python脚本运行命令并为测试结果生成trx文件。使用python而不是直接命令行执行的原因是,如果其中一个测试失败,该步骤不应失败。这将阻止执行下一步。
- 发布测试结果:此步骤使用该dorny/test-reporter@v1操作发布测试结果。其中一个测试失败意味着此步骤是最后一个步骤。
- 将NuGet包上传为项目:此步骤使用该actions/upload-artifact@v4操作将生成的NuGet包作为生成项目上传。
- 发布到NuGet:Publish to NuGet:此步骤运行dotnet nuget push命令以将包发布到NuGet.org(如果分支为main)。
管道使用机密来安全地管理敏感信息:
- 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强大的日志分析工具集成可为开发人员提供可靠的日志记录解决方案,该解决方案可随其应用程序进行缩放。
https://www.codeproject.com/Articles/5382319/Simplifying-NET-Application-Logging-Seamless-Integ