mirror of
https://github.com/katanemo/plano.git
synced 2026-05-24 14:05:14 +02:00
add dynamic log config
This commit is contained in:
parent
f019f05738
commit
f04c0b7cdd
9 changed files with 634 additions and 5 deletions
|
|
@ -11,7 +11,7 @@ use brightstaff::router::orchestrator::OrchestratorService;
|
|||
use brightstaff::state::memory::MemoryConversationalStorage;
|
||||
use brightstaff::state::postgresql::PostgreSQLConversationStorage;
|
||||
use brightstaff::state::StateStorage;
|
||||
use brightstaff::tracing::init_tracer;
|
||||
use brightstaff::tracing::{get_log_level, init_tracer, set_log_level};
|
||||
use bytes::Bytes;
|
||||
use common::configuration::{
|
||||
Agent, Configuration, FilterPipeline, ListenerType, ResolvedFilterChain,
|
||||
|
|
@ -384,6 +384,59 @@ async fn init_state_storage(
|
|||
Ok(Some(storage))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Admin handlers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
use http_body_util::BodyExt;
|
||||
|
||||
fn json_response(
|
||||
status: StatusCode,
|
||||
body: &str,
|
||||
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
||||
let bytes = Bytes::from(body.to_string());
|
||||
let body = http_body_util::Full::new(bytes)
|
||||
.map_err(|never| match never {})
|
||||
.boxed();
|
||||
let mut resp = Response::new(body);
|
||||
*resp.status_mut() = status;
|
||||
resp.headers_mut()
|
||||
.insert("Content-Type", HeaderValue::from_static("application/json"));
|
||||
Ok(resp)
|
||||
}
|
||||
|
||||
async fn handle_get_log_level() -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
||||
match get_log_level() {
|
||||
Some(level) => json_response(StatusCode::OK, &format!("{{\"level\":\"{level}\"}}")),
|
||||
None => json_response(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"{\"error\":\"tracer not initialized\"}",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_set_log_level(
|
||||
req: Request<Incoming>,
|
||||
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
|
||||
let body = req.collect().await.map_err(|_| ()).unwrap();
|
||||
let new_level = String::from_utf8_lossy(&body.to_bytes()).trim().to_string();
|
||||
|
||||
if new_level.is_empty() {
|
||||
return json_response(
|
||||
StatusCode::BAD_REQUEST,
|
||||
"{\"error\":\"body must contain a log level filter, e.g. 'debug'\"}",
|
||||
);
|
||||
}
|
||||
|
||||
match set_log_level(&new_level) {
|
||||
Ok(()) => {
|
||||
info!(level = %new_level, "log level updated");
|
||||
json_response(StatusCode::OK, &format!("{{\"level\":\"{new_level}\"}}"))
|
||||
}
|
||||
Err(e) => json_response(StatusCode::BAD_REQUEST, &format!("{{\"error\":\"{e}\"}}")),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Request routing
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -426,6 +479,13 @@ async fn route(
|
|||
}
|
||||
}
|
||||
|
||||
// --- Admin routes ---
|
||||
match (req.method(), path.as_str()) {
|
||||
(&Method::GET, "/admin/log-level") => return handle_get_log_level().await,
|
||||
(&Method::PUT, "/admin/log-level") => return handle_set_log_level(req).await,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// --- Standard routes ---
|
||||
match (req.method(), path.as_str()) {
|
||||
(&Method::POST, CHAT_COMPLETIONS_PATH | MESSAGES_PATH | OPENAI_RESPONSES_API_PATH) => {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,38 @@ use tracing::{Event, Subscriber};
|
|||
use tracing_subscriber::fmt::{format, time::FormatTime, FmtContext, FormatEvent, FormatFields};
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::registry::LookupSpan;
|
||||
use tracing_subscriber::reload;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
use super::ServiceNameOverrideExporter;
|
||||
use common::configuration::Tracing;
|
||||
|
||||
type ReloadHandle = reload::Handle<EnvFilter, tracing_subscriber::Registry>;
|
||||
|
||||
static LOG_LEVEL_HANDLE: OnceLock<ReloadHandle> = OnceLock::new();
|
||||
|
||||
/// Dynamically change the log level filter at runtime.
|
||||
///
|
||||
/// Accepts any valid `RUST_LOG` / `EnvFilter` syntax, e.g. `"debug"`,
|
||||
/// `"brightstaff=trace,info"`.
|
||||
pub fn set_log_level(new_filter: &str) -> Result<(), String> {
|
||||
let handle = LOG_LEVEL_HANDLE
|
||||
.get()
|
||||
.ok_or_else(|| "tracer not initialized".to_string())?;
|
||||
let filter = EnvFilter::try_new(new_filter)
|
||||
.map_err(|e| format!("invalid filter '{new_filter}': {e}"))?;
|
||||
handle
|
||||
.reload(filter)
|
||||
.map_err(|e| format!("failed to reload filter: {e}"))
|
||||
}
|
||||
|
||||
/// Returns the current log level filter string, if the tracer is initialized.
|
||||
pub fn get_log_level() -> Option<String> {
|
||||
let handle = LOG_LEVEL_HANDLE.get()?;
|
||||
handle.with_current(|f| f.to_string()).ok()
|
||||
}
|
||||
|
||||
struct BracketedTime;
|
||||
|
||||
impl FormatTime for BracketedTime {
|
||||
|
|
@ -118,9 +144,10 @@ pub fn init_tracer(tracing_config: Option<&Tracing>) -> &'static SdkTracerProvid
|
|||
let telemetry_layer =
|
||||
tracing_opentelemetry::layer().with_tracer(provider.tracer("brightstaff"));
|
||||
|
||||
// Combine the OpenTelemetry layer with fmt layer using the registry
|
||||
let env_filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
let (filter_layer, reload_handle) = reload::Layer::new(env_filter);
|
||||
LOG_LEVEL_HANDLE.set(reload_handle).ok();
|
||||
|
||||
// Create fmt layer with span field formatting enabled (no ANSI to keep fields parseable)
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
|
|
@ -129,8 +156,8 @@ pub fn init_tracer(tracing_config: Option<&Tracing>) -> &'static SdkTracerProvid
|
|||
.with_ansi(false);
|
||||
|
||||
let subscriber = tracing_subscriber::registry()
|
||||
.with(filter_layer)
|
||||
.with(telemetry_layer)
|
||||
.with(env_filter)
|
||||
.with(fmt_layer);
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
|
|
@ -144,6 +171,8 @@ pub fn init_tracer(tracing_config: Option<&Tracing>) -> &'static SdkTracerProvid
|
|||
|
||||
let env_filter =
|
||||
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
let (filter_layer, reload_handle) = reload::Layer::new(env_filter);
|
||||
LOG_LEVEL_HANDLE.set(reload_handle).ok();
|
||||
|
||||
// Create fmt layer with span field formatting enabled (no ANSI to keep fields parseable)
|
||||
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||
|
|
@ -152,7 +181,7 @@ pub fn init_tracer(tracing_config: Option<&Tracing>) -> &'static SdkTracerProvid
|
|||
.with_ansi(false);
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(env_filter)
|
||||
.with(filter_layer)
|
||||
.with(fmt_layer)
|
||||
.init();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub use constants::{
|
|||
error, http, llm, operation_component, routing, signals, OperationNameBuilder,
|
||||
};
|
||||
pub use custom_attributes::collect_custom_trace_attributes;
|
||||
pub use init::init_tracer;
|
||||
pub use init::{get_log_level, init_tracer, set_log_level};
|
||||
pub use service_name_exporter::{ServiceNameOverrideExporter, SERVICE_NAME_OVERRIDE_KEY};
|
||||
|
||||
use opentelemetry::trace::get_active_span;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue