datafusion_tracing/rule_options.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17//
18// This product includes software developed at Datadog (https://www.datadoghq.com/) Copyright 2025 Datadog, Inc.
19
20/// Instrumentation level for a phase (analyzer, optimizer, or physical optimizer).
21///
22/// Rule spans always require a parent phase span, so the levels are hierarchical:
23/// - `Disabled`: no spans (default)
24/// - `PhaseOnly`: only the phase span
25/// - `Full`: phase span + individual rule spans
26#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
27pub(crate) enum InstrumentationLevel {
28 /// No instrumentation for this phase (default).
29 #[default]
30 Disabled,
31 /// Only phase-level span (e.g., "optimize_logical_plan"), no individual rule spans.
32 /// Useful for reducing trace verbosity while still tracking phase timing.
33 PhaseOnly,
34 /// Full instrumentation: phase span + individual rule spans.
35 Full,
36}
37
38impl InstrumentationLevel {
39 /// Returns true if the phase span should be created.
40 pub fn phase_span_enabled(&self) -> bool {
41 matches!(self, Self::PhaseOnly | Self::Full)
42 }
43
44 /// Returns true if individual rule spans should be created.
45 pub fn rule_spans_enabled(&self) -> bool {
46 matches!(self, Self::Full)
47 }
48}
49
50/// Configuration options for instrumented DataFusion rules (Analyzer, Optimizer, Physical Optimizer).
51#[derive(Clone, Debug)]
52pub struct RuleInstrumentationOptions {
53 /// Whether to include a unified diff of plan changes in the span.
54 pub(crate) plan_diff: bool,
55
56 /// Instrumentation level for analyzer rules.
57 pub(crate) analyzer: InstrumentationLevel,
58
59 /// Instrumentation level for logical optimizer rules.
60 pub(crate) optimizer: InstrumentationLevel,
61
62 /// Instrumentation level for physical optimizer rules.
63 pub(crate) physical_optimizer: InstrumentationLevel,
64}
65
66impl Default for RuleInstrumentationOptions {
67 fn default() -> Self {
68 Self {
69 plan_diff: false,
70 analyzer: InstrumentationLevel::Disabled,
71 optimizer: InstrumentationLevel::Disabled,
72 physical_optimizer: InstrumentationLevel::Disabled,
73 }
74 }
75}
76
77impl RuleInstrumentationOptions {
78 /// Creates a new builder for `RuleInstrumentationOptions`.
79 pub fn builder() -> RuleInstrumentationOptionsBuilder {
80 RuleInstrumentationOptionsBuilder::default()
81 }
82
83 /// Creates options with all phases enabled at `Full` level.
84 ///
85 /// This is a convenience constructor for the common case of enabling
86 /// full instrumentation for all rule phases.
87 ///
88 /// # Example
89 ///
90 /// ```
91 /// use datafusion_tracing::RuleInstrumentationOptions;
92 ///
93 /// let options = RuleInstrumentationOptions::full();
94 /// ```
95 pub fn full() -> Self {
96 Self {
97 plan_diff: false,
98 analyzer: InstrumentationLevel::Full,
99 optimizer: InstrumentationLevel::Full,
100 physical_optimizer: InstrumentationLevel::Full,
101 }
102 }
103
104 /// Creates options with all phases enabled at `PhaseOnly` level.
105 ///
106 /// This creates phase-level spans without individual rule spans,
107 /// useful for reducing trace verbosity while still tracking phase timing.
108 pub fn phase_only() -> Self {
109 Self {
110 plan_diff: false,
111 analyzer: InstrumentationLevel::PhaseOnly,
112 optimizer: InstrumentationLevel::PhaseOnly,
113 physical_optimizer: InstrumentationLevel::PhaseOnly,
114 }
115 }
116
117 /// Returns a new options with plan diff enabled.
118 pub fn with_plan_diff(mut self) -> Self {
119 self.plan_diff = true;
120 self
121 }
122}
123
124/// The builder for `RuleInstrumentationOptions`.
125#[derive(Default)]
126pub struct RuleInstrumentationOptionsBuilder {
127 plan_diff: bool,
128 analyzer: InstrumentationLevel,
129 optimizer: InstrumentationLevel,
130 physical_optimizer: InstrumentationLevel,
131}
132
133impl RuleInstrumentationOptionsBuilder {
134 /// Enables plan diff in spans.
135 pub fn plan_diff(mut self) -> Self {
136 self.plan_diff = true;
137 self
138 }
139
140 /// Enables all phases at `Full` level.
141 pub fn all(mut self) -> Self {
142 self.analyzer = InstrumentationLevel::Full;
143 self.optimizer = InstrumentationLevel::Full;
144 self.physical_optimizer = InstrumentationLevel::Full;
145 self
146 }
147
148 /// Enables all phases at `PhaseOnly` level.
149 pub fn all_phase_only(mut self) -> Self {
150 self.analyzer = InstrumentationLevel::PhaseOnly;
151 self.optimizer = InstrumentationLevel::PhaseOnly;
152 self.physical_optimizer = InstrumentationLevel::PhaseOnly;
153 self
154 }
155
156 /// Enables analyzer instrumentation at `Full` level.
157 pub fn analyzer(mut self) -> Self {
158 self.analyzer = InstrumentationLevel::Full;
159 self
160 }
161
162 /// Enables analyzer instrumentation at `PhaseOnly` level.
163 pub fn analyzer_phase_only(mut self) -> Self {
164 self.analyzer = InstrumentationLevel::PhaseOnly;
165 self
166 }
167
168 /// Enables logical optimizer instrumentation at `Full` level.
169 pub fn optimizer(mut self) -> Self {
170 self.optimizer = InstrumentationLevel::Full;
171 self
172 }
173
174 /// Enables logical optimizer instrumentation at `PhaseOnly` level.
175 pub fn optimizer_phase_only(mut self) -> Self {
176 self.optimizer = InstrumentationLevel::PhaseOnly;
177 self
178 }
179
180 /// Enables physical optimizer instrumentation at `Full` level.
181 pub fn physical_optimizer(mut self) -> Self {
182 self.physical_optimizer = InstrumentationLevel::Full;
183 self
184 }
185
186 /// Enables physical optimizer instrumentation at `PhaseOnly` level.
187 pub fn physical_optimizer_phase_only(mut self) -> Self {
188 self.physical_optimizer = InstrumentationLevel::PhaseOnly;
189 self
190 }
191
192 /// Consumes the builder and creates a `RuleInstrumentationOptions` instance.
193 pub fn build(self) -> RuleInstrumentationOptions {
194 RuleInstrumentationOptions {
195 plan_diff: self.plan_diff,
196 analyzer: self.analyzer,
197 optimizer: self.optimizer,
198 physical_optimizer: self.physical_optimizer,
199 }
200 }
201}