Skip to content

Commit 6a9c213

Browse files
authored
tracing: Simplify initialization (#1026)
The `Settings::build` method is fairly large and complex. This change splits into a few smaller units and reduces boilerplate--especially around reading the settings from the environment. This change is being made in service of #601 to help avoid making the build method even more complicated.
1 parent cafa93d commit 6a9c213

File tree

2 files changed

+110
-99
lines changed

2 files changed

+110
-99
lines changed

linkerd/tracing/src/lib.rs

Lines changed: 109 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,48 @@ use std::{env, str};
1212
pub use tokio_trace::tasks::TaskList;
1313
use tokio_trace::tasks::TasksLayer;
1414
use tracing::Dispatch;
15-
use tracing_subscriber::{fmt::format, prelude::*};
15+
use tracing_subscriber::{
16+
fmt::format::{self, DefaultFields},
17+
layer::Layered,
18+
prelude::*,
19+
reload, EnvFilter,
20+
};
21+
22+
type Registry =
23+
Layered<reload::Layer<EnvFilter, tracing_subscriber::Registry>, tracing_subscriber::Registry>;
1624

1725
const ENV_LOG_LEVEL: &str = "LINKERD2_PROXY_LOG";
1826
const ENV_LOG_FORMAT: &str = "LINKERD2_PROXY_LOG_FORMAT";
1927

2028
const DEFAULT_LOG_LEVEL: &str = "warn,linkerd=info";
2129
const DEFAULT_LOG_FORMAT: &str = "PLAIN";
2230

31+
#[derive(Debug, Default)]
32+
pub struct Settings {
33+
filter: Option<String>,
34+
format: Option<String>,
35+
is_test: bool,
36+
}
37+
38+
#[derive(Clone)]
39+
pub struct Handle(Inner);
40+
41+
#[derive(Clone)]
42+
enum Inner {
43+
Disabled,
44+
Enabled {
45+
level: level::Handle,
46+
tasks: TaskList,
47+
},
48+
}
49+
2350
/// Initialize tracing and logging with the value of the `ENV_LOG`
2451
/// environment variable as the verbosity-level filter.
2552
pub fn init() -> Result<Handle, Error> {
26-
let log_level = env::var(ENV_LOG_LEVEL).unwrap_or_else(|_| DEFAULT_LOG_LEVEL.to_string());
27-
if let "OFF" = log_level.to_uppercase().trim() {
28-
return Ok(Handle(Inner::Disabled));
29-
}
30-
31-
let log_format = env::var(ENV_LOG_FORMAT).unwrap_or_else(|_| DEFAULT_LOG_FORMAT.to_string());
32-
let (dispatch, handle) = Settings::default()
33-
.filter(log_level)
34-
.format(log_format)
35-
.build();
53+
let (dispatch, handle) = match Settings::from_env() {
54+
Some(s) => s.build(),
55+
None => return Ok(Handle(Inner::Disabled)),
56+
};
3657

3758
// Set the default subscriber.
3859
tracing::dispatcher::set_global_default(dispatch)?;
@@ -57,110 +78,104 @@ pub fn init_log_compat() -> Result<(), Error> {
5778
Ok(())
5879
}
5980

60-
#[derive(Debug, Default)]
61-
pub struct Settings {
62-
filter: Option<String>,
63-
format: Option<String>,
64-
test: bool,
65-
}
81+
// === impl Settings ===
6682

6783
impl Settings {
68-
pub fn from_env() -> Self {
69-
let mut settings = Settings::default();
70-
if let Ok(filter) = env::var(ENV_LOG_LEVEL) {
71-
settings = settings.filter(filter);
72-
}
73-
if let Ok(format) = env::var(ENV_LOG_FORMAT) {
74-
settings = settings.format(format);
84+
pub fn from_env() -> Option<Self> {
85+
let filter = std::env::var(ENV_LOG_LEVEL).ok();
86+
if let Some(level) = filter.as_ref() {
87+
if level.to_uppercase().trim() == "OFF" {
88+
return None;
89+
}
7590
}
76-
settings
91+
92+
Some(Self {
93+
filter,
94+
format: std::env::var(ENV_LOG_FORMAT).ok(),
95+
is_test: false,
96+
})
7797
}
7898

79-
pub fn filter(self, filter: impl Into<String>) -> Self {
99+
fn for_test(filter: String, format: String) -> Self {
80100
Self {
81-
filter: Some(filter.into()),
82-
..self
101+
filter: Some(filter),
102+
format: Some(format),
103+
is_test: true,
83104
}
84105
}
85106

86-
pub fn format(self, format: impl Into<String>) -> Self {
87-
Self {
88-
format: Some(format.into()),
89-
..self
90-
}
107+
fn format(&self) -> String {
108+
self.format
109+
.as_deref()
110+
.unwrap_or(DEFAULT_LOG_FORMAT)
111+
.to_uppercase()
91112
}
92113

93-
pub fn test(self, test: bool) -> Self {
94-
Self { test, ..self }
114+
fn mk_registry(&self) -> (Registry, level::Handle) {
115+
let log_level = self.filter.as_deref().unwrap_or(DEFAULT_LOG_LEVEL);
116+
let (filter, level) = reload::Layer::new(EnvFilter::new(log_level));
117+
let reg = tracing_subscriber::registry().with(filter);
118+
(reg, level::Handle::new(level))
95119
}
96120

97-
pub fn build(self) -> (Dispatch, Handle) {
98-
let filter = self.filter.unwrap_or_else(|| DEFAULT_LOG_LEVEL.to_string());
99-
let format = self
100-
.format
101-
.unwrap_or_else(|| DEFAULT_LOG_FORMAT.to_string());
121+
fn mk_json(&self, registry: Registry) -> (Dispatch, TaskList) {
122+
let (tasks, tasks_layer) = TasksLayer::<format::JsonFields>::new();
123+
let registry = registry.with(tasks_layer);
102124

103-
// Set up the subscriber
104125
let fmt = tracing_subscriber::fmt::format()
105126
.with_timer(Uptime::starting_now())
106-
.with_thread_ids(!self.test);
107-
let filter = tracing_subscriber::EnvFilter::new(filter);
108-
let (filter, level) = tracing_subscriber::reload::Layer::new(filter);
109-
let level = level::Handle::new(level);
110-
let registry = tracing_subscriber::registry().with(filter);
111-
112-
let (dispatch, tasks) = match format.to_uppercase().as_ref() {
113-
"JSON" => {
114-
let (tasks, tasks_layer) = TasksLayer::<format::JsonFields>::new();
115-
let fmt = fmt
116-
// Configure the formatter to output JSON logs.
117-
.json()
118-
// Output the current span context as a JSON list.
119-
.with_span_list(true)
120-
// Don't output a field for the current span, since this
121-
// would duplicate information already in the span list.
122-
.with_current_span(false);
123-
let fmt = tracing_subscriber::fmt::layer()
124-
// Use the JSON event formatter.
125-
.event_format(fmt)
126-
// Since we're using the JSON event formatter, we must also
127-
// use the JSON field formatter.
128-
.fmt_fields(format::JsonFields::default());
129-
let registry = registry.with(tasks_layer);
130-
let dispatch = if self.test {
131-
registry.with(fmt.with_test_writer()).into()
132-
} else {
133-
registry.with(fmt).into()
134-
};
135-
(dispatch, tasks)
136-
}
137-
_ => {
138-
let (tasks, tasks_layer) = TasksLayer::<format::DefaultFields>::new();
139-
let registry = registry.with(tasks_layer);
140-
let fmt = tracing_subscriber::fmt::layer().event_format(fmt);
141-
let dispatch = if self.test {
142-
registry.with(fmt.with_test_writer()).into()
143-
} else {
144-
registry.with(fmt).into()
145-
};
146-
(dispatch, tasks)
147-
}
127+
.with_thread_ids(!self.is_test)
128+
// Configure the formatter to output JSON logs.
129+
.json()
130+
// Output the current span context as a JSON list.
131+
.with_span_list(true)
132+
// Don't output a field for the current span, since this
133+
// would duplicate information already in the span list.
134+
.with_current_span(false);
135+
136+
let fmt = tracing_subscriber::fmt::layer()
137+
// Use the JSON event formatter.
138+
.event_format(fmt)
139+
// Since we're using the JSON event formatter, we must also
140+
// use the JSON field formatter.
141+
.fmt_fields(format::JsonFields::default());
142+
143+
let dispatch = if self.is_test {
144+
registry.with(fmt.with_test_writer()).into()
145+
} else {
146+
registry.with(fmt).into()
148147
};
149148

150-
(dispatch, Handle(Inner::Enabled { level, tasks }))
149+
(dispatch, tasks)
151150
}
152-
}
153151

154-
#[derive(Clone)]
155-
pub struct Handle(Inner);
152+
fn mk_plain(&self, registry: Registry) -> (Dispatch, TaskList) {
153+
let (tasks, tasks_layer) = TasksLayer::<DefaultFields>::new();
154+
let registry = registry.with(tasks_layer);
156155

157-
#[derive(Clone)]
158-
enum Inner {
159-
Disabled,
160-
Enabled {
161-
level: level::Handle,
162-
tasks: TaskList,
163-
},
156+
let fmt = tracing_subscriber::fmt::format()
157+
.with_timer(Uptime::starting_now())
158+
.with_thread_ids(!self.is_test);
159+
let fmt = tracing_subscriber::fmt::layer().event_format(fmt);
160+
let dispatch = if self.is_test {
161+
registry.with(fmt.with_test_writer()).into()
162+
} else {
163+
registry.with(fmt).into()
164+
};
165+
166+
(dispatch, tasks)
167+
}
168+
169+
pub fn build(self) -> (Dispatch, Handle) {
170+
let (registry, level) = self.mk_registry();
171+
172+
let (dispatch, tasks) = match self.format().as_ref() {
173+
"JSON" => self.mk_json(registry),
174+
_ => self.mk_plain(registry),
175+
};
176+
177+
(dispatch, Handle(Inner::Enabled { level, tasks }))
178+
}
164179
}
165180

166181
// === impl Handle ===

linkerd/tracing/src/test.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,7 @@ pub fn trace_subscriber(default: impl ToString) -> (Dispatch, Handle) {
1515
// This may fail, since the global log compat layer may have been
1616
// initialized by another test.
1717
let _ = init_log_compat();
18-
Settings::default()
19-
.filter(log_level)
20-
.format(log_format)
21-
.test(true)
22-
.build()
18+
Settings::for_test(log_level, log_format).build()
2319
}
2420

2521
pub fn with_default_filter(default: impl ToString) -> tracing::dispatcher::DefaultGuard {

0 commit comments

Comments
 (0)