feat(core): add configurable log_dir (#10678)
Adds a top-level `log_dir` config key (defaults to `$CODEX_HOME/log`) so one-off runs can redirect `codex-tui.log` via `-c`, e.g.: codex -c log_dir=./.codex-log Also resolves relative paths in CLI `-c/--config` overrides for `AbsolutePathBuf` values against the effective cwd (when available). Tests: - cargo test -p codex-core
This commit is contained in:
parent
0e8d359da9
commit
cddfd1e675
6 changed files with 67 additions and 5 deletions
|
|
@ -1328,6 +1328,14 @@
|
|||
"description": "System instructions.",
|
||||
"type": "string"
|
||||
},
|
||||
"log_dir": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AbsolutePathBuf"
|
||||
}
|
||||
],
|
||||
"description": "Directory where Codex writes log files, for example `codex-tui.log`. Defaults to `$CODEX_HOME/log`."
|
||||
},
|
||||
"mcp_oauth_callback_port": {
|
||||
"description": "Optional fixed port for the local HTTP callback server used during MCP OAuth login. When unset, Codex will bind to an ephemeral port chosen by the OS.",
|
||||
"format": "uint16",
|
||||
|
|
|
|||
|
|
@ -271,6 +271,9 @@ pub struct Config {
|
|||
/// overridden by the `CODEX_HOME` environment variable).
|
||||
pub codex_home: PathBuf,
|
||||
|
||||
/// Directory where Codex writes log files (defaults to `$CODEX_HOME/log`).
|
||||
pub log_dir: PathBuf,
|
||||
|
||||
/// Settings that govern if and what will be written to `~/.codex/history.jsonl`.
|
||||
pub history: History,
|
||||
|
||||
|
|
@ -896,6 +899,10 @@ pub struct ConfigToml {
|
|||
#[serde(default)]
|
||||
pub history: Option<History>,
|
||||
|
||||
/// Directory where Codex writes log files, for example `codex-tui.log`.
|
||||
/// Defaults to `$CODEX_HOME/log`.
|
||||
pub log_dir: Option<AbsolutePathBuf>,
|
||||
|
||||
/// Optional URI-based file opener. If set, citations to files in the model
|
||||
/// output will be hyperlinked using the specified URI scheme.
|
||||
pub file_opener: Option<UriBasedFileOpener>,
|
||||
|
|
@ -1560,6 +1567,16 @@ impl Config {
|
|||
|
||||
let check_for_update_on_startup = cfg.check_for_update_on_startup.unwrap_or(true);
|
||||
|
||||
let log_dir = cfg
|
||||
.log_dir
|
||||
.as_ref()
|
||||
.map(AbsolutePathBuf::to_path_buf)
|
||||
.unwrap_or_else(|| {
|
||||
let mut p = codex_home.clone();
|
||||
p.push("log");
|
||||
p
|
||||
});
|
||||
|
||||
// Ensure that every field of ConfigRequirements is applied to the final
|
||||
// Config.
|
||||
let ConfigRequirements {
|
||||
|
|
@ -1626,6 +1643,7 @@ impl Config {
|
|||
tool_output_token_limit: cfg.tool_output_token_limit,
|
||||
agent_max_threads,
|
||||
codex_home,
|
||||
log_dir,
|
||||
config_layer_stack,
|
||||
history,
|
||||
ephemeral: ephemeral.unwrap_or_default(),
|
||||
|
|
@ -1816,9 +1834,7 @@ pub fn find_codex_home() -> std::io::Result<PathBuf> {
|
|||
/// Returns the path to the folder where Codex logs are stored. Does not verify
|
||||
/// that the directory exists.
|
||||
pub fn log_dir(cfg: &Config) -> std::io::Result<PathBuf> {
|
||||
let mut p = cfg.codex_home.clone();
|
||||
p.push("log");
|
||||
Ok(p)
|
||||
Ok(cfg.log_dir.clone())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -3842,6 +3858,7 @@ model_verbosity = "high"
|
|||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
codex_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
config_layer_stack: Default::default(),
|
||||
history: History::default(),
|
||||
ephemeral: false,
|
||||
|
|
@ -3927,6 +3944,7 @@ model_verbosity = "high"
|
|||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
codex_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
config_layer_stack: Default::default(),
|
||||
history: History::default(),
|
||||
ephemeral: false,
|
||||
|
|
@ -4027,6 +4045,7 @@ model_verbosity = "high"
|
|||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
codex_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
config_layer_stack: Default::default(),
|
||||
history: History::default(),
|
||||
ephemeral: false,
|
||||
|
|
@ -4113,6 +4132,7 @@ model_verbosity = "high"
|
|||
tool_output_token_limit: None,
|
||||
agent_max_threads: DEFAULT_AGENT_MAX_THREADS,
|
||||
codex_home: fixture.codex_home(),
|
||||
log_dir: fixture.codex_home().join("log"),
|
||||
config_layer_stack: Default::default(),
|
||||
history: History::default(),
|
||||
ephemeral: false,
|
||||
|
|
|
|||
|
|
@ -144,7 +144,15 @@ pub async fn load_config_layers_state(
|
|||
let cli_overrides_layer = if cli_overrides.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(overrides::build_cli_overrides_layer(cli_overrides))
|
||||
let cli_overrides_layer = overrides::build_cli_overrides_layer(cli_overrides);
|
||||
let base_dir = cwd
|
||||
.as_ref()
|
||||
.map(AbsolutePathBuf::as_path)
|
||||
.unwrap_or(codex_home);
|
||||
Some(resolve_relative_paths_in_config_toml(
|
||||
cli_overrides_layer,
|
||||
base_dir,
|
||||
)?)
|
||||
};
|
||||
|
||||
// Include an entry for the "system" config folder, loading its config.toml,
|
||||
|
|
|
|||
|
|
@ -56,6 +56,30 @@ async fn make_config_for_test(
|
|||
.await
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cli_overrides_resolve_relative_paths_against_cwd() -> std::io::Result<()> {
|
||||
let codex_home = tempdir().expect("tempdir");
|
||||
let cwd_dir = tempdir().expect("tempdir");
|
||||
let cwd_path = cwd_dir.path().to_path_buf();
|
||||
|
||||
let config = ConfigBuilder::default()
|
||||
.codex_home(codex_home.path().to_path_buf())
|
||||
.cli_overrides(vec![(
|
||||
"log_dir".to_string(),
|
||||
TomlValue::String("run-logs".to_string()),
|
||||
)])
|
||||
.harness_overrides(ConfigOverrides {
|
||||
cwd: Some(cwd_path.clone()),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let expected = AbsolutePathBuf::resolve_path_against_base("run-logs", cwd_path)?;
|
||||
assert_eq!(config.log_dir, expected.to_path_buf());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_config_error_for_invalid_user_config_toml() {
|
||||
let tmp = tempdir().expect("tempdir");
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ cargo test --all-features
|
|||
|
||||
Codex is written in Rust, so it honors the `RUST_LOG` environment variable to configure its logging behavior.
|
||||
|
||||
The TUI defaults to `RUST_LOG=codex_core=info,codex_tui=info,codex_rmcp_client=info` and log messages are written to `~/.codex/log/codex-tui.log`, so you can leave the following running in a separate terminal to monitor log messages as they are written:
|
||||
The TUI defaults to `RUST_LOG=codex_core=info,codex_tui=info,codex_rmcp_client=info` and log messages are written to `~/.codex/log/codex-tui.log` by default. For a single run, you can override the log directory with `-c log_dir=...` (for example, `-c log_dir=./.codex-log`).
|
||||
|
||||
```bash
|
||||
tail -F ~/.codex/log/codex-tui.log
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ RUST_LOG='codex_tui::streaming::commit_tick=trace,codex_tui=info,codex_core=info
|
|||
|
||||
## Log capture process
|
||||
|
||||
Tip: for one-off measurements, run with `-c log_dir=...` to direct logs to a fresh directory and avoid mixing sessions.
|
||||
|
||||
1. Record the current size of `~/.codex/log/codex-tui.log` as a start offset.
|
||||
2. Run an interactive prompt that produces sustained streamed output.
|
||||
3. Stop the run.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue