Rust
Le crate honk-me est un client asynchrone sur Tokio et reqwest avec rustls, donc sans OpenSSL. La feature blocking ajoute un client synchrone pour les scripts et les CLI.
- Paquet
-
honk-mePublié - Code source
- github.com/honk-me/honk-rust · Licence MIT
- Prérequis
- Rust 1.85 ou plus récent.
Installation
Pour du code synchrone : cargo add honk-me --features blocking.
cargo add honk-me
cargo add tokio --features macros,rt-multi-thread # if you don't have a runtime yet Une clé d’ingestion (honk_…) permet à quiconque la détient d’envoyer des messages dans son projet. Gardez-la sur vos serveurs, dans vos tâches et dans les secrets de votre CI, jamais dans un navigateur ni dans une app mobile ou de bureau.
Envoyer un événement
Ou explicitement : Honk::new("https://honk-me.app", key)?. Honk se clone à moindre coût : créez-en un et partagez-le, pour que les connexions restent ouvertes.
use honk_me::Honk;
#[tokio::main]
async fn main() -> honk_me::Result<()> {
let honk = Honk::from_env()?; // HONK_URL, HONK_KEY (+ HONK_SOURCE, HONK_ENVIRONMENT, HONK_CHANNEL)
honk.beep("Backup finished", "nightly pg_dump took 42 s").await?;
Ok(())
} Recette : une demande client sur votre téléphone
Un groupe par demande et votre propre clé d’idempotence : un webhook relancé ne fait jamais vibrer deux fois.
use honk_me::{Category, Message, Priority, Severity};
let msg = Message::new(format!("{} asked for a quote: {}", req.name, req.summary))
.title("New quote request")
.severity(Severity::LOUD)
.priority(Priority::High)
.category(Category::Customers)
.group_key(format!("requests/{}", req.id))
.url(format!("https://shop.example.com/admin/requests/{}", req.id)); // https only, "Open link"
honk.send(&msg, format!("request-{}", req.id).as_str()).await?; L’échelle Honk et les incidents
Chaque helper renvoie un PendingSend : enchaînez d’autres champs, puis attendez-le avec .await.
use honk_me::Priority;
honk.loud("Disk 91% full", "db-1 /var is at 91%")
.group_key("disk/db-1/var")
.priority(Priority::High)
.metadata("used_percent", 91)
.await?;
honk.problem("db/backup", "Backup failed", "pg_dump exited with 1").await?;
honk.recovery("db/backup", "Backup OK", "pg_dump finished").await?; Le client bloquant
Il exécute le client asynchrone sur un runtime privé à un seul thread, avec les mêmes nouvelles tentatives. Ne l’appelez pas depuis un runtime asynchrone.
// Cargo.toml: honk-me = { version = "0.2", features = ["blocking"] }
use honk_me::blocking::Honk;
let honk = Honk::from_env()?;
honk.beep("Backup finished", "nightly pg_dump took 42 s").send()?;
honk.send(&honk_me::Message::new("Imported 1 204 rows"), None)?; Erreurs
Chaque variante d’Error porte un Failure avec le statut, le code, chaque champ invalide et retry_after.
use honk_me::Error;
match honk.send(&msg, "order-1042-failed").await {
Ok(_) => {}
Err(Error::Validation(f)) => tracing::error!(?f.fields, "a bug: don't retry"),
Err(e) if e.is_retryable() => queue.retry_later(e.idempotency_key(), e.retry_after()),
Err(e) => return Err(e.into()),
} Des nouvelles tentatives sans doublon
- Chaque envoi porte une
Idempotency-Key: la vôtre, ou un UUIDv7 généré pour l’occasion. La même clé est réutilisée à chaque nouvelle tentative, et pendant 24 heures Honk répond à une requête rejouée avec l’identifiant d’origine etduplicate: true: une réponse perdue ne crée jamais de second message. - Une nouvelle tentative n’a lieu qu’en cas d’erreur réseau, de délai dépassé, de
429ou de5xx, avec un backoff exponentiel et un jitter complet, jamais avant leRetry-Afterdu serveur. Les autres réponses4xxne donnent lieu à aucune nouvelle tentative : corrigez plutôt la requête. - Chaque tentative expire au bout de 5 secondes, et tout s’arrête au bout de 30 secondes au total. Une attente qui dépasserait cette échéance, comme un quota quotidien remis à zéro à minuit, échoue immédiatement et indique quand réessayer.
- Les champs sont vérifiés localement avant l’envoi, et tous les champs invalides sont signalés en une seule fois.