Skip to content

Spans

Everything below is what Flare actually emits. Attributes marked conditional are absent rather than zero when the underlying value was never observed. A missing attribute means "not measured", which is different from a measured zero.

spark.application

SpanKind SERVER, trace root.

Opened when SparkContext initialises, closed at SparkContext.stop().

Attribute Type Description
spark.application.id string SparkContext.applicationId
spark.application.name string SparkContext.appName
spark.master.url string SparkContext.master
flare.version string Flare build version
flare.trace.granularity string Effective FLARE_TRACE_GRANULARITY

spark.sql.N

SpanKind INTERNAL, child of spark.application.

One per SparkListenerSQLExecutionStart. N is the execution id.

Attribute Type Description
spark.sql.execution.id long Matches the N in the span name
spark.sql.description string Conditional: omitted when Spark reports it empty. Capped by FLARE_SQL_DESCRIPTION_MAX_CHARS
spark.sql.details string Conditional: the call-site stack. Capped by FLARE_SQL_DETAILS_MAX_CHARS
spark.sql.plan string The physical plan that ran, post-AQE. Capped by FLARE_SQL_PLAN_MAX_CHARS
spark.sql.plan.truncated bool Conditional: set only when the cap clipped the plan
spark.sql.plan.initial string Conditional: the pre-AQE plan. Off unless FLARE_SQL_PLAN_INITIAL_MAX_CHARS > 0
spark.sql.plan.initial.truncated bool Conditional: as above, for the initial plan
spark.sql.plan.fingerprint string 16 hex chars. Hash of the plan's shape. Always emitted when a plan exists, including when the caps are 0
spark.sql.plan.initial.fingerprint string As above for the pre-AQE plan. The more stable of the two, see Plan fingerprints

spark.job.N

SpanKind INTERNAL, child of spark.sql.N or spark.application.

Attribute Type Description
spark.job.id long
spark.job.stage.count long Number of stages the job was planned with
spark.job.description string Conditional: from spark.job.description local property
databricks.job.id string Conditional: the Lakeflow job that submitted the Spark job, on Databricks
databricks.job.run_id string Conditional: that job's run. Every task of one run shares it
databricks.task.run_id string Conditional: the task run. A retried task gets a new one
spark.job.result string SUCCESS or FAILED
error.type string Conditional: the exception class, e.g. java.lang.ArithmeticException
error.message string Conditional: present only on FAILED, first 500 chars

spark.stage.N

SpanKind INTERNAL, child of spark.job.N.

Metrics below are Spark's own sums across every task in the stage, so they are directly comparable with each other. All are set at onStageCompleted from StageInfo.taskMetrics, except spark.stage.scheduler.delay_ms, which Spark does not report and Flare derives from task ends; see below.

Attribute Type Description
spark.stage.id long
spark.stage.attempt.id long
spark.stage.name string Spark's RDD.creationSite, the same string the Spark UI shows. Frequently useless for async stages; see below
spark.stage.sql.execution_id long Conditional: present when the stage's job belongs to a SQL execution
spark.stage.sql.description string Conditional: the SQL execution's description, e.g. show at SkewedJob.scala:48. This is the attribute that names user code when spark.stage.name cannot
spark.stage.task.count long
spark.stage.executor.run_time_ms long
spark.stage.executor.cpu_time_ms long Converted from Spark's nanoseconds
spark.stage.jvm.gc_time_ms long GC time rivalling run time is the answer to "why is this stage slow"
spark.stage.executor.deserialize_time_ms long
spark.stage.executor.deserialize_cpu_time_ms long Converted from nanoseconds
spark.stage.result.serialization_time_ms long
spark.stage.scheduler.delay_ms long Conditional: derived, see below
spark.stage.input.bytes / .records long
spark.stage.output.bytes / .records long
spark.stage.shuffle.read_bytes long
spark.stage.shuffle.write_bytes long
spark.stage.memory.spilled_bytes long
spark.stage.disk.spilled_bytes long
spark.stage.failure_reason string Conditional: first 500 chars
error.type string Conditional: see the failure note below; omitted more often here than on job or task spans
error.message string Conditional: present only on failure, first 500 chars

Why spark.stage.name is often useless, and what to use instead. Spark derives it from RDD.creationSite. For an async subquery or broadcast, the RDD is created on a Spark thread-pool thread, so the name resolves to something like $anonfun$withThreadLocalCaptured$2 at CompletableFuture.java:1768. The Spark UI shows the same string. In one four-stage SkewedJob run, three of the four stages looked like that.

