P7.5: structured JSON logging with request IDs and trace correlation

Convert all unstructured format-string logging (tracing::error!("msg: {}", var))
to structured field format (tracing::error!(error = %e, "msg")) across route
handlers and key rotation. Strip response text bodies from error messages in
scoped key mint/revoke paths to prevent potential PII (key material) from
appearing in logs.

The core structured JSON logging infrastructure (tracing-subscriber JSON layer,
request ID generation via UUIDv7, pod_id from POD_NAME env, telemetry middleware
span with request_id/pod_id/method/path) was already in place from prior work.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-20 08:28:39 -04:00
parent 14852a40ff
commit eb354bc3bb
5 changed files with 27 additions and 27 deletions

View file

@ -356,16 +356,18 @@ async fn create_index_handler(
Ok((_status, _text)) if _status >= 200 && _status < 300 => {
patch_ok.push(address.clone());
}
Ok((status, text)) => {
Ok((status, _text)) => {
tracing::warn!(
"failed to set _miroir_shard filterable on {}: HTTP {} — {}",
address, status, text
node = %address,
status,
"failed to set _miroir_shard filterable"
);
}
Err(e) => {
tracing::warn!(
"failed to set _miroir_shard filterable on {}: {}",
address, e
node = %address,
error = %e,
"failed to set _miroir_shard filterable"
);
}
}
@ -408,7 +410,7 @@ async fn list_indexes_handler(
let client = MeilisearchClient::new(config.node_master_key.clone());
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let (status, text) = client.get_raw(&address.address, "/indexes").await.map_err(|e| {
tracing::error!("list indexes failed: {}", e);
tracing::error!(error = %e, "list indexes failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {
@ -431,7 +433,7 @@ async fn get_index_handler(
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let path = format!("/indexes/{}", index);
let (status, text) = client.get_raw(&address.address, &path).await.map_err(|e| {
tracing::error!("get index failed: {}", e);
tracing::error!(error = %e, "get index failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {
@ -525,12 +527,11 @@ async fn rollback_index_update(
Ok((_status, _text)) if _status >= 200 && _status < 300 => {
tracing::info!(node = %address, "index update rollback succeeded");
}
Ok((status, text)) => {
Ok((status, _text)) => {
tracing::error!(
node = %address,
status,
"index update rollback failed: {}",
text
"index update rollback failed"
);
}
Err(e) => {
@ -622,7 +623,7 @@ async fn get_index_stats_handler(
}
}
Err(e) => {
tracing::warn!("stats fan-out failed for {}: {}", address, e);
tracing::warn!(node = %address, error = %e, "stats fan-out failed");
}
}
}
@ -826,12 +827,11 @@ async fn rollback_settings(
Ok((_status, _text)) if _status >= 200 && _status < 300 => {
tracing::info!(node = %address, "settings rollback succeeded");
}
Ok((status, text)) => {
Ok((status, _text)) => {
tracing::error!(
node = %address,
status,
"settings rollback failed: {}",
text
"settings rollback failed"
);
}
Err(e) => {
@ -854,7 +854,7 @@ async fn get_settings_handler(
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let path = format!("/indexes/{}/settings", index);
let (status, text) = client.get_raw(&address.address, &path).await.map_err(|e| {
tracing::error!("get settings failed: {}", e);
tracing::error!(error = %e, "get settings failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {
@ -872,7 +872,7 @@ async fn get_settings_subpath_handler(
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let path = format!("/indexes/{}/settings/{}", index, subpath);
let (status, text) = client.get_raw(&address.address, &path).await.map_err(|e| {
tracing::error!("get settings subpath failed: {}", e);
tracing::error!(error = %e, "get settings subpath failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {
@ -908,7 +908,7 @@ async fn preflight_handler(
.ok_or_else(|| "Failed to parse numberOfDocuments".into())
})
.map_err(|e| {
tracing::error!("Failed to get index stats: {}", e);
tracing::error!(error = %e, "failed to get index stats");
StatusCode::INTERNAL_SERVER_ERROR
})?;

View file

@ -198,8 +198,8 @@ async fn rollback_key_update(
Ok((_status, _text)) if _status >= 200 && _status < 300 => {
tracing::info!(node = %address, "key rollback succeeded");
}
Ok((status, text)) => {
tracing::error!(node = %address, status, "key rollback failed: {}", text);
Ok((status, _text)) => {
tracing::error!(node = %address, status, "key rollback failed");
}
Err(e) => {
tracing::error!(node = %address, error = %e, "key rollback failed");
@ -270,7 +270,7 @@ async fn list_keys_handler(
let client = MeilisearchClient::new(config.node_master_key.clone());
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let (status, text) = client.get_raw(&address.address, "/keys").await.map_err(|e| {
tracing::error!("list keys failed: {}", e);
tracing::error!(error = %e, "list keys failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {
@ -292,7 +292,7 @@ async fn get_key_handler(
let address = config.nodes.first().ok_or(StatusCode::SERVICE_UNAVAILABLE)?;
let path = format!("/keys/{}", key);
let (status, text) = client.get_raw(&address.address, &path).await.map_err(|e| {
tracing::error!("get key failed: {}", e);
tracing::error!(error = %e, "get key failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;
if status >= 200 && status < 300 {

View file

@ -161,7 +161,7 @@ async fn search_handler(
)
.await
.map_err(|e| {
tracing::error!("Search failed: {}", e);
tracing::error!(error = %e, "search failed");
StatusCode::INTERNAL_SERVER_ERROR
})?;

View file

@ -264,7 +264,7 @@ where
}
}
Err(e) => {
tracing::warn!("Failed to poll node {} for task {}: {:?}", node_id_str, id, e);
tracing::warn!(node = %node_id_str, task = %id, error = ?e, "failed to poll node for task");
// Don't mark as failed on network error - may be transient
all_succeeded = false;
}

View file

@ -271,8 +271,8 @@ async fn mint_scoped_key(
created_uid = resp.get("uid").and_then(|v| v.as_str()).map(String::from);
}
}
Ok((status, text)) => {
errors.push(format!("{}: HTTP {}{}", node.id, status, text));
Ok((status, _text)) => {
errors.push(format!("{}: HTTP {}", node.id, status));
}
Err(e) => {
errors.push(format!("{}: {}", node.id, e));
@ -298,10 +298,10 @@ async fn revoke_previous_key(
for node in &config.nodes {
match client.delete_raw(&node.address, &path).await {
Ok((_status, _text)) if _status >= 200 && _status < 300 => {}
Ok((status, text)) => {
Ok((status, _text)) => {
// 404 is fine — key was already revoked or never existed
if status != 404 {
errors.push(format!("{}: HTTP {}{}", node.id, status, text));
errors.push(format!("{}: HTTP {}", node.id, status));
}
}
Err(e) => {