Route SQLite logs to an explicitly configured log_dir (#55978)

## What changed

- Store `logs_2.sqlite` in `log_dir` when explicitly configured, unless a managed `sqlite_home` requirement applies. Keep other databases in `sqlite_home` and preserve the existing SQLite log location when `log_dir` is unset.
- Initialize and lock the selected log directory only when opening its database, so an unavailable directory does not prevent authentication commands from loading configuration. Share the cleanup lock across configuration clones and allow failed initialization to be retried.
- Leave existing logs in place. Read legacy logs for diagnostic submissions when the current query returns no logs, attaching them as `codex-logs-legacy.log`. Continue retention cleanup and page reclamation for both locations, with independent maintenance locks and filesystem checks.

## Testing

Add coverage for configuration routing and managed requirements, deferred initialization and lock lifetimes, authentication with an unavailable log directory, legacy log attachments, and cleanup of current and legacy databases.

GitOrigin-RevId: 48216694596d738eff1b3c5eef6c4e7ada6d0137
This commit is contained in:
Dmitriy Kovalenko
2026-10-21 19:53:00 +00:00
committed by copyberry
parent 3c4c04b224
commit 17a0e6f63f
27 changed files with 1082 additions and 230 deletions
+58
View File
@@ -132,6 +132,64 @@ fn app_server_emits_json_info_events() -> Result<()> {
Ok(())
}
#[test]
fn app_server_keeps_sqlite_logs_in_overridden_sqlite_home() -> Result<()> {
let home = TempDir::new()?;
let sqlite_home = TempDir::new()?;
std::fs::write(
home.path().join("config.toml"),
"[features]\nplugins = false\n",
)?;
codex_command(home.path())?
.env("CODEX_SQLITE_HOME", sqlite_home.path())
.env(
"CODEX_APP_SERVER_MANAGED_CONFIG_PATH",
home.path().join("managed_config.toml"),
)
.arg("app-server")
.write_stdin("")
.assert()
.success();
assert!(sqlite_home.path().join("state_5.sqlite").is_file());
assert!(std::fs::metadata(sqlite_home.path().join("logs_2.sqlite"))?.len() > 0);
assert!(!home.path().join("logs_2.sqlite").exists());
assert!(!home.path().join("log/logs_2.sqlite").exists());
Ok(())
}
#[cfg(unix)]
#[test]
fn app_server_preserves_the_existing_sqlite_logs_symlink() -> Result<()> {
let home = TempDir::new()?;
let external_logs = TempDir::new()?;
let target = external_logs.path().join("logs_2.sqlite");
let link = home.path().join("logs_2.sqlite");
std::os::unix::fs::symlink(&target, &link)?;
std::fs::write(
home.path().join("config.toml"),
"[features]\nplugins = false\n",
)?;
codex_command(home.path())?
.env_remove("CODEX_SQLITE_HOME")
.env(
"CODEX_APP_SERVER_MANAGED_CONFIG_PATH",
home.path().join("managed_config.toml"),
)
.arg("app-server")
.write_stdin("")
.assert()
.success();
assert_eq!(std::fs::read_link(&link)?, target);
assert!(std::fs::metadata(&target)?.len() > 0);
assert!(home.path().join("state_5.sqlite").is_file());
assert!(!home.path().join("log/logs_2.sqlite").exists());
Ok(())
}
#[test]
fn print_startup_config_preserves_project_login_restrictions() -> Result<()> {
let home = TempDir::new()?;
+2 -2
View File
@@ -223,7 +223,7 @@ fn doctor_reports_failed_database_paths() -> Result<()> {
let sqlite_home = toml::Value::String(sqlite_home.display().to_string());
std::fs::write(
&config_file,
format!("sqlite_home = {sqlite_home}\n{config}"),
format!("sqlite_home = {sqlite_home}\nlog_dir = {sqlite_home}\n{config}"),
)?;
for (snapshot_name, failed_paths) in [
@@ -317,7 +317,7 @@ fn doctor_redacts_failed_database_paths_in_json() -> Result<()> {
let sqlite_home = toml::Value::String(sqlite_home.display().to_string());
std::fs::write(
&config_file,
format!("sqlite_home = {sqlite_home}\n{config}"),
format!("sqlite_home = {sqlite_home}\nlog_dir = {sqlite_home}\n{config}"),
)?;
std::fs::write(sqlite.logs_db_path(), "not a SQLite database")?;
+37
View File
@@ -87,6 +87,43 @@ fn login_with_api_key_reads_stdin_and_writes_auth_json() -> Result<()> {
Ok(())
}
#[test]
fn api_key_auth_commands_work_when_log_dir_is_unavailable() -> Result<()> {
let codex_home = TempDir::new()?;
let log_dir = codex_home.path().join("unavailable-logs");
std::fs::write(&log_dir, "not a directory")?;
std::fs::write(
codex_home.path().join("config.toml"),
format!("cli_auth_credentials_store = \"file\"\nlog_dir = {log_dir:?}\n"),
)?;
codex_command(codex_home.path())?
.args(["login", "--with-api-key"])
.write_stdin("sk-test\n")
.assert()
.success()
.stderr(contains("Warning: failed to create login log directory"));
assert_eq!(
read_auth_json(codex_home.path())?["OPENAI_API_KEY"],
"sk-test"
);
codex_command(codex_home.path())?
.args(["login", "status"])
.assert()
.success()
.stderr(contains("API key"));
codex_command(codex_home.path())?
.arg("logout")
.assert()
.success()
.stderr(contains("Successfully logged out"));
assert!(!codex_home.path().join("auth.json").exists());
Ok(())
}
#[test]
fn login_status_reports_auth_storage_errors() -> Result<()> {
let codex_home = TempDir::new()?;