Files
khadhroony-solana-project/crates/ksp-logging-lib/USAGE.md
2026-08-15 19:58:18 +02:00

6.8 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.

Contrat des outputs

LoggingSettings possède :

  • le default_filter global de takeover KSP ;
  • les TargetFilter globaux ;
  • la politique SpanEvents ;
  • une console déclarée optionnellement ;
  • zéro, un ou plusieurs FileSettings.

Chaque output possède un OutputFilter indépendant. Son niveau, ses targets et ses domains s'appliquent en plus de la politique globale. * signifie « tous » dans la dimension concernée.

Les formats publics sont :

Human
Compact
Pretty
Json

Une sortie fichier possède un output_id stable et unique dans LoggingSettings. Cet identifiant appartient au runtime Logging et ne doit pas être confondu avec les file_id utilisés par ksp-config-lib pour identifier ses documents.

Les fichiers persistants interdisent ansi = true.

Compatibilité runtime de pre.004

0.1.3-pre.004 étend d'abord le contrat public. Le runtime multi-sink/routing est livré séparément en pre.005.

Jusqu'à cette tranche suivante, initialize/reinitialize refusent explicitement toute capacité nouvellement représentée qu'ils ne savent pas encore appliquer : plusieurs fichiers actifs, format non Human, ANSI console ou filtre propre à un output. Ces valeurs ne sont donc jamais acceptées puis ignorées silencieusement.

Les helpers ConsoleSettings::stdout() et ConsoleSettings::stderr() construisent une console compatible avec le runtime actuel : activée, Human, sans ANSI et sans filtre supplémentaire par rapport au takeover global.

Initialisation compatible avec le runtime actuel

let file = ksp_logging_lib::FileSettings::new(
    "file.worker",
    true,
    "logs",
    "worker.log",
    ksp_logging_lib::FileRotation::Daily,
    ksp_logging_lib::LogFormat::Human,
    ksp_logging_lib::OutputFilter::unrestricted(),
);

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::vec![file],
);

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 output actif est valide et installe une infrastructure initialement silencieuse qui pourra être activée plus tard par hot reload.

Construction d'un contrat multi-output

Le contrat public peut déjà représenter la future configuration Config complète :

let console = ksp_logging_lib::ConsoleSettings::new(
    true,
    ksp_logging_lib::ConsoleOutput::Stderr,
    true,
    ksp_logging_lib::LogFormat::Compact,
    ksp_logging_lib::OutputFilter::new(
        ksp_logging_lib::LogFilterLevel::Debug,
        std::vec!["*".to_string()],
        std::vec!["*".to_string()],
    ),
);

let error_file = ksp_logging_lib::FileSettings::new(
    "file.config.error",
    true,
    "logs/config",
    "error.jsonl",
    ksp_logging_lib::FileRotation::Daily,
    ksp_logging_lib::LogFormat::Json,
    ksp_logging_lib::OutputFilter::new(
        ksp_logging_lib::LogFilterLevel::Error,
        std::vec!["ksp-config-lib".to_string()],
        std::vec!["config".to_string()],
    ),
);

let settings = ksp_logging_lib::LoggingSettings::new(
    ksp_logging_lib::LogFilterLevel::Warn,
    ksp_logging_lib::SpanEvents::NewAndClose,
    std::option::Option::Some(console),
    std::vec![error_file],
);

let validation = settings.validate();

validate() vérifie le contrat structurel. L'activation runtime de ce routage enrichi appartient à pre.005.

Hot reload

Une configuration compatible avec les capacités runtime actives 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::vec::Vec::new(),
)
.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 compteurs restent encore agrégés console/fichier pendant pre.004 ; leur généralisation par output appartient au runtime multi-sink :

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. 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.