mirror of
https://github.com/openai/codex.git
synced 2026-11-11 06:31:37 +00:00
feat: add --search to just log (#11995)
Summary - extend the log client to accept an optional `--search` substring filter when querying codex-state logs - propagate the filter through `LogQuery` and apply it in `push_log_filters` via `INSTR(message, ...)` - add an integration test that exercises the new search filtering behavior Testing - Not run (not requested)
This commit is contained in:
1 parent
56cd85cd4b
commit
31d4bfdde0
3 files changed
+66
No files matched your search
@@ -46,6 +46,10 @@ struct Args {
|
|||||||
#[arg(long = "thread-id")]
|
#[arg(long = "thread-id")]
|
||||||
thread_id: Vec<String>,
|
thread_id: Vec<String>,
|
||||||
|
|
||||||
|
/// Substring match against the log message.
|
||||||
|
#[arg(long)]
|
||||||
|
search: Option<String>,
|
||||||
|
|
||||||
/// Include logs that do not have a thread id.
|
/// Include logs that do not have a thread id.
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
threadless: bool,
|
threadless: bool,
|
||||||
@@ -67,6 +71,7 @@ struct LogFilter {
|
|||||||
module_like: Vec<String>,
|
module_like: Vec<String>,
|
||||||
file_like: Vec<String>,
|
file_like: Vec<String>,
|
||||||
thread_ids: Vec<String>,
|
thread_ids: Vec<String>,
|
||||||
|
search: Option<String>,
|
||||||
include_threadless: bool,
|
include_threadless: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +159,7 @@ fn build_filter(args: &Args) -> anyhow::Result<LogFilter> {
|
|||||||
module_like,
|
module_like,
|
||||||
file_like,
|
file_like,
|
||||||
thread_ids,
|
thread_ids,
|
||||||
|
search: args.search.clone(),
|
||||||
include_threadless: args.threadless,
|
include_threadless: args.threadless,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -233,6 +239,7 @@ fn to_log_query(
|
|||||||
module_like: filter.module_like.clone(),
|
module_like: filter.module_like.clone(),
|
||||||
file_like: filter.file_like.clone(),
|
file_like: filter.file_like.clone(),
|
||||||
thread_ids: filter.thread_ids.clone(),
|
thread_ids: filter.thread_ids.clone(),
|
||||||
|
search: filter.search.clone(),
|
||||||
include_threadless: filter.include_threadless,
|
include_threadless: filter.include_threadless,
|
||||||
after_id,
|
after_id,
|
||||||
limit,
|
limit,
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ pub struct LogQuery {
|
|||||||
pub module_like: Vec<String>,
|
pub module_like: Vec<String>,
|
||||||
pub file_like: Vec<String>,
|
pub file_like: Vec<String>,
|
||||||
pub thread_ids: Vec<String>,
|
pub thread_ids: Vec<String>,
|
||||||
|
pub search: Option<String>,
|
||||||
pub include_threadless: bool,
|
pub include_threadless: bool,
|
||||||
pub after_id: Option<i64>,
|
pub after_id: Option<i64>,
|
||||||
pub limit: Option<usize>,
|
pub limit: Option<usize>,
|
||||||
|
|||||||
@@ -711,6 +711,11 @@ fn push_log_filters<'a>(builder: &mut QueryBuilder<'a, Sqlite>, query: &'a LogQu
|
|||||||
if let Some(after_id) = query.after_id {
|
if let Some(after_id) = query.after_id {
|
||||||
builder.push(" AND id > ").push_bind(after_id);
|
builder.push(" AND id > ").push_bind(after_id);
|
||||||
}
|
}
|
||||||
|
if let Some(search) = query.search.as_ref() {
|
||||||
|
builder.push(" AND INSTR(message, ");
|
||||||
|
builder.push_bind(search.as_str());
|
||||||
|
builder.push(") > 0");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_like_filters<'a>(
|
fn push_like_filters<'a>(
|
||||||
@@ -906,6 +911,8 @@ mod tests {
|
|||||||
use super::StateRuntime;
|
use super::StateRuntime;
|
||||||
use super::ThreadMetadata;
|
use super::ThreadMetadata;
|
||||||
use super::state_db_filename;
|
use super::state_db_filename;
|
||||||
|
use crate::LogEntry;
|
||||||
|
use crate::LogQuery;
|
||||||
use crate::STATE_DB_FILENAME;
|
use crate::STATE_DB_FILENAME;
|
||||||
use crate::STATE_DB_VERSION;
|
use crate::STATE_DB_VERSION;
|
||||||
use crate::model::Phase2JobClaimOutcome;
|
use crate::model::Phase2JobClaimOutcome;
|
||||||
@@ -2495,6 +2502,57 @@ VALUES (?, ?, ?, ?, ?)
|
|||||||
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn query_logs_with_search_matches_substring() {
|
||||||
|
let codex_home = unique_temp_dir();
|
||||||
|
let runtime = StateRuntime::init(codex_home.clone(), "test-provider".to_string(), None)
|
||||||
|
.await
|
||||||
|
.expect("initialize runtime");
|
||||||
|
|
||||||
|
runtime
|
||||||
|
.insert_logs(&[
|
||||||
|
LogEntry {
|
||||||
|
ts: 1_700_000_001,
|
||||||
|
ts_nanos: 0,
|
||||||
|
level: "INFO".to_string(),
|
||||||
|
target: "cli".to_string(),
|
||||||
|
message: Some("alpha".to_string()),
|
||||||
|
thread_id: Some("thread-1".to_string()),
|
||||||
|
process_uuid: None,
|
||||||
|
file: Some("main.rs".to_string()),
|
||||||
|
line: Some(42),
|
||||||
|
module_path: None,
|
||||||
|
},
|
||||||
|
LogEntry {
|
||||||
|
ts: 1_700_000_002,
|
||||||
|
ts_nanos: 0,
|
||||||
|
level: "INFO".to_string(),
|
||||||
|
target: "cli".to_string(),
|
||||||
|
message: Some("alphabet".to_string()),
|
||||||
|
thread_id: Some("thread-1".to_string()),
|
||||||
|
process_uuid: None,
|
||||||
|
file: Some("main.rs".to_string()),
|
||||||
|
line: Some(43),
|
||||||
|
module_path: None,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
.await
|
||||||
|
.expect("insert test logs");
|
||||||
|
|
||||||
|
let rows = runtime
|
||||||
|
.query_logs(&LogQuery {
|
||||||
|
search: Some("alphab".to_string()),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.expect("query matching logs");
|
||||||
|
|
||||||
|
assert_eq!(rows.len(), 1);
|
||||||
|
assert_eq!(rows[0].message.as_deref(), Some("alphabet"));
|
||||||
|
|
||||||
|
let _ = tokio::fs::remove_dir_all(codex_home).await;
|
||||||
|
}
|
||||||
|
|
||||||
fn test_thread_metadata(
|
fn test_thread_metadata(
|
||||||
codex_home: &Path,
|
codex_home: &Path,
|
||||||
thread_id: ThreadId,
|
thread_id: ThreadId,
|
||||||
|
|||||||
Reference in new issue
Block a user