YtSoft.Gmc.Core.Logging.Contracts
1.0.2
dotnet add package YtSoft.Gmc.Core.Logging.Contracts --version 1.0.2
NuGet\Install-Package YtSoft.Gmc.Core.Logging.Contracts -Version 1.0.2
<PackageReference Include="YtSoft.Gmc.Core.Logging.Contracts" Version="1.0.2" />
<PackageVersion Include="YtSoft.Gmc.Core.Logging.Contracts" Version="1.0.2" />
<PackageReference Include="YtSoft.Gmc.Core.Logging.Contracts" />
paket add YtSoft.Gmc.Core.Logging.Contracts --version 1.0.2
#r "nuget: YtSoft.Gmc.Core.Logging.Contracts, 1.0.2"
#:package YtSoft.Gmc.Core.Logging.Contracts@1.0.2
#addin nuget:?package=YtSoft.Gmc.Core.Logging.Contracts&version=1.0.2
#tool nuget:?package=YtSoft.Gmc.Core.Logging.Contracts&version=1.0.2
YtSoft.Gmc.Core.Logging.Contracts 1.0.2
YtSoft.Gmc.Core.Logging.Contracts 是日志生产端、统一消费端和业务自定义消费者共用的纯消息契约包。它只包含 DTO、枚举、JSON 序列化和消息校验,不读取 Nacos,不引用 YtSoft.Gmc.Core.Config、RabbitMQ 或 MongoDB。
引用方式和职责
生产业务系统只需引用 YtSoft.Gmc.Core.Logging;Logging 会传递引入 Config 1.0.3 和本契约包,业务代码不需要重复声明 Contracts。
只消费业务自有队列的系统可以直接引用 Config 和 Contracts:
<PackageReference Include="YtSoft.Gmc.Core.Config" Version="1.0.3" />
<PackageReference Include="YtSoft.Gmc.Core.Logging.Contracts" Version="1.0.2" />
Config 1.0.3 和 Contracts 1.0.2 的可还原状态以实际使用的 NuGet 源为准。消费者仍应通过 Config 获取 MQ 连接信息,不要自行读取 Nacos 连接环境变量或拼接 DataId。
主要模型和枚举
LogRequestContext:请求关联和公共上下文,包括TradeId、Service、Instance、Environment、Module、用户信息、客户端 IP、主机名、路由、方法、URL、Trace/Span、状态码、请求头和扩展属性。LogWriteRequest:继承请求上下文,增加Kind、Level、ContentFormat、字符串Content、业务侧Timestamp(Unix 毫秒)、结构化Exception、可选Destination、MessageTtl(分钟)和IsTerminal。LogEvent:生产端最终发送的事件,增加SchemaVersion、EventId、OccurredAt、ConfigRevision和可选EventSequence;Timestamp原样保留,不由生产端补值。LogDestination:交换机、队列和路由键;生产请求中可以为空字段表示继承默认目标,真正发送的LogEvent必须包含完整目标。LogExceptionInfo:异常类型、消息、堆栈和内部异常链。LogWriteResult:生产入口的本地接受结果,包括Accepted、TradeId、EventId和失败原因。
三个枚举分别是:
| 枚举 | 值 |
|---|---|
LogKind |
Request、Response、Error、Sql、Other |
LogSeverity |
Trace、Debug、Information、Warning、Error、Critical |
LogContentFormat |
Text、Json、Xml、Query、Sql |
业务生产端调用 ILogWriter.Write(LogWriteRequest),不手工创建 LogEvent。Logging 会补齐 schemaVersion=2、eventId、occurredAt、配置版本和解析后的 MQ 目标;eventId 在重试和消息重投时保持不变,tradeId 用于串联同一次请求的多条事件。Timestamp 必须由业务系统采集并原样传递,Request 表示开始、Response 或终结 Error 表示结束;Logging 和消费端都不能用 MQ 到达时间代替它。
Timestamp 使用 Unix 毫秒数,合法范围为 0 到 253402300799999,不携带时区。业务系统按照北京时间采集后直接传入即可;它用于计算接口耗时。生产端自动生成的 OccurredAt 是消息产生时间,序列化时带 +08:00 偏移,不能替代业务开始或结束时间。
JSON 规则和校验
LogJson.CreateOptions() 使用 camelCase 属性名、camelCase 字符串枚举、禁止整数枚举值、忽略 null 属性,并保留中文字符。LogJson.Deserialize 只接受根对象显式包含 schemaVersion: 2 的新消息,不兼容旧消息结构。
LogEventValidator.TryValidate 会检查:
schemaVersion必须为 2,eventId和tradeId必须非空、无控制字符且不超过 128 个字符;- 三个枚举必须是已定义值,
occurredAt不能为默认值,content不能为 null; timestamp如果提供,必须是0到253402300799999范围内的 Unix 毫秒时间戳;非法值会被拒绝,不会阻断业务宿主;Destination必须存在,交换机、队列和路由键必须非空、无控制字符且 UTF-8 长度不超过 255 字节;队列名不能含中英文逗号;MessageTtl如果提供,必须是非负整数;零表示消息立即过期,空值表示由生产端继承配置默认值。
契约版本仍为 schemaVersion=2。新增的 timestamp 和 messageTtl 都是可选字段:旧消息缺少它们时按空值读取,旧版消费者遇到未知字段时仍可按 JSON 规则忽略,不影响其他字段解析。DurationMs 不再属于生产端写入模型,业务应改为传入 Request/Response 的 Timestamp,由消费端计算 durationMs;历史 JSON 中若仍有 durationMs,反序列化时会按未知字段忽略。
下面的完整示例适合消费者或契约测试,展示序列化、反序列化和校验:
using YtSoft.Gmc.Core.Logging.Contracts;
var value = new LogEvent
{
SchemaVersion = 2,
EventId = "event-001",
TradeId = "trade-001",
OccurredAt = DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(8)),
Timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
ConfigRevision = 7,
Kind = LogKind.Response,
Level = LogSeverity.Information,
ContentFormat = LogContentFormat.Json,
Content = "{\"message\":\"处理成功\"}",
Destination = new LogDestination
{
ExchangeName = "gmc-logs",
QueueName = "gateway-logs",
RoutingKey = "gateway-logs"
}
};
if (!LogEventValidator.TryValidate(value, out var error))
{
throw new InvalidOperationException(error);
}
var body = LogJson.Serialize(value);
var restored = LogJson.Deserialize(body)
?? throw new InvalidOperationException("日志消息不是 schemaVersion=2 的有效 JSON。");
if (!LogEventValidator.TryValidate(restored, out var restoredError))
{
throw new InvalidOperationException(restoredError);
}
Console.WriteLine($"{restored.TradeId}/{restored.EventId}");
这个示例直接构造 LogEvent 只是为了说明纯契约的序列化边界;生产业务仍应调用 Logging 的 Write,不应自己生成事件标识或目标。
消费端约定
统一消费端和业务自有消费者都应先复制消息正文,再调用 LogJson.Deserialize 和 LogEventValidator.TryValidate。消费端以 (tradeId,eventId) 事件标识做幂等判断,以 tradeId 聚合同一次请求的 Request、Response、Error、Sql 和 Other 内容;同一个 tradeId 的事件可能乱序或分次到达。
只有在幂等判断完成且数据已经持久化后才能 ACK。RabbitMQ 的 ACK、重试、死信、MongoDB 聚合和 QueueNames 过滤由具体消费者实现,本契约包不创建客户端,也不提供 ACK 实现。业务自有队列可以继续使用同一消息契约;不在 Middleware.MqConnections.QueueNames 名单中的队列不会由统一 Mqlog 消费,应由业务系统自行订阅处理。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net6.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on YtSoft.Gmc.Core.Logging.Contracts:
| Package | Downloads |
|---|---|
|
YtSoft.Gmc.Core.Logging
统一日志生产 SDK。业务服务通过 ILogWriter 写入带 tradeId 的日志事件,经 RabbitMQ 投递到日志平台。 |
GitHub repositories
This package is not used by any popular GitHub repositories.