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}