如何在跨汇编环境中最好地部署哨兵?

How to best deploy Sentry in cross-assembly environment?

所以我们构建了这个 library/framework 东西,里面充满了与业务流程相关的代码和跨多个应用程序共享的公共元素(C#、.Net 4.7.1、WPF、MVVM)。我们的日志记录都是通过这个框架设置的,所以自然而然地感觉它是 Sentry 的最佳位置。我们各个应用程序中的所有引用都手动指向 dll,即我们的共享库自行安装的文件夹。到目前为止一切顺利。

当我们最初设置 Sentry 时,一切似乎都运行良好。我们做了一些更新,错误似乎正在减少。那是因为我们很棒,而 Sentry 帮助我们变得更棒,对吧?没有!好吧,我的意思是有点。

正在处理范围,因此我们不会再收到未处理的异常。一开始我们没有注意到,因为当我们通过 Logging.Log() 方法处理错误时,我们仍然会收到哨兵日志。此日志记录方法调用 SentrySdk.Init() 我怀疑它是在执行程序集中配置客户端。

我们还开始使用 Sentry 进行一些简单的使用情况跟踪,方法是在 Sentry 中启动一个名为 Usage-Tracker 的单独项目,并将带有 ApplicationName.UsageTracker 枚举的简单 "DoThingApplication has been launched" 作为参数传递给我们的日志记录方法。

问题: 什么是处理这个问题的好方法,我的设置可以有一个哨兵实例来包装我的 using(sentryClientStuff){ ComposeObjects(); } 并且仍然让我的日志记录方法寻找现有客户并使用它(如果存在)?

注意事项:

一些相关的想法


我真的想要一个跨汇编单例的实践。

这里真的发生了很多事情。此外,如果不查看任何代码,就很难想象事物是如何布局的。如果您分享项目结构的一些(甚至是虚拟的)示例,则更有可能获得您正在寻找的答案。

我会尝试分解它并解决我能解决的问题:

关于:

使用跟踪器: 您可以创建一个新的客户端并绑定到一个范围。这样,任何对 SentrySdk static class 的使用(我假设你的 Logger.Log 路由到)都会被接受。 换句话说,像您目前所做的那样调用 SentrySdk.Init,使用共享库在任何应用程序之间共享的选项,然后使用 Usage 的 DSN 创建一个客户端-哨兵中的Tracker 项目。推送一个范围,绑定客户端,你可以使用SentrySdk

有一个 example in the GitHub repo of the SDK:

using (SentrySdk.PushScope())
{
    SentrySdk.AddBreadcrumb(request.Path, "request-path");

    // Change the SentryClient in case the request is to the admin part:
    if (request.Path.StartsWith("/admin"))
    {
        // Within this scope, the _adminClient will be used instead of whatever
        // client was defined before this point:
        SentrySdk.BindClient(_adminClient);
    }

    SentrySdk.CaptureException(new Exception("Error at the admin section"));
    // Else it uses the default client

    _middleware?.Invoke(request);

} // Scope is disposed.

SDK 只需初始化一次,但您始终可以使用 new SentryClient 创建新客户端,推送新范围 (SentrySdk.PushScope()) 并将其绑定到新范围 (SentrySdk.BindClient).一旦弹出作用域,就无法再通过 SentrySdk.CaptureException 或静态 class SentrySdk.

上的任何其他方法访问客户端

您也可以直接使用客户端,根本不将其绑定到作用域。

using (var c = new SentryClient(new SentryOptions { Dsn = new Dsn("...") })) {
    c.CaptureMessage("hello world!");
}

using 块用于确保后台线程刷新事件。

初始化SDK的中心位置: 在共享 framework/library 中会有您想要修复的配置,但肯定每个应用程序(组合根)都会有自己的设置。发布是自动发现的。 来自 docs.sentry.io:

The SDK will firstly look at the entry assembly’s AssemblyInformationalVersionAttribute, which accepts a string as value and is often used to set a GIT commit hash. If that returns null, it’ll look at the default AssemblyVersionAttribute which accepts the numeric version number.

如果您在构建服务器中修补程序集,应该会自动报告正确的 Release。如果没有,您可以通过将 SentryOptions 作为参数传递的委托来为每个应用程序定义它。

类似于:

Framework code:


public class MyLogging 
{
    void Init(Action<SentryOptions> configuration)
    {
        var o = new SentryOptions();
        // Add things that should run for all users of this library:
        o.AddInAppExclude("SomePrefixTrueForAllApplications");
        o.AddEventProcessor(new GeneralEventProessor());
        // Give the application a chance to reconfigure anything it needs:
        configuration?.Invoke(o);
    }
}

App code:

void Main() 
{
    MyLogging.Init(o => o.Environment = "my env");
}

正在处理范围,因此我们不会再收到未处理的异常。"

不确定我明白这里发生了什么。推送和弹出(处置)作用域不会影响 SDK 捕获未处理异常的能力。能否请您分享一份复制品?

此日志记录方法调用 SentrySdk.Init(),我怀疑它正在执行程序集中配置客户端。: 除非您使用 new SentryClient 创建客户端 "by hand",否则 运行 进程 中只有 1 个客户端。请注意我说的是 运行 过程而不是组装。实例不保存在程序集中。程序集仅包含可以执行的代码。如果你调用 SentrySdk.CaptureException ,它会将调用分派给绑定到当前作用域的 SentryClient 。如果您没有 PushScope,总会有一个隐式作用域,即根作用域。在这种情况下,一切都足够透明,你不应该关心那里有一个范围。您也无法处理该范围,因为您从来没有这样做的句柄(您没有调用 PushScope 所以您没有得到 returns 调用 Dispose 的内容).

我们个人应用程序中的所有引用都手动指向 dlls,即我们的共享库东西自行安装的文件夹。:

根据您的环境,需要考虑的一件事是 distribute packages via NuGet. I'm unsure whether you expect to use these libraries in non .NET Framework applications (like .NET Core). But considering .NET Core 3.0 is bringing Windows Desktop framework support like WPF and WinForm, it's possible that eventually you will. If that's the case, consider targeting .NET Standard instead of .NET Framework for your code libraries