Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions codex-rs/core/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,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",
Expand Down
26 changes: 23 additions & 3 deletions codex-rs/core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 9 additions & 1 deletion codex-rs/core/src/config_loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
24 changes: 24 additions & 0 deletions codex-rs/core/src/config_loader/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/tui-stream-chunking-validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Loading