StageInfo.details cannot recover it either: that stack was captured on the same pool thread and contains no user frame at all, only org.apache.spark.* and java.base/*. The stages whose names are useful are the only ones whose stacks contain user code, so walking the stack adds nothing where it is needed.

spark.stage.sql.description is the answer: for any stage under a SQL execution, Spark's own execution description names the user code exactly. spark.stage.name is left untouched so Spark UI correlation still works. A pure-RDD stage belonging to no SQL execution gets neither attribute: there is no source for the information in that case.

spark.stage.scheduler.delay_ms is the only stage attribute Spark does not report. It is derived per task as duration − executorRunTime − deserializeTime − resultSerializationTime − resultFetchTime and summed over the stage, matching Spark's own AppStatusUtils.schedulerDelay. The clamp at zero is applied per task, never to the sum, because driver and executor clocks differ and a fast task can report more run time than its own wall clock. The attribute is omitted when no task ends were observed, because a 0 there would read as "no queueing" rather than "not measured".

spark.task.executor

SpanKind INTERNAL, child of spark.stage.N, emitted on the executor JVM.

Subject to FLARE_TRACE_GRANULARITY, FLARE_SLOW_TASK_MS, FLARE_RETRY_TASKS_ONLY and FLARE_MAX_SPANS_PER_TRACE. This span is built from TaskContext, so driver-only TaskInfo fields (host, locality, speculative) are not available to it.

Under FLARE_SLOW_TASK_MS, spans made inside a task, such as a JDBC query, and the task's log lines are children of the stage span, not the task span. Whether a task span is kept is only known when the task ends, and anything pointing at a dropped one would point at nothing.

Attribute Type Description
spark.task.partition.id long
spark.task.attempt.id long > 0 means a retry
spark.stage.id long Conditional: only under FLARE_TRACE_GRANULARITY=all. The parent span already identifies the stage; this repeats it so you can filter without a join
spark.task.sql.execution_id long Conditional: present when the task belongs to a SQL execution
spark.task.result string SUCCESS, FAILED, KILLED if Spark killed the task (a losing speculative attempt, or a cancelled job or stage), or SHUTDOWN if the JVM went down mid-task. Only FAILED and SHUTDOWN set an error status
spark.task.kill_reason string Conditional: why Spark killed a KILLED task: another_attempt_succeeded (the losing attempt under speculation), stage_finished (still running when its stage finished: under speculation, the original once its copy finished the stage, or a task no longer needed when its stage ends early), cancelled (its job, stage or job group was cancelled) or other
error.type string Conditional: exception class for a user exception, otherwise the Spark failure reason class, e.g. org.apache.spark.TaskResultLost
error.message string Conditional: present only on FAILED, first 500 chars
spark.task.duration_ms long Wall clock on the executor thread
spark.task.input.bytes / output.bytes long
spark.task.shuffle.read_bytes / write_bytes long
spark.task.peak_memory_bytes long

Failures

error.type, error.message and the exception event.

A failed job, stage or task span carries error.message and status ERROR, and error.type when the failure's type can be recovered, which on stage spans it sometimes cannot (see below). Where a stack trace is available it is attached as an OTEL exception span event with exception.type, exception.message and exception.stacktrace (capped at 8000 chars).

Spark reports failures three different ways, which is why the detail differs by span:

Span Source error.type exception event
spark.job.N live Throwable from JobFailed Exception class Yes, the full driver-side stack trace
spark.stage.N formatted string only Recovered by pattern; omitted if none matches No
spark.task.executor TaskFailedReason ExceptionFailure.className, else the reason class Yes, for ExceptionFailure

error.type is the key you group failures by, so it is left unset rather than guessed. On a stage span Spark hands Flare only a formatted string, and that string names the wrapper before it names the cause:

org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 1.0
failed 4 times, most recent failure: Lost task 0.3 in stage 1.0 (TID 7) (executor 1):
java.lang.ArithmeticException: / by zero

Reading the first class name here would report SparkException for practically every failed stage. Flare instead searches after the last most recent failure:, or after the innermost Caused by: when that is absent, so the reported type is java.lang.ArithmeticException. When neither anchor is present it falls back to the first fully-qualified name ending in Exception, Error or Throwable.

Spark 3.4+ also formats many failures as an error class with no exception name anywhere in the text:

[DIVIDE_BY_ZERO] Division by zero. Use `try_divide` to tolerate divisor being 0 …
== SQL (line 1, position 1) ==
id div divisor

When no class name is found, that leading error class is used instead, so this stage reports error.type = DIVIDE_BY_ZERO. Spark error classes are stable, low-cardinality identifiers, which makes them a good grouping key rather than a lesser one. An exception class still wins when both are present. Only when neither is available is the attribute omitted.

A missing error.type means "not recoverable here", not "no error": error.message and spark.stage.failure_reason are still populated. Under FLARE_TRACE_GRANULARITY=tasks or all the task span for the same failure carries the structured version, read from ExceptionFailure's own fields rather than from text.

The four byte counts and peak memory come from TaskContext.taskMetrics(), which is private[spark] and can throw from outside org.apache.spark or in barrier mode. Flare logs at debug and emits the span without them rather than failing the task, so all five are absent together when that happens.