Files
2026-08-14 20:29:48 +02:00

4.3 KiB

Utilisation de ksp-logging-lib

Target d'une crate consommatrice

Chaque crate KSP comportementale fournit explicitement son target, égal au nom Cargo de la crate :

const LOGGING_TARGET: &str = "ksp-store-lib";

ksp_logging_lib::trace!(
    target: LOGGING_TARGET,
    domain = "store",
    component = "postgres",
    operation = "load_transactions",
    "executing store operation"
);

Les champs domain, component, operation et autres champs structurés sont ajoutés par le caller lorsqu'ils sont utiles ; ils ne remplacent pas le target propriétaire.

Initialisation

initialize installe le subscriber global KSP une seule fois et retourne le LoggingGuard qui doit rester vivant pendant la durée du runtime :

let settings = ksp_logging_lib::LoggingSettings::new(
    ksp_logging_lib::LogFilterLevel::Info,
    ksp_logging_lib::SpanEvents::NewAndClose,
    std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()),
    std::option::Option::Some(ksp_logging_lib::FileSettings::new(
        "logs",
        "worker.log",
        ksp_logging_lib::FileRotation::Daily,
    )),
);

let initialize_result = ksp_logging_lib::initialize(&settings);
let mut logging_guard = match initialize_result {
    std::result::Result::Ok(guard) => guard,
    std::result::Result::Err(error) => return std::result::Result::Err(error),
};

Une configuration sans console ni fichier est valide et installe une infrastructure initialement silencieuse qui pourra être activée plus tard par hot reload.

Hot reload

Une nouvelle configuration peut être appliquée sans redémarrer le processus ou le worker :

let debug_settings = ksp_logging_lib::LoggingSettings::new(
    ksp_logging_lib::LogFilterLevel::Info,
    ksp_logging_lib::SpanEvents::NewAndClose,
    std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()),
    std::option::Option::None,
)
.with_target_filter(ksp_logging_lib::TargetFilter::new(
    "ksp-store-lib",
    ksp_logging_lib::LogFilterLevel::Debug,
));

let reload_result = ksp_logging_lib::reinitialize(&mut logging_guard, &debug_settings);
if let std::result::Result::Err(error) = reload_result {
    return std::result::Result::Err(error);
}

La nouvelle configuration est préparée avant la bascule. Si sa validation ou la création d'un nouveau sink échoue, l'ancienne configuration reste active.

Spans synchrones

let span = ksp_logging_lib::trace_span!(
    target: LOGGING_TARGET,
    "materialize_transaction",
    domain = "store"
);

let output = span.in_scope(|| {
    return materialize_transaction();
});

Avec SpanEvents::NewAndClose, le formatter produit les événements de création/fermeture et les temps busy / idle à la fermeture.

Spans async

Une Future doit être instrumentée avec ksp_logging_lib::instrument ; un guard d'entrée de span ne doit pas être conservé à travers .await :

let span = ksp_logging_lib::trace_span!(
    target: LOGGING_TARGET,
    "fetch_account",
    domain = "transport"
);

let output = ksp_logging_lib::instrument(span, fetch_account()).await;

La future instrumentée entre/sort du span pendant ses polls et lors de son Drop, conformément au contrat de la primitive tracing sous-jacente.

Lignes abandonnées

Les sorties utilisent des queues lossy afin de ne pas appliquer de backpressure au hot path. Les pertes restent observables :

let dropped = logging_guard.dropped_lines();
ksp_logging_lib::warn!(
    target: LOGGING_TARGET,
    console = dropped.console(),
    file = dropped.file(),
    total = dropped.total(),
    "logging queues dropped lines"
);

Instrumentation async et executor

instrument(span, future) accepte une Future standard et ne dépend d'aucun executor particulier :

let span = ksp_logging_lib::trace_span!(target: LOGGING_TARGET, "load_transactions");
let result = ksp_logging_lib::instrument(span, async_operation()).await;

La crate ne requiert pas Tokio en production. Tokio n'est présent qu'en dev-dependency pour valider la surface sur un executor réel, y compris après plusieurs suspensions et sur un runtime multi-thread. Un consumer peut donc utiliser l'executor adapté à son propre contexte sans que Logging lui en impose un.