diff --git a/editoast/editoast_models/src/tables.rs b/editoast/editoast_models/src/tables.rs index db0a4992c0d..b5f50f3d3ed 100644 --- a/editoast/editoast_models/src/tables.rs +++ b/editoast/editoast_models/src/tables.rs @@ -596,6 +596,18 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use postgis_diesel::sql_types::*; + + stdcm_logs (id) { + id -> Int8, + trace_id -> Text, + request -> Jsonb, + response -> Nullable, + } +} + diesel::table! { use diesel::sql_types::*; use postgis_diesel::sql_types::*; @@ -851,6 +863,7 @@ diesel::allow_tables_to_appear_in_same_query!( search_signal, search_study, search_track, + stdcm_logs, stdcm_search_environment, study, temporary_speed_limit, diff --git a/editoast/migrations/2024-11-21-145205_create_stdcm_logs/down.sql b/editoast/migrations/2024-11-21-145205_create_stdcm_logs/down.sql new file mode 100644 index 00000000000..83466c32b52 --- /dev/null +++ b/editoast/migrations/2024-11-21-145205_create_stdcm_logs/down.sql @@ -0,0 +1 @@ +DROP TABLE stdcm_logs; diff --git a/editoast/migrations/2024-11-21-145205_create_stdcm_logs/up.sql b/editoast/migrations/2024-11-21-145205_create_stdcm_logs/up.sql new file mode 100644 index 00000000000..9eb486088b3 --- /dev/null +++ b/editoast/migrations/2024-11-21-145205_create_stdcm_logs/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE stdcm_logs ( + id int8 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + trace_id text UNIQUE NOT NULL, + request jsonb NOT NULL, + response jsonb NULL +); diff --git a/editoast/src/core/conflict_detection.rs b/editoast/src/core/conflict_detection.rs index c50fdc4ba10..45689d7f083 100644 --- a/editoast/src/core/conflict_detection.rs +++ b/editoast/src/core/conflict_detection.rs @@ -29,7 +29,7 @@ pub struct ConflictDetectionRequest { pub work_schedules: Option, } -#[derive(Debug, Clone, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TrainRequirements { pub start_time: DateTime, pub spacing_requirements: Vec, diff --git a/editoast/src/core/simulation.rs b/editoast/src/core/simulation.rs index a9a61ad2811..86a34fedf64 100644 --- a/editoast/src/core/simulation.rs +++ b/editoast/src/core/simulation.rs @@ -34,7 +34,7 @@ editoast_common::schemas! { SimulationResponse, } -#[derive(Debug, Serialize, Derivative)] +#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] #[derivative(Hash)] pub struct PhysicsConsist { pub effort_curves: EffortCurves, diff --git a/editoast/src/core/stdcm.rs b/editoast/src/core/stdcm.rs index 19703288fee..bd522530439 100644 --- a/editoast/src/core/stdcm.rs +++ b/editoast/src/core/stdcm.rs @@ -19,7 +19,7 @@ use super::simulation::SimulationResponse; use crate::core::AsCoreRequest; use crate::core::Json; -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Request { /// Infrastructure id pub infra: i64, @@ -61,7 +61,7 @@ pub struct Request { pub temporary_speed_limits: Vec, } -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PathItem { /// The track offsets of the path item pub locations: Vec, @@ -72,7 +72,7 @@ pub struct PathItem { } /// Contains the data of a step timing, when it is specified -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct StepTimingData { /// Time the train should arrive at this point pub arrival_time: DateTime, @@ -83,7 +83,7 @@ pub struct StepTimingData { } /// Lighter description of a work schedule, with only the relevant information for core -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct WorkSchedule { /// Start time as a time delta from the stdcm start time in ms pub start_time: u64, @@ -94,7 +94,7 @@ pub struct WorkSchedule { } /// Lighter description of a work schedule with only the relevant information for core -#[derive(Debug, Serialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct TemporarySpeedLimit { /// Speed limitation in m/s pub speed_limit: f64, diff --git a/editoast/src/models/mod.rs b/editoast/src/models/mod.rs index f8b1b28148a..28a96bedf55 100644 --- a/editoast/src/models/mod.rs +++ b/editoast/src/models/mod.rs @@ -6,6 +6,7 @@ pub mod fixtures; pub mod infra; pub mod infra_objects; pub mod layers; +pub mod stdcm_log; // We allow unused until models is moved to a separate crate pub mod auth; pub mod pagination; @@ -41,6 +42,7 @@ editoast_common::schemas! { infra::schemas(), projects::schemas(), rolling_stock_model::schemas(), + stdcm_log::schemas(), } #[cfg(test)] diff --git a/editoast/src/models/stdcm_log.rs b/editoast/src/models/stdcm_log.rs new file mode 100644 index 00000000000..1f83008142e --- /dev/null +++ b/editoast/src/models/stdcm_log.rs @@ -0,0 +1,22 @@ +use editoast_derive::Model; +use serde::Deserialize; +use serde::Serialize; +use utoipa::ToSchema; + +use crate::core::stdcm::Request; +use crate::core::stdcm::Response; + +editoast_common::schemas! { + StdcmLog, +} + +#[derive(Clone, Debug, Serialize, Deserialize, Model, ToSchema)] +#[model(table = editoast_models::tables::stdcm_logs)] +#[model(gen(ops = cru, list))] +pub struct StdcmLog { + pub id: i64, + pub trace_id: String, + #[model(json)] + pub request: Request, + pub response: Option>, +} diff --git a/editoast/src/views/timetable/stdcm.rs b/editoast/src/views/timetable/stdcm.rs index a906dc57990..9c4c2d160e4 100644 --- a/editoast/src/views/timetable/stdcm.rs +++ b/editoast/src/views/timetable/stdcm.rs @@ -17,6 +17,7 @@ use editoast_schemas::train_schedule::Margins; use editoast_schemas::train_schedule::ReceptionSignal; use editoast_schemas::train_schedule::ScheduleItem; use failure_handler::SimulationFailureHandler; +use opentelemetry::trace::TraceContextExt; use request::convert_steps; use request::Request; use serde::Deserialize; @@ -24,6 +25,8 @@ use serde::Serialize; use std::collections::HashMap; use std::sync::Arc; use thiserror::Error; +use tracing::Instrument; +use tracing_opentelemetry::OpenTelemetrySpanExt; use utoipa::IntoParams; use utoipa::ToSchema; @@ -36,6 +39,7 @@ use crate::core::simulation::{RoutingRequirement, SimulationResponse, SpacingReq use crate::core::AsCoreRequest; use crate::core::CoreClient; use crate::error::Result; +use crate::models::stdcm_log::StdcmLog; use crate::models::timetable::TimetableWithTrains; use crate::models::train_schedule::TrainSchedule; use crate::models::Infra; @@ -46,10 +50,13 @@ use crate::views::train_schedule::train_simulation_batch; use crate::views::AuthenticationExt; use crate::views::AuthorizationError; use crate::AppState; +use crate::Model; use crate::Retrieve; use crate::RetrieveBatch; use crate::ValkeyClient; +use super::Create; + editoast_common::schemas! { request::schemas(), } @@ -135,7 +142,7 @@ async fn stdcm( } let db_pool = app_state.db_pool_v2.clone(); - let conn = &mut db_pool.get().await?; + let conn = &mut db_pool.clone().get().await?; let valkey_client = app_state.valkey.clone(); let core_client = app_state.core_client.clone(); @@ -251,7 +258,34 @@ async fn stdcm( let stdcm_response = stdcm_request.fetch(core_client.as_ref()).await?; - // 6. Handle STDCM Core Response + // 6. Check if the current tracing level is debug or lower, and if so, log STDCM request and response + if tracing::Level::DEBUG <= tracing::level_filters::LevelFilter::current() { + let stdcm_response_for_spawn = stdcm_response.clone(); + let _ = tokio::spawn( + async move { + match &mut db_pool.get().await { + Ok(conn) => { + let trace_id = tracing::Span::current() + .context() + .span() + .span_context() + .trace_id(); + let _ = StdcmLog::changeset() + .trace_id(trace_id.to_string()) + .request(stdcm_request) + .response(Some(diesel_json::Json(stdcm_response_for_spawn))) + .create(conn) + .await; + } + Err(_) => tracing::warn!("Database connection failed during log operation."), + } + } + .in_current_span(), + ) + .await; + } + + // 7. Handle STDCM Core Response match stdcm_response { crate::core::stdcm::Response::Success { simulation,