Skip to content

Commit 796b8a4

Browse files
committed
Unify log source labeling and env inheritance
1 parent b040d91 commit 796b8a4

6 files changed

Lines changed: 74 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ All notable changes to `devloop` will be recorded in this file.
55
## [Unreleased]
66

77
### Fixed
8-
- Stopped `devloop`'s own `RUST_LOG` setting from leaking into managed
9-
child processes unless the child explicitly sets `RUST_LOG` in
10-
config.
11-
- Defaulted managed child `RUST_LOG` to `info` when unset so Rust-based
12-
child processes do not surprise users with verbose debug output.
138
- Removed bright white from inherited output label colors and dimmed
149
source labels alongside dimmed inherited process bodies.
10+
- Restored managed child-process environment inheritance so `devloop`
11+
and supervised processes read the same ambient `RUST_LOG` unless repo
12+
config explicitly overrides it.
13+
- Prefixed internal dependency logs under `devloop`, for example
14+
`[devloop hyper_util ...]`, and reordered managed-process labels to
15+
`[executable process-name]` so the emitting process is visible first.
1516

1617
## [0.4.0] - 2026-03-25
1718

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,18 +122,18 @@ At the same time, the tunnel itself is described as a managed process:
122122
* inherited process output is source-labeled without wrapper scripts
123123

124124
When you need to identify which managed process emitted a line in mixed
125-
output, inherited process lines include the configured process name and
126-
executable automatically. The label is color-coded per process, and the
127-
body style is configurable:
125+
output, inherited process lines include the executable first and the
126+
configured process name second. The label is color-coded per process,
127+
and the body style is configurable:
128128

129129
```toml
130130
[process.tunnel]
131131
command = ["cloudflared", "tunnel", "--url", "http://127.0.0.1:18080"]
132132
output = { inherit = true, body_style = "plain", rules = [{ state_key = "tunnel_url", extract = "url_token" }] }
133133
```
134134

135-
That renders inherited lines with the process name and executable, for
136-
example `[tunnel cloudflared] ...`, using ANSI color when stdout is a
135+
That renders inherited lines with the executable and process name, for
136+
example `[cloudflared tunnel] ...`, using ANSI color when stdout is a
137137
terminal and `NO_COLOR` is not set.
138138

139139
For the runtime behavior reference, see

docs/behavior.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ Managed processes are long-running child commands.
5959
`devloop` is shutting down.
6060
- `restart = "on_failure"` restarts only after unsuccessful exit.
6161
- `restart = "never"` never restarts automatically.
62-
- Managed child processes default to `RUST_LOG=info` unless the process
63-
config explicitly sets `env.RUST_LOG`.
62+
- Managed child processes inherit the ambient environment unless the
63+
process config explicitly overrides individual variables such as
64+
`env.RUST_LOG`.
6465

6566
Liveness probes are checked on the configured interval while the process
6667
is running. If a liveness probe fails and the restart policy allows it,
@@ -90,8 +91,11 @@ Hooks are one-shot commands executed inside workflows.
9091
- Child stdout is forwarded to `devloop` stdout.
9192
- Child stderr is forwarded to `devloop` stderr.
9293
- `devloop` engine and process logs are emitted through `tracing`.
93-
- Managed-process and hook output is source-labeled with the configured
94-
name and executable.
94+
- Managed-process and hook output is source-labeled as
95+
`[executable process-name]`.
96+
- Internal `devloop` and dependency logs are grouped under
97+
`[devloop ...]` labels so the emitting supervisor remains visible
98+
first.
9599
- When output color is enabled, labels are colorized per source.
96100

97101
### Color rules

src/main.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use tracing_subscriber::registry::LookupSpan;
1717

1818
use crate::config::Config;
1919
use crate::engine::Engine;
20-
use crate::output::{format_output_prefix, normalize_source_label, should_colorize_output};
20+
use crate::output::{format_output_prefix, normalize_internal_log_label, should_colorize_output};
2121

