
构建工具开发工具CLI【免费下载链接】msbuildThe Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.项目地址https://gitcode.com/gh_mirrors/ms/msbuild点击查看免费下载MSBuildThe Microsoft Build Engine是 .NET 与 Visual Studio 的构建平台其日志系统允许开发者通过 API 或命令行接入内置与自定义 Logger在构建过程中接收丰富的跟踪信息即BuildEventArgs。本文以 documentation/wiki/Logging-Internals.md 为核心深入剖析 MSBuild 日志基础设施的内部设计从LoggingService的单一职责与生命周期到单节点、分布式两种场景下事件的完整投递链路再到同步/异步两种处理模式的取舍。读完本文你将理解中央 LoggerCentral Logger、转发 LoggerForwarding Logger与分布式 LoggerDistributed Logger各自的角色与协作方式能够基于源码定位日志丢失、性能瓶颈或自定义转发行为的关键节点。术语与角色定位在进入实现细节前先统一一批核心术语。理解这些角色是阅读后续链路的前提Entrypoint node入口节点为响应构建请求来自 CLI、API 或通过 API 来自 Visual Studio而创建的构建进程。Scheduler node调度节点负责构建编排的进程。当前实现中它与Entrypoint node是同一进程但设计上允许分离例如将来出现独立的精简构建客户端时。In-Proc node进程内节点运行在 Scheduler node 进程内部的构建执行单元。Out-Of-Proc node / OOP node进程外节点运行在独立进程中的构建执行单元。LoggerILogger或INodeLogger接口的实现者。注册后可接收构建过程产生的详细跟踪信息并进一步处理。最常见的用途是把信息输出到控制台或文件——Logger之名由此而来但它也可以服务于其他目的例如诊断调试或 BuildCheck 检查。Central Logger中央 Logger运行在中央节点即 Scheduler node上的 Logger接收整个构建的全部信息无论构建在单进程还是多进程中执行。Forwarding Logger转发 Logger在每个逻辑执行节点上各有一个实例能够在BuildEventArgs序列化并远程投递到 Scheduler node 之前对其过滤和/或改写跟踪流。主要内置实现为CentralForwardingLogger与ConfigurableForwardingLogger。Distributed Logger分布式 Logger一个 Central Logger 与一个 Forwarding Logger 的配对组合。LoggerDescription描述转发 Logger 的可序列化信息用于在 OOP node 中构造转发 Logger。它携带LoggerId又称SinkId保证远程数据能被正确路由到对应的中央 Logger另外两个关键数据点是Verbosity与LoggerSwitchParameters二者用于转发器在 OOP node 中被构造后正确初始化。其实现位于 src/Build/Logging/LoggerDescription.cs除LoggerId、LoggerSwitchParameters、Verbosity外还暴露Name由类名与程序集拼接与IsOptional等属性并实现了ITranslatable以便跨进程传输。EventSource跟踪事件的来源——既可以是活跃构建进程也可以是事后从存储源回放的流。Sink跟踪数据的消费者以事件形式对外暴露数据对消费它的 Logger 而言扮演着EventSource的角色。MSBuild 的多进程执行模式是这套日志架构存在的前提关于节点编排的详细讨论见 documentation/wiki/Nodes-Orchestration.md。下图展示了分布式 Logger 的配对结构LoggingService日志基础设施的骨架LoggingService是整个日志基础设施的骨干它既是构建过程日志的消费者又是向各个分布式/中央 Logger 分发事件的调度器在 OOP 节点内部它还承担跨进程传输通道的角色。实现见 src/Build/BackEnd/Components/Logging/LoggingService.cs。其实例分布遵循单例原则Scheduler node 中有单个实例每个进程外节点OOP node中各有单个实例ProjectCollection中有单个实例服务于独立的、由 API 驱动的 Evaluation。LoggingService还维护两个关键状态机LoggerMode同步Synchronous/ 异步Asynchronous定义于 LoggingService.cs与LoggingServiceStateInstantiated→Initialized→ShuttingDown→Shutdown。异步模式下默认的事件队列容量上限为DefaultQueueCapacity 200000见 LoggingService.cs队列满时生产者会阻塞等待。方法概览RegisterLogger(ILogger logger)以单个CentralForwardingLogger将 Logger 注册为分布式——无论该方法被调用多少次只会存在这一个共享转发器。由BuildManager在启动构建时Scheduler node 上调用。也被ProjectCollection用来注册 OM/API 操作的 Logger。此时ProjectCollection会用ReusableLogger包装注册的 Logger——ReusableLogger能把分离的设计时design-time与构建时build-time两次调用产生的构建事件合并到一个已注册 Logger 上实现见 src/Build/Logging/ReusableLogger.cs。源码层面LoggingService.cs第一个注册的中央 Logger 会触发创建CentralForwardingLogger及其对应的EventSourceSink并把该 sink 的 Id 记录在_centralForwardingLoggerSinkId中后续再注册的中央 Logger 直接附着到同一个共享EventSourceSink上。这就是所有中央 Logger 共享一个转发器的实现机制。RegisterDistributedLogger(ILogger centralLogger, LoggerDescription forwardingLogger)中央 Logger 运行在 Scheduler node转发 Logger 运行在进程外 worker node。为每个分布式 Logger 各创建一个EventSourceSink运行在 Scheduler node作为已注册中央 Logger 的IEventSource向它们泵入事件Scheduler node 上的LoggingService根据事件在 OOP node 中经过的源转发 Logger把构建事件多路复用分发到对应的EventSourceSink。为每个分布式 Logger 各创建一个EventRedirectorToSink运行在 OOP node为BuildEvent打上正确的LoggerId标记以便 Scheduler node 上的LoggingService能据此多路复用到正确的EventSourceSink进而到达正确的 Logger。维护一个递增的已注册分布式 Logger 计数器每个新 Logger 会被分配下一个 Id作为 sinkEventSourceSink与EventRedirectorToSink的标识该 Id 会写入传入的LoggerDescription从而可被远程投递到 OOP node 并初始化正确的转发。同时在LoggingService中创建一个单一的EventSourceSink供所有转发器作为事件源使用。InitializeNodeLoggers(ICollectionLoggerDescription descriptions, IBuildEventSink forwardingLoggerSink, int nodeId)由OutOfProcNode.HandleNodeConfiguration调用用于处理节点间通信传来的NodeConfiguration数据包。此时也是环境与各种构建级配置——例如在 EvaluationFinished 事件中包含已求值的属性——被接收并应用的时机同时也是 OOP node 中LoggingService被创建的时机。一个BuildEventArgTransportSink被传入LoggingService它与中央 Logger 的 Id 一起被封装在EventRedirectorToSink中附着到每个转发 Logger 上。BuildEventArgTransportSink负责把构建事件与目标 Logger 的 Id某些上下文中称为 sinkId打包再通过节点间通信发送出去。其实现见 src/Build/BackEnd/Components/Logging/BuildEventArgTransportSink.cs内部持有一个SendDataDelegate把new LogMessagePacket(new KeyValuePairint, BuildEventArgs(sinkId, buildEvent))交给传输层BuildEventArgTransportSink.cs。源码实现LoggingService.cs遍历每个LoggerDescription通过description.CreateForwardingLogger()反射创建转发器设置其Verbosity、Parameters、NodeId并将new EventRedirectorToSink(description.LoggerId, forwardingLoggerSink)挂到其BuildEventRedirector上后初始化。ProcessLoggingEvent(object buildEvent)构建事件的摄入点。既可以通过内部 API 记录事件来自LogBuildEvent或LogXXX系列例如LogBuildStarted、LogError等也可以从节点间远程传输经PacketReceived调用。保证隔离投递同步模式下加锁异步模式下入队并由单一线程从队列中取出投递。隔离投递的代价是单个慢 Logger 会阻塞所有其他 Logger事件被全部 Logger 消费的总时间等于各个 Logger 消费时间的总和。单节点构建中的事件投递在单节点进程内构建中事件投递完全在 Scheduler node 内部完成每次构建有 1 个LoggingService实例。它持有 1 个EventSourceSink向转发器泵入事件。可以有多个转发器一个由RegisterLogger注册的、被所有中央 Logger 共享的转发器以及每个经RegisterDistributedLogger注册的分布式 Logger 各自对应的转发器。转发器示例包括CentralForwardingLogger与ConfigurableForwardingLogger。每个转发器处理跟踪流并可以选择把事件转发给一个EventRedirectorToSink每个转发器对应 1 个重定向器。EventRedirectorToSink再把事件转发给EventSourceSink该实例在重定向器构造时传入每个EventRedirectorToSink对应 1 个EventSourceSink。EventSourceSink把事件分发给已注册的 Logger。同一个EventSourceSink上可以注册多个 Logger——这正是经RegisterLogger以中央 Logger方式注册时的情形。EventSourceSink的实现见 src/Build/BackEnd/Components/Logging/EventSourceSink.cs它实现了IEventSource5, IBuildEventSink暴露MessageRaised、ErrorRaised、WarningRaised、BuildStarted、BuildFinished、ProjectStarted、TargetStarted、TaskStarted、CustomEventRaised、StatusEventRaised、AnyEventRaised等事件Consume(BuildEventArgs)方法把事件按具体类型分发到对应事件处理器EventSourceSink.cs。CentralForwardingLoggersrc/Build/BackEnd/Components/Logging/CentralForwardingLogger.cs在Initialize(IEventSource)时订阅AnyEventRaised由于遥测事件不属于所有事件它还会在IEventSource2上订阅TelemetryLogged、在IEventSource5上订阅WorkerNodeTelemetryLogged统一转发CentralForwardingLogger.cs。上述流程图是简化版未刻画组件内部调用当前实际调用序列可能随版本变化如下面的堆栈快照所示分布式构建中的事件投递分布式执行时NodeManager会请求启动额外的进程外OOP节点。这些节点产生的日志事件被传输到 Scheduler node最终由中央 Logger 消费。除 OOP 节点外构建还可以有一个进程内节点In-Proc Node——与 Scheduler node 共享同一进程的执行节点。因此前面描述的单节点事件投递在分布式构建中同样完全适用以下内容是对它的补充。OOP 节点侧OOP 节点中日志工作流的起始部分与进程内投递完全相同每次构建有 1 个LoggingService实例。它持有 1 个EventSourceSink向转发器泵入事件。可以有多个转发器一个共享的CentralForwardingLogger以及每个分布式 Logger 各自的ConfigurableForwardingLogger等。每个转发器处理跟踪流并选择性地把事件转发给EventRedirectorToSink。不同之处在于从EventRedirectorToSink之后的处理。由于通过InitializeNodeLoggers注入了一个唯一的BuildEventArgTransportSink作为EventRedirectorToSink数据的消费者每次构建有单个BuildEventArgTransportSink消费所有EventRedirectorToSink的数据数据会被打上 SinkId即 LoggerId——该 Id 在 Logger 注册时创建并随LoggerDescription一起远程投递到 OOP 节点。BuildEventArgTransportSink把跟踪数据BuildEventArgs实例与 SinkId 打包交给管道排队等待远程投递到 Scheduler node。EventRedirectorToSink的实现非常简洁src/Build/BackEnd/Components/Logging/EventRedirectorToSink.cs其ForwardEvent(BuildEventArgs)只是调用_sink.Consume(buildEvent, _centralLoggerId)——中心 Logger 的 Id 在构造时传入EventRedirectorToSink.cs。调用序列的示意堆栈如下Scheduler 节点侧LoggingService接收远程投递来的数据——即捆绑了 SinkId 的BuildEventArgs实例。依据 SinkIdLoggingService把收到的数据路由到已注册的EventSourceSink每个已注册转发器对应一个。EventSourceSink把事件分发给订阅的 Logger对分布式 Logger 注册而言最多 1 个订阅 Logger对中央 Logger 注册而言则可能有多个。路由的核心实现在RouteBuildEvent(KeyValuePairint, BuildEventArgs nodeEvent)LoggingService.cs通过_eventSinkDictionary.TryGetValue(nodeEvent.Key, out sink)找到目标 sink 并调用sink.Consume(...)。而进程内路径RouteBuildEvent(BuildEventArgs)LoggingService.cs则先把事件交给_filterEventSource过滤后广播再对BuildStartedEventArgs/BuildFinishedEventArgs做一次兜底分发——这是为了向后兼容 Orcas 时代转发 Logger 不得转发 BuildStarted/BuildFinished的行为保证那些不能转发首尾事件的旧 Logger 也能收到它们。对应的调用堆栈示意同步与异步事件处理正如ProcessLoggingEvent所述LoggingService可以同步或异步两种模式投递日志事件。日志模式由注入LoggingService的LoggerMode决定LoggingService.csOOP 节点中模式永远是异步的。这是合理的设计取舍worker 节点承担实际构建工作异步化日志投递可以避免日志 I/O 拖慢构建本身。Scheduler 节点默认同步除非环境变量MSBUILDLOGASYNC被设为1。CLI 侧对应代码见 src/MSBuild/XMake.cs默认设置parameters.UseSynchronousLogging true以保持与旧版本控制台输出行为一致同步稍慢仅当环境变量为1时才不启用同步。ProjectCollection的模式取决于构造参数useAsynchronousLogging在未显式传入该参数的签名中默认同步。见 src/Build/Definition/ProjectCollection.cs_loggerMode useAsynchronousLogging ? LoggerMode.Asynchronous : LoggerMode.Synchronous。异步模式的实现细节LoggingService.cs事件进入ConcurrentQueueobject队列通过AutoResetEvent控制入队/出队节流队列达到_queueCapacity默认 200000时生产者阻塞等待。WaitForLoggingToProcessEvents()LoggingService.cs用于在 BuildFinished 事件抛出前或组件关闭前等待队列中所有事件被完整处理。无论采用哪种模式顺序且隔离的投递始终被保证单个 Logger 不会在返回上一个事件之前收到下一个事件任何事件被某个 Logger 处理期间其他 Logger 不会收到该事件。未来的版本可能会改为向不同 Logger 独立投递届时单个 Logger 的处理不会阻塞其他 Logger。此外LoggingService还支持OnlyLogCriticalEvents过滤LoggingService.cs启用时只保留CustomBuildEventArgs、BuildErrorEventArgs、BuildWarningEventArgs与CriticalBuildMessageEventArgs等关键事件用于高吞吐或低噪音场景。值得关注的日志记录器MSBuild 内置了若干重要 Logger本文不展开其内部实现仅列出入口供继续深入Binary logger二进制日志记录器详见 documentation/wiki/Binary-Log.md用于生成.binlog结构化构建日志。Terminal logger终端日志记录器详见 documentation/terminallogger/面向交互式终端的现代化输出。Console logger控制台日志记录器其参数定义位于字符串资源 src/MSBuild/Resources/Strings.resx 中。File logger文件日志记录器参数同样定义于 src/MSBuild/Resources/Strings.resx。对希望自定义转发行为的开发者ConfigurableForwardingLoggersrc/Build/Logging/DistributedLoggers/ConfigurableForwardingLogger.cs是公开的IForwardingLogger实现可作为编写自定义转发 Logger 的参照。测试覆盖仓库为日志基础设施提供了较完整的单元测试可作为理解行为契约的补充材料src/Build.UnitTests/BackEnd/LoggingService_Tests.cs覆盖RegisterLogger、RegisterDistributedLogger、InitializeNodeLoggers的注册与路由行为。src/Build.UnitTests/BackEnd/EventSourceSink_Tests.cs、EventRedirectorToSink_Tests.cs、BuildEventArgTransportSink_Tests.cs分别验证事件分发、重定向与跨进程打包环节。src/Build.UnitTests/BackEnd/CentralForwardingLogger_Tests.cs验证转发器的事件转发行为。src/Build.UnitTests/BackEnd/LoggingServiceFactory_Tests.cs验证工厂按指定LoggerMode与节点 Id 创建实例。小结MSBuild 的日志体系以LoggingService为中枢通过EventSourceSink、EventRedirectorToSink、BuildEventArgTransportSink三个核心构件在单节点与多进程分布式两种执行形态下保持统一的转发 → 打标 → 路由 → 分发语义。理解这条链路不仅能解释为什么 OOP 节点日志默认异步、为什么慢 Logger 会拖慢整次构建等常见现象也能为排查日志事件缺失、设计高性能自定义 Logger 提供明确的源码级依据。赞分享构建工具开发工具CLI【免费下载链接】msbuildThe Microsoft Build Engine (MSBuild) is the build platform for .NET and Visual Studio.项目地址https://gitcode.com/gh_mirrors/ms/msbuild点击查看免费下载相关推荐NLog源码深度解析揭秘.NET日志框架的内部架构与核心机制NLog源码深度解析揭秘.NET日志框架的内部架构与核心机制 NLog作为一款先进的.NET结构化日志记录框架为各种.NET平台提供了强大而灵活的日志记录能后端运维观测Linux 内核通用通知机制watch_queue深度解析基于管道的内核事件订阅与投递Linux 内核通用通知机制watch_queue深度解析基于管道的内核事件订阅与投递 通用通知机制general notification mecha操作系统内核驱动驱动开发虚拟化嵌入式网络存储事件驱动架构实战Watermill消息投递语义深度解析事件驱动架构实战Watermill消息投递语义深度解析 在现代分布式系统中消息投递语义直接决定了系统的可靠性和数据一致性。无论你是构建电商订单系统、实时数据消息队列后端微服务创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考