本文档详细说明 Sentry 管理后台中各种 Trace 数据字段的监控机制、触发时机和实现原理。
Sentry Java/Android SDK 自动生成多种类型的 Trace 操作,每种操作都有特定的监控目的和触发时机:
graph TD
A[应用程序] --> B[UI 操作]
A --> C[网络操作]
A --> D[数据库操作]
A --> E[文件操作]
A --> F[业务操作]
B --> B1[ui.load Transaction]
B --> B2[ui.load.initial_display]
B --> B3[ui.load.full_display]
B --> B4[app.start.cold/warm]
B --> B5[activity.load]
C --> C1[http.client]
C --> C2[http.server]
C --> C3[graphql.operation]
C --> C4[graphql.fetcher]
D --> D1[db.query]
D --> D2[db.sql.query]
E --> E1[file.read]
E --> E2[file.write]
F --> F1[bean]
F --> F2[custom operations]
操作类型: ui.load
监控对象: Activity 完整生命周期
触发时机: Activity 创建时开始,Activity 销毁或超时时结束
// 源码位置: ActivityLifecycleIntegration.java
static final String UI_LOAD_OP = "ui.load";
// 创建时机
ITransaction transaction = scopes.startTransaction(
new TransactionContext(
activityName, // 事务名称 (Activity 类名)
TransactionNameSource.COMPONENT,
UI_LOAD_OP, // 操作类型
appStartSamplingDecision),
transactionOptions);监控内容:
- Activity 完整加载过程
- 包含所有子 Span (TTID、TTFD、App Start 等)
- 帧率性能指标
- 内存使用情况
操作类型: ui.load.initial_display
监控对象: Time To Initial Display - 首次显示时间
触发时机: Activity 开始创建 → 首帧绘制完成
// 源码位置: ActivityLifecycleIntegration.java
static final String TTID_OP = "ui.load.initial_display";
// 创建时机
final @NotNull ISpan ttidSpan = transaction.startChild(
TTID_OP,
getTtidDesc(activityName), // "ActivityName initial display"
ttidStartTime, // 开始时间
Instrumenter.SENTRY,
spanOptions);
// 结束时机: onFirstFrameDrawn()
private void onFirstFrameDrawn(final @Nullable ISpan ttfdSpan, final @Nullable ISpan ttidSpan) {
if (options != null && ttidSpan != null) {
final SentryDate endDate = options.getDateProvider().now();
final long durationNanos = endDate.diff(ttidSpan.getStartDate());
final long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
// 设置 TTID 指标
ttidSpan.setMeasurement(
MeasurementValue.KEY_TIME_TO_INITIAL_DISPLAY,
durationMillis,
MILLISECOND);
finishSpan(ttidSpan, endDate);
}
}关键时间点:
- 开始时间:
- 首次 Activity: 应用启动时间 (
appStartTime) - 后续 Activity: 上一个 Activity 的
onPause()时间
- 首次 Activity: 应用启动时间 (
- 结束时间: 首帧绘制完成时间
- 测量指标:
time_to_initial_display(毫秒)
操作类型: ui.load.full_display
监控对象: Time To Full Display - 完全显示时间
触发时机: Activity 开始创建 → 开发者调用 Sentry.reportFullyDisplayed()
// 源码位置: ActivityLifecycleIntegration.java
static final String TTFD_OP = "ui.load.full_display";
static final long TTFD_TIMEOUT_MILLIS = 25000; // 25秒超时
// 创建时机 (需要启用 timeToFullDisplaySpanEnabled)
if (timeToFullDisplaySpanEnabled && fullyDisplayedReporter != null && options != null) {
final @NotNull ISpan ttfdSpan = transaction.startChild(
TTFD_OP,
getTtfdDesc(activityName), // "ActivityName full display"
ttidStartTime,
Instrumenter.SENTRY,
spanOptions);
ttfdSpanMap.put(activity, ttfdSpan);
// 设置自动超时关闭
ttfdAutoCloseFuture = options.getExecutorService().schedule(
() -> finishExceededTtfdSpan(ttfdSpan, ttidSpan),
TTFD_TIMEOUT_MILLIS);
}
// 手动结束时机: Sentry.reportFullyDisplayed()
public static void reportFullyDisplayed() {
getCurrentScopes().reportFullyDisplayed();
}结束条件:
- 正常结束: 开发者调用
Sentry.reportFullyDisplayed() - 超时结束: 25秒后自动结束,状态设为
DEADLINE_EXCEEDED - 早期调用: 如果在首帧绘制前调用,会在首帧绘制时一起结束
特殊处理:
private void onFullFrameDrawn(final @NotNull ISpan ttidSpan, final @NotNull ISpan ttfdSpan) {
// 如果 TTID 还未完成,说明首帧还未绘制,设置标记
if (!ttidSpan.isFinished()) {
fullyDisplayedCalled = true;
return;
}
// 正常情况下设置 TTFD 指标并结束
if (options != null) {
final SentryDate endDate = options.getDateProvider().now();
final long durationNanos = endDate.diff(ttfdSpan.getStartDate());
final long durationMillis = TimeUnit.NANOSECONDS.toMillis(durationNanos);
ttfdSpan.setMeasurement(
MeasurementValue.KEY_TIME_TO_FULL_DISPLAY,
durationMillis,
MILLISECOND);
finishSpan(ttfdSpan, endDate);
}
}操作类型: activity.load
监控对象: 单个 Activity 的加载性能
触发时机: Performance V2 模式下的 Activity 级别监控
// 源码位置: PerformanceAndroidEventProcessor.java
private static final String APP_METRICS_ACTIVITIES_OP = "activity.load";
// 在 Performance V2 模式下创建
if (isPerformanceV2Enabled && spanContext.getOperation().contentEquals(UI_LOAD_OP)) {
// 为每个 Activity 创建独立的性能监控 Span
}监控指标:
- Activity 创建到显示的时间
- 帧率性能数据
- 内存使用情况
操作类型: app.start.cold
监控对象: 冷启动过程 (进程从零开始创建)
触发时机: 进程创建 → 首帧绘制完成
// 源码位置: ActivityLifecycleIntegration.java
static final String APP_START_COLD = "app.start.cold";
// 创建条件
if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
// 创建应用启动 Span
appStartSpan = transaction.startChild(
getAppStartOp(coldStart), // "app.start.cold"
getAppStartDesc(coldStart), // "Cold Start"
appStartTime, // 进程启动时间
Instrumenter.SENTRY,
spanOptions);
}
private @NotNull String getAppStartOp(final boolean coldStart) {
if (coldStart) {
return APP_START_COLD;
} else {
return APP_START_WARM;
}
}时间测量:
- 开始时间:
Process.getStartUptimeMillis()(Android N+) 或 SDK 初始化时间 - 结束时间: 首帧绘制完成时间
- 包含阶段: 进程创建 → ContentProvider 初始化 → Application.onCreate → Activity 创建 → 首帧绘制
操作类型: app.start.warm
监控对象: 热启动过程 (进程已存在,重新启动 Activity)
触发时机: Activity 重新创建 → 首帧绘制完成
// 热启动判断逻辑
private void setColdStart(final @Nullable Bundle savedInstanceState) {
if (!firstActivityCreated) {
final @NotNull TimeSpan appStartSpan = AppStartMetrics.getInstance().getAppStartTimeSpanWithFallback(options);
if ((appStartSpan.hasStarted() && appStartSpan.hasStopped())
|| (!AppStartMetrics.getInstance().isColdStartValid())) {
// 重启应用启动测量,标记为热启动
AppStartMetrics.getInstance().restartAppStart(lastPausedUptimeMillis);
AppStartMetrics.getInstance().setAppStartType(AppStartMetrics.AppStartType.WARM);
} else {
// 根据 savedInstanceState 判断启动类型
AppStartMetrics.getInstance().setAppStartType(
savedInstanceState == null
? AppStartMetrics.AppStartType.COLD
: AppStartMetrics.AppStartType.WARM);
}
}
}时间测量:
- 开始时间: 上一个 Activity 的
onPause()时间 - 结束时间: 首帧绘制完成时间
- 包含阶段: Activity 重新创建 → 首帧绘制
操作类型: application.load
监控对象: Application 类的初始化过程
触发时机: Application.onCreate() 执行期间
// 源码位置: PerformanceAndroidEventProcessor.java
private static final String APP_METRICS_APPLICATION_OP = "application.load";操作类型: contentprovider.load
监控对象: ContentProvider 的初始化过程
触发时机: ContentProvider.onCreate() 执行期间
// 源码位置: PerformanceAndroidEventProcessor.java
private static final String APP_METRICS_CONTENT_PROVIDER_OP = "contentprovider.load";操作类型: process.load
监控对象: 进程级别的初始化过程
触发时机: 进程创建到应用组件加载完成
// 源码位置: PerformanceAndroidEventProcessor.java
private static final String APP_METRICS_PROCESS_INIT_OP = "process.load";操作类型: http.client
监控对象: HTTP 客户端发起的网络请求
触发时机: OkHttp、Spring WebClient、OpenFeign 等拦截器自动创建
// 源码位置: SentryOkHttpInterceptor.kt
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
// 创建 HTTP 客户端 Span
val span = parentSpan?.startChild("http.client", "$method $url")
// 设置 HTTP 相关标签
span?.setTag("http.method", request.method)
span?.setTag("http.url", request.url.toString())
try {
val response = chain.proceed(request)
span?.setTag("http.status_code", response.code.toString())
return response
} catch (e: IOException) {
span?.setThrowable(e)
span?.setStatus(SpanStatus.INTERNAL_ERROR)
throw e
} finally {
span?.finish()
}
}监控指标:
- DNS 解析时间
- 连接建立时间
- SSL 握手时间
- 请求发送时间
- 响应接收时间
- HTTP 状态码
- 错误信息
集成支持:
- OkHttp:
SentryOkHttpInterceptor - Spring WebClient:
SentrySpanClientWebRequestFilter - Spring RestTemplate:
SentrySpanClientHttpRequestInterceptor - OpenFeign:
SentryFeignClient - Apollo GraphQL:
SentryApollo3HttpInterceptor,SentryApollo4HttpInterceptor
操作类型: http.server
监控对象: HTTP 服务端接收的请求
触发时机: Spring MVC、WebFlux、Servlet 等框架自动创建
// 源码位置: SentryTracingFilter.java
private static final String TRANSACTION_OP = "http.server";
private static final String TRACE_ORIGIN = "auto.http.spring.webmvc";
// 创建服务端事务
ITransaction transaction = scopes.startTransaction(
new TransactionContext(name, TransactionNameSource.URL, "http.server"),
transactionOptions);监控内容:
- HTTP 请求处理时间
- 请求路径和方法
- 响应状态码
- 异常信息
- 用户信息
集成支持:
- Spring MVC:
SentryTracingFilter - Spring WebFlux:
SentryWebFilter - Servlet:
SentryServletRequestListener
操作类型: db.query
监控对象: 通用数据库查询操作
触发时机: JDBC 拦截器自动创建
// 源码位置: SentryJdbcEventListener.java
@Override
public void onBeforeAnyExecute(final @NotNull StatementInformation statementInformation) {
final ISpan parent = scopes.getSpan();
if (parent != null && !parent.isNoOp()) {
final @NotNull SpanOptions spanOptions = new SpanOptions();
spanOptions.setOrigin("auto.db.jdbc");
final ISpan span = parent.startChild("db.query", statementInformation.getSql(), spanOptions);
CURRENT_SPAN.set(span);
}
}监控指标:
- SQL 语句执行时间
- 数据库类型 (
db.system) - 数据库名称 (
db.name) - 执行结果和异常
操作类型: db.sql.query
监控对象: SQL 数据库查询
触发时机: SQLite、Room 等数据库操作时自动创建
// 源码位置: SQLiteSpanManager.kt
fun performSql(sql: String?, operation: () -> T): T {
val span = scopes.span?.startChild("db.sql.query", sql, startTimestamp, Instrumenter.SENTRY)
span?.setTag("db.type", "sqlite")
span?.setTag("db.operation", getOperationType(sql))
try {
val result = operation()
span?.setStatus(SpanStatus.OK)
return result
} catch (e: Exception) {
span?.setThrowable(e)
span?.setStatus(SpanStatus.INTERNAL_ERROR)
throw e
} finally {
span?.finish()
}
}监控内容:
- SQL 语句类型 (SELECT, INSERT, UPDATE, DELETE)
- 执行时间
- 影响行数
- 错误信息
操作类型: file.read
监控对象: 文件读取操作
触发时机: SentryFileInputStream 自动创建
// 源码位置: SentryFileInputStream.java
private static FileInputStreamInitData init(
final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IScopes scopes)
throws FileNotFoundException {
final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read");
if (delegate == null) {
delegate = new FileInputStream(file);
}
return new FileInputStreamInitData(file, span, delegate, scopes.getOptions());
}监控指标:
- 文件路径 (
file.path) - 读取字节数 (
file.size) - 读取时间
- IO 异常
操作类型: file.write
监控对象: 文件写入操作
触发时机: SentryFileOutputStream 自动创建
// 源码位置: SentryFileOutputStream.java
private static FileOutputStreamInitData init(
final @Nullable File file, @Nullable FileOutputStream delegate, final @NotNull IScopes scopes)
throws FileNotFoundException {
final ISpan span = FileIOSpanManager.startSpan(scopes, "file.write");
if (delegate == null) {
delegate = new FileOutputStream(file);
}
return new FileOutputStreamInitData(file, span, delegate, scopes.getOptions());
}监控指标:
- 文件路径 (
file.path) - 写入字节数 (
file.size) - 写入时间
- IO 异常
操作类型: graphql.operation
监控对象: GraphQL 查询、变更、订阅操作
触发时机: GraphQL 执行引擎自动创建
// 源码位置: SentryGraphqlInstrumentation.java
@Override
public InstrumentationContext<ExecutionResult> beginExecution(
InstrumentationExecutionParameters parameters) {
final String operationName = parameters.getOperation().getName();
final String operationType = parameters.getOperation().getOperation().name();
final ISpan span = activeSpan.startChild(
"graphql.operation",
operationType + " " + operationName);
return new SimpleInstrumentationContext.Builder<ExecutionResult>()
.onCompleted((result, throwable) -> {
if (throwable != null) {
span.setThrowable(throwable);
span.setStatus(SpanStatus.INTERNAL_ERROR);
} else {
span.setStatus(SpanStatus.OK);
}
span.finish();
})
.build();
}监控内容:
- 操作类型 (query, mutation, subscription)
- 操作名称
- 执行时间
- 错误信息
- 字段解析性能
操作类型: graphql.fetcher
监控对象: GraphQL 字段解析器执行
触发时机: 字段解析时自动创建
// 源码位置: SentryDataFetcher.java
@Override
public Object get(DataFetchingEnvironment environment) throws Exception {
final String fieldName = environment.getField().getName();
final ISpan span = activeSpan.startChild("graphql.fetcher", fieldName);
try {
Object result = delegate.get(environment);
span.setStatus(SpanStatus.OK);
return result;
} catch (Exception e) {
span.setThrowable(e);
span.setStatus(SpanStatus.INTERNAL_ERROR);
throw e;
} finally {
span.finish();
}
}操作类型: bean
监控对象: Spring Bean 方法执行
触发时机: @SentryTransaction 或 @SentrySpan 注解的方法
// 源码位置: SentryTransactionAdvice.java
String operation;
if (sentryTransaction != null && !StringUtils.isEmpty(sentryTransaction.operation())) {
operation = sentryTransaction.operation();
} else {
operation = "bean"; // 默认操作类型
}
ITransaction transaction = forkedScopes.startTransaction(
new TransactionContext(nameAndSource.name, nameAndSource.source, operation),
transactionOptions);使用示例:
@Component
public class UserService {
@SentryTransaction(operation = "bean")
public User createUser(String name) {
// 业务逻辑
return new User(name);
}
@SentrySpan(operation = "bean")
public void validateUser(User user) {
// 验证逻辑
}
}操作类型: 开发者自定义
监控对象: 业务逻辑操作
触发时机: 开发者手动创建
// 手动创建自定义 Span
public void performBusinessOperation() {
final ISpan span = Sentry.getSpan();
final ISpan customSpan = span != null ?
span.startChild("business.operation", "Process User Data") : null;
if (customSpan != null) {
customSpan.setTag("user.id", "12345");
customSpan.setTag("operation.type", "data_processing");
}
try {
// 执行业务逻辑
processUserData();
if (customSpan != null) {
customSpan.setStatus(SpanStatus.OK);
}
} catch (Exception e) {
if (customSpan != null) {
customSpan.setThrowable(e);
customSpan.setStatus(SpanStatus.INTERNAL_ERROR);
}
throw e;
} finally {
if (customSpan != null) {
customSpan.finish();
}
}
}// 源码位置: MeasurementValue.java
public static final String KEY_APP_START_COLD = "app_start_cold";
public static final String KEY_APP_START_WARM = "app_start_warm";指标说明:
app_start_cold: 冷启动时间 (毫秒)app_start_warm: 热启动时间 (毫秒)
// 源码位置: MeasurementValue.java
public static final String KEY_TIME_TO_INITIAL_DISPLAY = "time_to_initial_display";
public static final String KEY_TIME_TO_FULL_DISPLAY = "time_to_full_display";
public static final String KEY_FRAMES_TOTAL = "frames_total";
public static final String KEY_FRAMES_SLOW = "frames_slow";
public static final String KEY_FRAMES_FROZEN = "frames_frozen";
public static final String KEY_FRAMES_DELAY = "frames_delay";指标说明:
time_to_initial_display: 首次显示时间 (毫秒)time_to_full_display: 完全显示时间 (毫秒)frames_total: 总帧数frames_slow: 慢帧数量 (>16.67ms for 60fps)frames_frozen: 冻结帧数量 (>700ms)frames_delay: 帧延迟时间 (秒)
// 源码位置: SpanDataConvention.java
String DB_SYSTEM_KEY = "db.system";
String DB_NAME_KEY = "db.name";
String HTTP_QUERY_KEY = "http.query";
String HTTP_FRAGMENT_KEY = "http.fragment";
String HTTP_METHOD_KEY = "http.request.method";
String THREAD_ID = "thread.id";
String THREAD_NAME = "thread.name";sequenceDiagram
participant App as 应用进程
participant Activity as Activity
participant Sentry as Sentry SDK
participant Backend as Sentry 后台
Note over App: 冷启动开始
App->>Sentry: 进程创建 (Process.getStartUptimeMillis)
Sentry->>Sentry: 开始 app.start.cold Span
App->>Activity: onCreate()
Activity->>Sentry: 开始 ui.load Transaction
Sentry->>Sentry: 开始 ui.load.initial_display Span
Sentry->>Sentry: 开始 ui.load.full_display Span (可选)
Activity->>Activity: onStart()
Activity->>Activity: onResume()
Activity->>Activity: 首帧绘制
Activity->>Sentry: onFirstFrameDrawn()
Sentry->>Sentry: 结束 app.start.cold Span
Sentry->>Sentry: 结束 ui.load.initial_display Span
Sentry->>Sentry: 设置 TTID 指标
Note over Activity: 用户交互,内容加载完成
Activity->>Sentry: Sentry.reportFullyDisplayed()
Sentry->>Sentry: 结束 ui.load.full_display Span
Sentry->>Sentry: 设置 TTFD 指标
Activity->>Activity: onPause()
Activity->>Sentry: 记录 lastPausedTime
Sentry->>Backend: 上报 Transaction 和所有 Span 数据
sequenceDiagram
participant App as 应用
participant OkHttp as OkHttp
participant Interceptor as SentryInterceptor
participant Network as 网络
participant Sentry as Sentry 后台
App->>OkHttp: 发起 HTTP 请求
OkHttp->>Interceptor: intercept()
Interceptor->>Interceptor: 创建 http.client Span
Interceptor->>Interceptor: 设置请求标签 (method, url)
Interceptor->>Network: 执行网络请求
Note over Network: DNS 解析、连接建立、数据传输
Network->>Interceptor: 返回响应
Interceptor->>Interceptor: 设置响应标签 (status_code)
Interceptor->>Interceptor: 结束 Span
Interceptor->>Sentry: 上报 Span 数据
Interceptor->>App: 返回响应
sequenceDiagram
participant App as 应用
participant JDBC as JDBC Driver
participant P6Spy as P6Spy Interceptor
participant DB as 数据库
participant Sentry as Sentry 后台
App->>JDBC: 执行 SQL 查询
JDBC->>P6Spy: onBeforeAnyExecute()
P6Spy->>P6Spy: 创建 db.query Span
P6Spy->>P6Spy: 设置 SQL 语句和数据库信息
P6Spy->>DB: 执行 SQL
Note over DB: 查询处理、索引查找、结果返回
DB->>P6Spy: 返回查询结果
P6Spy->>P6Spy: onAfterAnyExecute()
P6Spy->>P6Spy: 设置执行结果和异常信息
P6Spy->>P6Spy: 结束 Span
P6Spy->>Sentry: 上报 Span 数据
P6Spy->>App: 返回查询结果
SentryAndroid.init(this) { options ->
// 启用性能监控
options.tracesSampleRate = 1.0
// 启用 Activity 生命周期追踪
options.isEnableAutoActivityLifecycleTracing = true
// 启用应用启动追踪
options.isEnableAppStartTracking = true
// 启用 TTFD 追踪 (需要手动调用 reportFullyDisplayed)
options.isEnableTimeToFullDisplayTracing = true
// 启用网络监控
options.isEnableNetworkEventBreadcrumbs = true
// 启用帧率监控
options.isEnableFramesTracking = true
// 启用文件 IO 监控
options.isTracingEnabled = true
}// OkHttp 集成
val client = OkHttpClient.Builder()
.addInterceptor(SentryOkHttpInterceptor())
.build()
// Spring WebClient 集成
@Bean
fun webClient(): WebClient {
return WebClient.builder()
.filter(SentrySpanClientWebRequestFilter())
.build()
}
// Apollo GraphQL 集成
val apolloClient = ApolloClient.Builder()
.serverUrl("https://api.example.com/graphql")
.addHttpInterceptor(SentryApollo3HttpInterceptor())
.build()# application.properties
# 启用 P6Spy JDBC 拦截器
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:postgresql://localhost:5432/mydb
# spy.properties
driverlist=org.postgresql.Driver
appender=io.sentry.jdbc.SentryJdbcEventListenerclass MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 异步加载数据
loadDataAsync {
// 数据加载完成,UI 完全显示
Sentry.reportFullyDisplayed()
}
}
private fun loadDataAsync(callback: () -> Unit) {
lifecycleScope.launch {
// 模拟数据加载
delay(1000)
// 更新 UI
updateUI()
// 通知 Sentry UI 完全显示
callback()
}
}
}// 使用 use 扩展函数自动管理 Span 生命周期
fun processUserData(userId: String) {
Sentry.startTransaction("user_data_processing", "business").use { transaction ->
transaction.setTag("user.id", userId)
// 数据库查询 Span
transaction.startChild("db.query", "SELECT user data").use { dbSpan ->
dbSpan.setTag("db.table", "users")
val userData = database.getUser(userId)
dbSpan.setData("rows.affected", 1)
}
// 数据处理 Span
transaction.startChild("data.processing", "Transform user data").use { processSpan ->
processSpan.setTag("processing.type", "transformation")
val processedData = transformData(userData)
processSpan.setData("items.processed", processedData.size)
}
transaction.setStatus(SpanStatus.OK)
}
}@Service
public class UserService {
// 事务级别监控
@SentryTransaction(name = "createUser", operation = "business")
public User createUser(CreateUserRequest request) {
return processUserCreation(request);
}
// Span 级别监控
@SentrySpan(operation = "validation")
public void validateUser(User user) {
// 验证逻辑
}
@SentrySpan(operation = "db.operation", description = "Save user to database")
public void saveUser(User user) {
userRepository.save(user);
}
}TTID/TTFD Span 未出现:
- 检查是否启用了
isEnableAutoActivityLifecycleTracing - 确认
tracesSampleRate> 0 - 验证 Activity 是否正常完成生命周期
TTFD Span 超时:
- 检查是否调用了
Sentry.reportFullyDisplayed() - 确认调用时机是否在 25 秒内
- 验证是否在正确的线程调用
App Start Span 缺失:
- 确认是否为前台启动 (非后台启动)
- 检查启动时间是否超过 1 分钟 (会被标记为无效)
- 验证是否为首次 Activity 创建
HTTP Span 未创建:
- 确认是否正确配置了拦截器
- 检查是否存在活跃的父 Span
- 验证网络请求是否成功执行
数据库 Span 缺失:
- 确认是否正确配置了 P6Spy
- 检查 JDBC URL 是否包含 p6spy 前缀
- 验证是否存在活跃的事务
// 启用详细日志查看 Span 创建过程
SentryAndroid.init(this) { options ->
options.isDebug = true
options.diagnosticLevel = SentryLevel.DEBUG
// 设置自定义 logger
options.setLogger(object : ILogger {
override fun log(level: SentryLevel, message: String, vararg args: Any?) {
Log.d("Sentry", String.format(message, *args))
}
override fun log(level: SentryLevel, message: String, throwable: Throwable?) {
Log.d("Sentry", message, throwable)
}
override fun log(level: SentryLevel, throwable: Throwable?, message: String, vararg args: Any?) {
Log.d("Sentry", String.format(message, *args), throwable)
}
override fun isEnabled(level: SentryLevel?): Boolean = true
})
}Android 应用:
- 启用 Activity 生命周期追踪
- 配置合适的采样率 (生产环境建议 0.1)
- 集成 OkHttp 拦截器进行网络监控
- 在关键页面调用
reportFullyDisplayed() - 启用帧率监控检测 UI 卡顿
Spring 应用:
- 启用 Spring 集成自动配置
- 在关键业务方法添加
@SentryTransaction注解 - 配置数据库监控 (P6Spy)
- 集成 WebClient 或 RestTemplate 拦截器
通用配置:
- 设置合理的事务超时时间
- 配置错误采样策略
- 启用分布式追踪头传播
- 设置环境和版本信息
通过理解这些 Trace 操作字段的监控机制,开发者可以更好地分析应用性能,识别瓶颈,并进行针对性优化。每种操作类型都有其特定的用途和最佳实践,合理配置和使用这些监控功能可以大大提升应用的可观测性。