saddle-observability 0.3.32

Saddle structured logging and trace correlation
Documentation
use saddle_core::{
    BoundedDiagnostic, BoundedDiagnosticCause, CaptureSite, ContextFact, ContextLabel,
    Diagnostic, DiagnosticCategory, DiagnosticCause, DiagnosticCode, DiagnosticStage,
    RequestLocalFacts, RequestRootPublisher, RequestViewPhase,
};
use saddle_observability::{
    EmergencyDiagnostics, FileLoggingConfig, RootDiagnosticScope, RootOutcomeFacts,
    RootRequestEvent, RootSupervisionReturn, Rotation,
};

#[test]
fn written_receipt_follows_actual_terminal_record() {
    use std::os::unix::fs::PermissionsExt;
    let directory =
        std::env::temp_dir().join(format!("saddle-written-source-{}", std::process::id()));
    std::fs::create_dir_all(&directory).unwrap();
    let existing = directory.join(saddle_observability::EMERGENCY_FILE_NAME);
    std::fs::write(&existing, b"").unwrap();
    std::fs::set_permissions(&existing, std::fs::Permissions::from_mode(0o600)).unwrap();
    let mut writer =
        EmergencyDiagnostics::start(&FileLoggingConfig::new(&directory, Rotation::Daily)).unwrap();
    let handle = writer.handle();
    let root = RequestRootPublisher::create(
        ContextLabel::checked("dispatch-test").unwrap(),
        ContextFact::NotEstablished,
    )
    .unwrap();
    let view = root
        .reference()
        .view(RequestLocalFacts::new(RequestViewPhase::Handler));
    let source = RootDiagnosticScope::new(&view, Some(&handle));
    let error = std::io::Error::new(std::io::ErrorKind::InvalidData, "dispatch original cause");
    let written = source
        .source_error_written(
            &error,
            DiagnosticStage::RequestHandler,
            RootRequestEvent::Handler,
            RootOutcomeFacts::default(),
        )
        .ok()
        .expect("writer must acknowledge complete source");
    let long_error = std::io::Error::other("long original ".repeat(1500));
    let long_written = source.source_error_written(
        &long_error,
        DiagnosticStage::RequestHandler,
        RootRequestEvent::Handler,
        RootOutcomeFacts::default(),
    ).ok().expect("every continuation and terminal must be acknowledged");
    let code = DiagnosticCode::new("test.original_description").unwrap();
    let description = "independent panic payload with private detail";
    let bounded = BoundedDiagnostic::capture(
        DiagnosticCategory::UnexpectedError,
        CaptureSite::FirstObserved,
        BoundedDiagnosticCause::new(DiagnosticStage::RequestHandler, code),
    );
    let bounded_id = bounded.occurrence();
    let description_written = source
        .source_description_written(
            &description,
            bounded,
            code,
            RootRequestEvent::Handler,
            RootOutcomeFacts::default(),
        )
        .ok()
        .expect("description source must be confirmed");
    assert_eq!(
        serde_json::to_value(description_written.occurrence()).unwrap(),
        serde_json::to_value(bounded_id).unwrap()
    );
    let existing = Diagnostic::capture(
        DiagnosticCategory::UnexpectedError,
        CaptureSite::FirstObserved,
        DiagnosticCause::new(DiagnosticStage::RequestHandler, code),
    );
    let existing_id = existing.occurrence();
    let existing_written = source
        .source_existing_error_written(
            &error,
            existing,
            code,
            RootRequestEvent::Handler,
            RootOutcomeFacts::default(),
        )
        .ok()
        .expect("existing source must be confirmed");
    assert_eq!(
        serde_json::to_value(existing_written.occurrence()).unwrap(),
        serde_json::to_value(existing_id).unwrap()
    );
    let rows = std::fs::read_to_string(writer.target()).unwrap();
    assert_eq!(std::fs::metadata(writer.target()).unwrap().permissions().mode() & 0o777, 0o644);
    assert!(rows.contains("dispatch original cause"));
    assert!(rows.contains(description));
    assert!(
        rows.lines()
            .any(|row| row.contains("exposed_chain_complete"))
    );
    let supervised = RootSupervisionReturn::<()>::failed(written).consume().unwrap_err();
    assert_eq!(
        supervised.original_capture(),
        saddle_observability::root_diagnostic::OriginalCaptureState::CompleteWritten
    );
    assert_eq!(long_written.into_source().original_capture(), saddle_observability::root_diagnostic::OriginalCaptureState::CompleteWritten);
    assert_eq!(description_written.into_source().original_capture(), saddle_observability::root_diagnostic::OriginalCaptureState::CompleteWritten);
    assert_eq!(existing_written.into_source().original_capture(), saddle_observability::root_diagnostic::OriginalCaptureState::CompleteWritten);
    let continuations = rows.lines().filter_map(|line| serde_json::from_str::<serde_json::Value>(line).ok())
        .filter(|row| row["state"] == "continuation").count();
    assert!(continuations >= 2, "long original must reuse the confirmation slot across segments");
    drop(handle);
    while writer.shutdown() == saddle_observability::DiagnosticShutdown::Pending {
        std::thread::yield_now();
    }
    drop(writer);
    let fresh = directory.join("fresh");
    let mut new_writer = EmergencyDiagnostics::start(&FileLoggingConfig::new(&fresh, Rotation::Daily)).unwrap();
    let new_handle = new_writer.handle();
    let new_root = RequestRootPublisher::create(ContextLabel::checked("fresh").unwrap(), ContextFact::NotEstablished).unwrap();
    let new_view = new_root.reference().view(RequestLocalFacts::new(RequestViewPhase::Handler));
    let new_error = std::io::Error::other("new file mode");
    let new_written = RootDiagnosticScope::new(&new_view, Some(&new_handle)).source_error_written(
        &new_error, DiagnosticStage::RequestHandler, RootRequestEvent::Handler,
        RootOutcomeFacts::default(),
    ).ok().expect("new file writer confirms source");
    assert_eq!(new_written.into_source().original_capture(), saddle_observability::root_diagnostic::OriginalCaptureState::CompleteWritten);
    assert_eq!(std::fs::metadata(new_writer.target()).unwrap().permissions().mode() & 0o777, 0o644);
    drop(new_handle);
    while new_writer.shutdown() == saddle_observability::DiagnosticShutdown::Pending {
        std::thread::yield_now();
    }
    std::fs::remove_dir_all(directory).unwrap();
}

#[test]
fn missing_output_cannot_issue_written_receipt() {
    let root = RequestRootPublisher::create(
        ContextLabel::checked("dispatch-test").unwrap(),
        ContextFact::NotEstablished,
    )
    .unwrap();
    let view = root
        .reference()
        .view(RequestLocalFacts::new(RequestViewPhase::Handler));
    let scope = RootDiagnosticScope::new(&view, None);
    let error = std::io::Error::other("unavailable output");
    assert!(
        scope
            .source_error_written(
                &error,
                DiagnosticStage::RequestHandler,
                RootRequestEvent::Handler,
                RootOutcomeFacts::default()
            )
            .is_err()
    );
    let code = DiagnosticCode::new("test.original_description").unwrap();
    let bounded = BoundedDiagnostic::capture(
        DiagnosticCategory::UnexpectedError,
        CaptureSite::FirstObserved,
        BoundedDiagnosticCause::new(DiagnosticStage::RequestHandler, code),
    );
    assert!(
        scope
            .source_description_written(
                &"unavailable original",
                bounded,
                code,
                RootRequestEvent::Handler,
                RootOutcomeFacts::default(),
            )
            .is_err()
    );
}