SDK Fingerprinting
在受支持的 SDK
中,可以覆盖 Sentry
的默认分组,该分组将 fingerprint
属性作为字符串数组传递。fingerprint
数组的长度不受限制。这类似于 fingerprint rules functionality,它总是可用的,并可以实现类似的结果。
Basic Example
在最基本的情况下,值直接传递:
function makeRequest(method, path, options) { return fetch(method, path, options).catch(function(err) { Sentry.withScope(function(scope) { // group errors together based on their request and response scope.setFingerprint([method, path, String(err.statusCode)]); Sentry.captureException(err); }); }); }
您可以使用变量替换将动态值填充到通常在服务器上计算出的指纹中。例如,可以添加值 {{default}}
,以将整个正常生成的分组哈希添加到指纹中。这些值与服务器端指纹识别相同。有关更多信息,请参见 Variables。
Group Errors With Greater Granularity
您的应用程序查询远程过程调用模型(Remote Procedure Call Model, RPC
)接口或外部应用程序编程接口(external application Programming interface, API
)服务,因此堆栈跟踪通常是相同的(即使传出请求非常不同)。
以下示例将进一步分解 Sentry 将创建的默认组(用 {{default}}
表示),并考虑错误对象的一些属性:
class MyRPCError extends Error { constructor(message, functionName, errorCode) { super(message); // The name of the RPC function that was called (e.g. "getAllBlogArticles") this.functionName = functionName; // For example a HTTP status code returned by the server. this.errorCode = errorCode; } } Sentry.init({ ..., beforeSend: function(event, hint) { const exception = hint.originalException; if (exception instanceof MyRPCError) { event.fingerprint = [ '{{ default }}', String(exception.functionName), String(exception.errorCode) ]; } return event; } });
Group Errors More Aggressively
通用错误(例如数据库连接错误)具有许多不同的堆栈跟踪,并且从不在一起。
以下示例将通过从数组中省略 {{ default }}
来完全覆盖 Sentry 的分组:
class DatabaseConnectionError extends Error {} Sentry.init({ ..., beforeSend: function(event, hint) { const exception = hint.originalException; if (exception instanceof DatabaseConnectionError) { event.fingerprint = ['database-connection-error']; } return event; } });
Fingerprint Rules
Fingerprint 规则(以前称为服务器端指纹)也配置了类似于 stack trace rules 的配置,但语法略有不同。匹配器(matchers)是相同的,但不是翻转标志(flipping flags),而是分配一个指纹,它完全覆盖默认分组。
这些规则可以在 Settings > Projects > [project] > Issue Grouping > Fingerprint Rules 中按项目设置。Group Settings 有输入字段,您可以在其中写入自定义指纹规则。该语法遵循 Discover queries 中的语法。如果要否定匹配,可以在表达式前面加上感叹号(!
)。
所有值都匹配,并且在堆栈跟踪的情况下,将考虑所有帧。如果所有匹配项都匹配,则应用指纹。
Fingerprinting Config
# You can use comments to explain the rules. Rules themselves follow the # following syntax: matcher:expression -> list of values # The list of values can be hardcoded or substituted values.
下面是一个实际的示例,它将特定类型的异常组合在一起:
error.type:DatabaseUnavailable -> system-down error.type:ConnectionError -> system-down error.value:"connection error: *" -> connection-error, {{ transaction }}
Matchers
匹配器通常使用通配符语法。可以使用以下匹配器:
error.type
alias: type
- 与异常类型(异常名称)匹配。匹配以区分大小写的形式进行。
error.type:ZeroDivisionError -> zero-division error.type:ConnectionError -> connection-error
error.value
alias: value
- 与异常值匹配。错误或异常通常具有易于理解的描述(值)。该匹配器允许不区分大小写的匹配。
error.value:"connection error (code: *)" -> connection-error error.value:"could not connect (*)" -> connection-error
message
- 匹配日志消息。它还会自动检查其他异常值,因为它们很难分开。匹配不区分大小写。
message:"system encountered a fatal problem: *" -> fatal-log
logger
- 匹配 logger 的名称,这对于将 logger 的所有消息组合在一起非常有用。此匹配区分大小写。
logger:"com.myapp.mypackage.*" -> mypackage-logger
level
- 在日志级别匹配。匹配不区分大小写。
logger:"com.myapp.FooLogger" level:"error" -> mylogger-error
tags.tag_name
- 与标签 tag_name 的值匹配。这对于过滤某些类型的事件很有用。例如,您可以分离由特定服务器引起的事件:
tags.server_name:"canary-*.mycompany.internal" -> canary-events
stack.abs_path
alias: path
- 在事件的路径上匹配,并且不区分大小写。它使用路径遍历语义,这意味着
*
不匹配斜杠,而**
匹配。请注意,此匹配器在abs_path
和filename
上都匹配,因为 SDK 关于如何支持这些值可能会非常不一致。如果 glob 匹配这些值中的任何一个,则视为匹配。
stack.abs_path:"**/my-utils/*.js" -> my-utils, {{ error.type }}
stack.module
alias: module
- 与
stack.abs_path
类似,但是匹配模块名称。匹配区分大小写,并且会应用常规的规则(*
也匹配斜杠)。
stack.module:"*/my-utils/*" -> my-utils, {{ error.type }}
stack.function
alias: function
- 检查堆栈跟踪中的任何函数是否与全局匹配。匹配区分大小写:
stack.function:"my_assertion_failed" -> my-assertion-failed
stack.package
alias: package
- 与 frame 的 "package" 匹配。这通常是包含 frame 的 debug symbol/object file 的名称。如果有任何 frame 与该目标文件匹配,那么它将匹配。
stack.package:"**/libcurl.dylib" -> libcurl stack.package:"**/libcurl.so" -> libcurl
family
- 用于 "scope" 匹配器。存在以下族:用于任何类型的 JavaScript 事件的
javascript
,用于任何类型的 Native 事件的native
。任何其他平台称为other
。
family:native !stack.module:"myproject::*" -> not-from-my-project
app
- 检查 frame 是否在应用程序内。与其他匹配器结合使用时特别有用。可能的值为
yes
和no
:
app:yes stack.function:"assert" -> assert
Combining Matchers
当多个匹配器组合在一起时,它们都需要匹配。在 frame 上运行的匹配器必须全部应用于同一 frame;否则,它们不被视为匹配项。
例如,如果在函数名称和模块名称上都匹配,则仅当 frame 同时在函数名称和模块名称上匹配时,才存在匹配项。一个frame 仅与函数名称匹配是不够的,即使另一个 frame 本身会与模块名称匹配也是如此。
# this matches if a frame exists with a specific function and module name # and also a specific error type is thrown error.type:ConnectionError stack.function:"connect" stack.module:"bot" -> bot-error
Variables
在 fingerprint 的右边,你可以使用常数值和变量。变量会被自动替换,并具有与匹配器相同的名称,但它们的填充方式可能不同。
变量用双花括号括起来({{variable_name}}
)。
{{ default }}
- 这将填充正常分组操作将产生的默认 fingerprint。如果您想用其他方法细分一个已经存在的组,这将非常有用:
stack.function:"query_database" -> {{ default }}, {{ transaction }}
{{ transaction }}
- 这会将 transaction 名称填写到 transaction 中。它将强制为每个 transaction 创建一个组:
error.type:"ApiError" -> api-error, {{ transaction }}
{{ error.type }}
alias: {{ type }}
- 这将填写发生的错误的名称。使用 ·chained exceptions· 时,它将是最近抛出的错误。这将强制每个 ·transaction· 创建一个组:
stack.function:"evaluate_script" -> script-evaluation, {{ error.type }}
{{ stack.function }}
alias: {{ function }}
- 这将填充 "crashing frame," 的功能名称,也称为应用程序代码的最上层 frame。
error.type:"ScriptError" -> script-evaluation, {{ stack.function }}
{{ stack.module }}
alias: {{ module }}
- 这将填写"crashing frame,"的模块名称,也称为应用程序代码的最上层 frame。
error.type:"ScriptError" -> script-evaluation, {{ stack.module }}
{{ stack.package }}
alias: {{ package }}
- 这将填写 "crashing frame," 的 package 名称(object file),也称为应用程序代码的最上层 frame。
stack.function:"assert" -> assertion, {{ stack.package }}
{{ logger }}
- 这将填写引起事件的 logger 的名称。
message:"critical connection error*" -> connection-error, {{ logger }}
{{ level }}
- 这将填充用于创建事件的日志级别的名称。
message:"connection error*" -> connection-error, {{ logger }}, {{ level }}
{{ tags.tag_name }}
- 这会将 tag 的值填充到 fingerprint 中,例如,可以使用 fingerprint 按 server name 或类似名称拆分事件。
message:"connection error*" -> connection-error, {{ tags.server_name }}
Stack Trace Rules
如果使用堆栈跟踪进行分组,则堆栈跟踪规则(以前称为分组增强)会影响输入该算法的数据。可以在 Settings > Projects > [project] > Issue Grouping > Stack Trace Rules 下,针对每个项目配置这些规则。
每行都是一条规则;当所有表达式匹配时,一个或多个匹配表达式后跟一个或多个要执行的动作。所有规则在堆栈跟踪中的所有帧上从上到下执行。
堆栈跟踪规则的语法类似于:
matcher-name:expression other-matcher:expression ... action1 action2 ...
该语法遵循 Discover queries 中的语法。如果要否定匹配,可以在表达式前面加上感叹号(!)。
下面是一个实际的例子:
# mark all code in node modules not to be in app stack.abs_path:**/node_modules/** -app # remove all generated javascript code from all grouping stack.abs_path:**/generated/**.js -group
Matchers
可以在一行中定义多个匹配器。下面的匹配器是可用的:
family
- 匹配通用 platform family,目前包括 javascript, native 和 other。逗号分隔规则将它们应用于多个平台。
family:javascript stack.abs_path:**/generated/** -group
stack.abs_path
alias: path
- 该匹配器对堆栈跟踪中路径上的 Unix glob 行为不区分大小写。路径分隔符被标准化为 /。作为特殊规则,如果文件名是相对的,则它仍在 **/ 上匹配。
# match on all files under `project` with a `.c` extension stack.abs_path:**/project/**.c` +app # matches on vendor/foo without sub folders stack.abs_path:**/vendor/foo/*.c` -app # matches on `foo.c` as well as `foo/bar.c`. stack.abs_path:**/*.gen.c` -group
stack.module
alias: module
- Module 与 path 类似,但与 module 匹配。它不用于 Native,但用于 JavaScript、Python 和类似平台。匹配是区分大小写的,常规通配符是可用的。请注意,modules 不是 packages,这可能会让 Native 环境感到困惑。
stack.function
alias: function
- 匹配堆栈跟踪中的函数,并且使用常规通配符区分大小写。
stack.function:myproject_* +app stack.function:malloc -group
stack.package
alias: package
- 在堆栈跟踪中匹配 package。
package
是包含function
或module
的容器。这是一个 .jar ,一个 .dylib 或类似的。匹配规则与 path 相同。例如,这通常是一个绝对路径。
stack.package:**/libcurl.dylib -group
app
- 与
stack trace frame
的应用内标记的当前状态匹配。yes
表示该 frame 是应用程序内 frame,no
表示不是。
Actions
有两种类型的 actions
:标记(flag
)和变量(variables
)设置。
flag
标识在所有匹配器都匹配并使用以下前缀时采取的动作:
+
设置flag
-
取消设置flag
^
适用于匹配帧之上的帧(走向崩溃)。v
适用于匹配帧下面的帧(远离崩溃)。
例如,-group ^-group
从组中移除匹配帧和它上面的所有帧。
app
:在应用程序内标记或取消标记 framegroup
:从分组中添加或删除 frame
variables:可以设置变量(variable=value
)。当前只有一种:
max-frames
:设置要分组的总帧数。默认值为 0,表示“所有帧”。如果设置为 3,则仅考虑前三个帧。
如果一行以 hash(#) 作为前缀,则它是一个注释并被忽略。
stack.abs_path:**/node_modules/** -group stack.abs_path:**/app/utils/requestError.jsx -group stack.abs_path:**src/getsentry/src/getsentry/** +app family:native max-frames=3 stack.function:fetchSavedSearches v-group stack.abs_path:**/app/views/**.jsx stack.function:fetchData ^-group family:native stack.function:SpawnThread v-app -app family:native stack.function:_NSRaiseError ^-group -app family:native stack.function:std::* -app family:native stack.function:core::* -app
Recommendations
这些建议将大大改善您的即用型分组体验。
Mark in-app Frames
为了主动改善您的体验,请帮助 Sentry 确定堆栈跟踪中的哪些帧是“应用程序内”(属于您自己的应用程序),哪些不是。SDK 定义了默认规则,但是在许多情况下,也可以在服务器上进行改进。特别是对于需要服务器端处理的语言(例如,Native C,C++ 或 JavaScript),最好在服务器上覆盖它。
stack.function:myapplication::* +app
你也可以通过标记其他帧 “not in-app” 来达到同样的效果。然而,如果是这种情况,你应该确保首先将所有帧设置为 “in-app”,以覆盖默认值:
app:no +app stack.function:std::* -app stack.function:boost::* -app
你需要强制所有帧首先在应用程序内,因为客户端 SDK 或早期处理可能已经设置了一些默认值。
Cut Stack Traces
在许多情况下,您要删除堆栈跟踪的顶部或底部。例如,许多代码库使用通用函数来生成错误。在这种情况下,错误机制将显示为堆栈跟踪的一部分。
例如,如果你使用 Rust,你可能想要删除一些与 panic 处理相关的 frames:
stack.function:std::panicking::begin_panic ^-app -app ^-group stack.function:core::panicking::begin_panic ^-app -app ^-group
在这里,我们告诉系统从 begin-panic
到崩溃位置的所有 frame 都不是应用程序的一部分(包括 panic frame 本身)。在所有情况下,以上所有 frame 均与分组无关。
同样,您也可以删除堆栈跟踪的 base。如果您有不同的主循环来驱动应用程序,则此功能特别有用:
例如,如果你使用 Rust
,你可能想要删除一些与 panic
处理相关的 frames
:
stack.function:std::panicking::begin_panic ^-app -app ^-group stack.function:core::panicking::begin_panic ^-app -app ^-group
这并不适用于所有的项目,但它可以很好地工作于许多崩溃的大型应用程序。默认的策略是考虑与分组相关的大多数堆栈跟踪。这意味着导致崩溃的每个不同的堆栈跟踪都将导致创建不同的组。如果你不想这样,你可以通过限制应该考虑的帧数来强制设置更大的组。
例如,如果堆栈跟踪中的任何帧都指向一个公共的外部库,你可以告诉系统只考虑 top N
帧:
# always only consider the top 1 frame for all native events family:native max-frames=1 # if the bug is in proprietarymodule.so, only consider top 2 frames family:native stack.package:**/proprietarymodule.so max-frames=2 # these are functions we want to consider much more of the stack trace for family:native stack.function:KnownBadFunction1 max-frames=5 family:native stack.function:KnownBadFunction2 max-frames=5