2222
#[derive(Debug, Parser)]
2323
#[command(
@@ -93,7 +93,7 @@ where
9393
event: &Event<'_>,
9494
) -> std::fmt::Result {
9595
let metadata = event.metadata();
96-
let label = normalize_source_label(metadata.target());
96+
let label = normalize_internal_log_label(metadata.target());
9797
writer.write_str(&format_output_prefix(&label, should_colorize_output()))?;
9898
write!(writer, "{} ", metadata.level())?;
9999
self.timer.format_time(&mut writer)?;
@@ -122,7 +122,9 @@ fn resolve_config_path(config: Option<PathBuf>) -> Result<PathBuf> {
122122
#[cfg(test)]
123123
mod tests {
124124
use super::default_rust_log;
125-
use crate::output::{format_output_prefix, normalize_source_label};
125+
use crate::output::{
126+
format_output_prefix, normalize_internal_log_label, normalize_source_label,
127+
};
126128
use std::sync::{Mutex, OnceLock};
127129

128130
fn rust_log_lock() -> &'static Mutex<()> {
@@ -158,4 +160,15 @@ mod tests {
158160
"[devloop processes] "
159161
);
160162
}
163+
164+
#[test]
165+
fn tracing_prefix_wraps_dependency_targets_under_devloop() {
166+
assert_eq!(
167+
format_output_prefix(
168+
&normalize_internal_log_label("hyper_util::client::legacy::connect::http"),
169+
false
170+
),
171+
"[devloop hyper_util client legacy connect http] "
172+
);
173+
}
161174
}

src/output.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ pub(crate) fn normalize_source_label(source_label: &str) -> String {
77
source_label.replace("::", " ")
88
}
99

10+
pub(crate) fn normalize_internal_log_label(source_label: &str) -> String {
11+
let normalized = normalize_source_label(source_label);
12+
if normalized.starts_with("devloop ") {
13+
normalized
14+
} else {
15+
format!("devloop {normalized}")
16+
}
17+
}
18+
1019
pub(crate) fn format_output_prefix(source_label: &str, colorize: bool) -> String {
1120
format_output_prefix_with_style(source_label, colorize, OutputBodyStyle::Plain)
1221
}
@@ -68,8 +77,9 @@ fn colorize_label(source_label: &str, body_style: OutputBodyStyle) -> String {
6877
#[cfg(test)]
6978
mod tests {
7079
use super::{
71-
dim_start, format_output_prefix, format_output_prefix_with_style, normalize_source_label,
72-
output_color_code, style_output_text, style_reset,
80+
dim_start, format_output_prefix, format_output_prefix_with_style,
81+
normalize_internal_log_label, normalize_source_label, output_color_code, style_output_text,
82+
style_reset,
7383
};
7484
use crate::config::OutputBodyStyle;
7585

@@ -81,6 +91,22 @@ mod tests {
8191
);
8292
}
8393

94+
#[test]
95+
fn normalize_internal_log_label_prefixes_non_devloop_targets() {
96+
assert_eq!(
97+
normalize_internal_log_label("hyper_util::client::legacy::connect::http"),
98+
"devloop hyper_util client legacy connect http"
99+
);
100+
}
101+
102+
#[test]
103+
fn normalize_internal_log_label_keeps_devloop_targets_stable() {
104+
assert_eq!(
105+
normalize_internal_log_label("devloop::engine"),
106+
"devloop engine"
107+
);
108+
}
109+
84110
#[test]
85111
fn format_output_prefix_falls_back_without_color() {
86112
assert_eq!(

src/processes.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ fn process_output_source_label(name: &str, command: &[String]) -> String {
724724
.first()
725725
.map(|program| executable_display_name(program))
726726
.unwrap_or_else(|| "unknown".to_owned());
727-
format!("{name} {executable}")
727+
format!("{executable} {name}")
728728
}
729729

730730
fn executable_display_name(program: &str) -> String {
@@ -752,10 +752,6 @@ fn configure_command(
752752
let mut cmd = Command::new(program);
753753
cmd.args(&command[1..]);
754754
cmd.current_dir(cwd);
755-
cmd.env_remove("RUST_LOG");
756-
if !env.contains_key("RUST_LOG") {
757-
cmd.env("RUST_LOG", "info");
758-
}
759755
cmd.envs(env);
760756
cmd.env("DEVLOOP_ROOT", root);
761757
cmd.env("DEVLOOP_STATE", state_path);
@@ -1237,7 +1233,7 @@ mod tests {
12371233

12381234
write_captured_output_to_writer(
12391235
&writer,
1240-
"build_css build-css.sh",
1236+
"build-css.sh build_css",
12411237
b"Done in 73ms\n",
12421238
OutputBodyStyle::Dim,
12431239
)
@@ -1252,7 +1248,7 @@ mod tests {
12521248
.await
12531249
.expect("read rendered output");
12541250

1255-
assert!(rendered.contains("[build_css build-css.sh]"));
1251+
assert!(rendered.contains("[build-css.sh build_css]"));
12561252
if should_colorize_output() {
12571253
assert!(rendered.contains("\u{1b}[2mDone in 73ms\u{1b}[0m"));
12581254
} else {
@@ -1261,13 +1257,13 @@ mod tests {
12611257
}
12621258

12631259
#[test]
1264-
fn process_output_source_label_uses_process_name_and_executable() {
1260+
fn process_output_source_label_uses_executable_before_process_name() {
12651261
let label = process_output_source_label(
12661262
"build_css",
12671263
&["./scripts/build-css.sh".into(), "--watch".into()],
12681264
);
12691265

1270-
assert_eq!(label, "build_css build-css.sh");
1266+
assert_eq!(label, "build-css.sh build_css");
12711267
}
12721268

12731269
#[test]
@@ -1276,7 +1272,7 @@ mod tests {
12761272
}
12771273

12781274
#[test]
1279-
fn configure_command_defaults_child_rust_log_to_info() {
1275+
fn configure_command_inherits_parent_rust_log_by_default() {
12801276
let original = std::env::var_os("RUST_LOG");
12811277
unsafe {
12821278
std::env::set_var("RUST_LOG", "debug");
@@ -1296,11 +1292,12 @@ mod tests {
12961292
let rust_log = command
12971293
.as_std()
12981294
.get_envs()
1299-
.find(|(key, _)| *key == std::ffi::OsStr::new("RUST_LOG"))
1300-
.and_then(|(_, value)| value)
1301-
.expect("RUST_LOG entry should be set");
1295+
.find(|(key, _)| *key == std::ffi::OsStr::new("RUST_LOG"));
13021296

1303-
assert_eq!(rust_log, "info");
1297+
assert!(
1298+
rust_log.is_none(),
1299+
"RUST_LOG should not be overridden in child env"
1300+
);
13041301

13051302
restore_rust_log(original);
13061303
}

0 commit comments

Comments
 (0)