• 如何在C#中实现自定义的 msbuild logger
  • 发布于 1周前
  • 49 热度
    0 评论
前言
最近想改一下项目,发现有很多警告,想把所有的 warning 导出到 JSON 以方便统计 warning 类型以及出现的次数,但是目前 dotnet build 还不支持,于是想提一个 issue 希望支持一下,然后有大佬说可以自定义一个 msbuild logger 来实现,.NET SDK 里的 Terminal Logger 也是这种方式实现的,于是就找了下文档动手尝试了一下,分享一下,希望对大家有所帮助。

进入正题
1.首先我们来创建一个新的类库项目,并添加 Microsoft.Build.Framework nuget 包的引用
2.然后就可以创建一个 logger,实现:Microsoft.Build.Framework.ILogger 接口即可,代码实现如下:
public sealed class JsonErrorLogger : ILogger
{
    private const string ErrorLogFileName = "json-error-logger.json";
    private static readonly JsonSerializerOptions JsonSerializerOptions = new()
    {
        // 堆代码 duidaima.com
        WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };
    
    private readonly List<BuildError> _errors = new(), _warnings = new();
    public void Initialize(IEventSource eventSource)
    {
        eventSource.WarningRaised += EventSourceOnWarningRaised;
        eventSource.ErrorRaised += EventSourceOnErrorRaised;
    }

    private void EventSourceOnWarningRaised(object sender, BuildWarningEventArgs e)
    {
        _warnings.Add(new BuildError(e.Subcategory, e.Code, e.File, e.LineNumber, e.ColumnNumber, e.EndLineNumber, e.EndColumnNumber, e.Message, e.HelpKeyword, e.SenderName));
    }

    private void EventSourceOnErrorRaised(object sender, BuildErrorEventArgs e)
    {
        _errors.Add(new BuildError(e.Subcategory, e.Code, e.File, e.LineNumber, e.ColumnNumber, e.EndLineNumber, e.EndColumnNumber, e.Message, e.HelpKeyword, e.SenderName));
    }

    public void Shutdown()
    {
        using var fs = File.Create(ErrorLogFileName);
        JsonSerializer.Serialize(fs, new
        {
            warnings = _warnings,
            errors = _errors
        }, JsonSerializerOptions);
        
        _errors.Clear();
    }

    public LoggerVerbosity Verbosity { get; set; }
    public string? Parameters { get; set; }
}


public sealed record BuildError(
    string Subcategory,
    string Code,
    string File,
    int LineNumber,
    int ColumnNumber,
    int EndLineNumber,
    int EndColumnNumber,
    string? Message,
    string? HelpKeyword,
    string? SenderName);
这里实现的逻辑比较简单,在 Initialize 的时候注册 warning 和 error 事件,将 warning 和 error 信息记录下来,并在 Shutdown 的时候将其导出到 json 文件中,代码搞好之后编译我们的项目,确保成功生成 dll 文件。然后在原来 dotnet build 的基础上添加 -logger 参数使用我们自定义的这个 logger,示例如下:
dotnet build -logger:"JsonErrorLogger,C:\projects\source\SamplesInPractice\MSBuildLoggerSample\MSBuildJsonLogger\bin\Debug\net8.0\MSBuildJsonLogger.dll"
此时就会生成一个类似下面的 json 文件

这样我们就可以知道哪里出错了,发生了什么错误,也可以更加方便地根据 error code 来统计,我们也可以在自定义 logger 的实现里,在导出之前进行统计,生成统计信息等。

更多
前面的这个 logger 比较简单,目前 path 是写死的,也只关心 warning 和 error log,msbuild 提供了一个 ILogger 的实现 Logger, 也可以继承这个 Logger 来实现,实际要复用的话可以结合 msbuild 的一些参数和自己的需求进行改进和定制,除了自定义 msbuild logger 之外 Roslyn 在编译的时候也可以生成一个编译 error 的文件,可以导出成 json,可以在项目文件里配置。
<ErrorLog>logVersion21.json,version=2.1</ErrorLog>
不过这种方式我试下来应该只有 Roslyn 代码编译的 error log,一些补充的自定义的 task report 的 error 不会出现,比如像 NuGet Audit 的 Error 是没有的,所以想要获取完整的 error 建议还是要通过自定义 msbuild logger 的方式。


总结

本文分享了一个 msbuild logger 的 demo 来导出 msbuild warning/error 为 json 文件,感谢热心帮忙的大佬们,希望对大家有所帮助

参考
https://github.com/dotnet/sdk/issues/43269?wt.mc_id=DT-MVP-5004222
https://learn.microsoft.com/en-us/visualstudio/msbuild/build-loggers?view=vs-2022&wt.mc_id=DT-MVP-5004222
https://github.com/WeihanLi/SamplesInPractice/blob/main/MSBuildLoggerSample/MSBuildJsonLogger/JsonLogger.cs?wt.mc_id=DT-MVP-5004222
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-options/errors-warnings#errorlog?wt.mc_id=DT-MVP-5004222
用户评论