Aller au contenu

Routage par Tags & Tentatives

Les tags Apprise ne font pas que regrouper des services — ils peuvent piloter l’ordre de livraison, les chaînes de secours et le comportement en cas d’échec sans modifier une seule ligne de code applicatif.

Cette page explique comment configurer ces fonctionnalités dans vos fichiers de configuration et comment les contrôler depuis la bibliothèque Python ou la CLI.


Chaque tag dans un fichier de configuration peut porter un préfixe numérique de priorité séparé par deux-points : N:nomdetag.

Les numéros inférieurs signifient une urgence plus haute — ils sont traités en premier.

# Priorité 1 -- canaux d'alerte principaux (urgence maximale)
1:alerts=slack://tokenA/tokenB/tokenC
1:alerts=discord://webhook_id/webhook_token
# Priorité 2 -- canaux secondaires (utilisés uniquement si la priorité 1 échoue entièrement)
2:alerts=telegram://bottoken/chatid
# Priorité 5 -- dernier recours
5:alerts=mailto://user:pass@example.com

Les tags sans préfixe de priorité ont par défaut la priorité 0 (urgence maximale).


Lorsque vous déclenchez un tag sans préfixe de priorité, Apprise utilise la chaîne d’escalade :

  1. Les services sont regroupés par priorité de tag configurée.
  2. Le groupe au numéro le plus bas (urgence la plus haute) s’exécute en premier.
  3. Si chaque service de ce groupe réussit, Apprise retourne True immédiatement — les groupes de numéros supérieurs ne sont jamais déclenchés.
  4. Si un service du groupe échoue, Apprise escalade vers le niveau de priorité suivant.
# Déclencher tous les services 'alerts' avec la chaîne d'escalade.
# Les entrées de priorité 1 s'exécutent en premier. Si elles réussissent toutes,
# les entrées de priorité 5 ne sont jamais déclenchées.
apobj.notify(body="Utilisation disque à 90%", tag="alerts")
Fenêtre de terminal
# Équivalent CLI
apprise --config=config.yml --tag="alerts" --body="Utilisation disque à 90%"

Tous les services partageant le même nom de tag et le même numéro de priorité sont traités comme un seul groupe. Le groupe est considéré comme réussi uniquement si chaque service qu’il contient réussit. Si un seul service échoue, Apprise escalade immédiatement vers le groupe au numéro supérieur suivant.

Les tags sans préfixe numérique ont par défaut la priorité 0 — le niveau d’urgence le plus élevé. Cela signifie qu’il est possible de mélanger des versions simples et préfixées d’un même tag pour créer une chaîne d’escalade sans modifier les entrées d’URL existantes :

# Priorité 0 (par défaut, urgence maximale) -- ces deux entrées forment le premier groupe
abc=slack://tokenA/tokenB/tokenC
abc=discord://webhook_id/webhook_token
# Priorité 1 -- secours si le groupe de priorité 0 échoue
1:abc=telegram://bottoken/chatid

Lorsque notify(tag="abc") est appelé avec la configuration ci-dessus :

  1. Le groupe de priorité 0 (Slack + Discord) s’exécute en premier.
  2. Si les deux réussissent, True est retourné — Telegram n’est jamais contacté.
  3. Si l’un ou l’autre échoue, Apprise escalade vers le groupe de priorité 1 (Telegram).

Les tags sans préfixe ayant par défaut la priorité 0, les définitions abc et 0:abc sont entièrement équivalentes dans une configuration de service. Toutes deux placent le service dans le groupe de priorité 0, et un filtre "abc" comme un filtre "0:abc" correspondent aux services définis avec l’une ou l’autre forme. Si les deux formes figurent dans le même fichier de configuration, ces services seront toujours distribués ensemble comme un groupe unique.

Lorsque vous spécifiez un préfixe de priorité dans le filtre, Apprise ignore la chaîne d’escalade et ne notifie que les services dont le tag correspondant porte exactement cette priorité.

# Déclencher UNIQUEMENT les entrées 'alerts' configurées avec la priorité 2.
# Les entrées de priorité 1 et 5 ne sont pas affectées.
apobj.notify(body="Fenêtre de maintenance planifiée", tag="2:alerts")
Fenêtre de terminal
# Équivalent CLI
apprise --config=config.yml --tag="2:alerts" --body="Fenêtre de maintenance planifiée"
Valeur du filtreComportement
"alerts"Chaîne d’escalade — priorité la plus haute (numéro le plus bas) d’abord
"2:alerts"Exclusif — uniquement les entrées alerts de priorité 2
"0:alerts"Exclusif — uniquement les entrées de priorité 0 (alerts et 0:alerts sont identiques)

Quand notify() execute plusieurs chaines OR independantes en parallele, le comportement par defaut est de laisser chaque chaine se terminer avant de retourner — meme si une chaine a deja echoue. Cela garantit que chaque service configure obtient au moins une tentative.

Pour arreter immediatement tous les rounds d’escalade restants des qu’une chaine epuise ses groupes de priorite sans succes, definissez abort_on_chain_failure=True sur votre AppriseAsset :

from apprise import Apprise, AppriseAsset
asset = AppriseAsset(abort_on_chain_failure=True)
apobj = Apprise(asset=asset)
# ...ajout des services...
# Si la chaine 'ops' echoue completement, Apprise s'arrete immediatement
# et ne lance pas de nouveaux rounds d'escalade pour la chaine 'securite'.
apobj.notify(body="Alerte critique", tag=["ops", "securite"])
abort_on_chain_failureComportement en cas d’echec d’une chaine
False (defaut)Toutes les chaines se terminent ; chaque service est tente
TrueLes rounds restants sont ignores ; False est retourne immediatement

Chaque service peut être configuré avec son propre nombre de tentatives et délai inter-tentative. Ces paramètres sont indépendants de la chaîne d’escalade et s’appliquent à chaque échec de livraison.

  • retry — nombre de tentatives supplémentaires après le premier échec (0 = pas de nouvelle tentative).
  • wait — secondes de pause entre les tentatives (décimales acceptées ; 0.0 = pas de pause).

Ajoutez ?retry=N et/ou ?wait=S directement à l’URL sous forme de paramètres de requête.

# jusqu'à 3 nouvelles tentatives, avec 5 secondes d'attente entre chacune
1:alerts=slack://tokenA/tokenB/tokenC?retry=3&wait=5
# jusqu'à 2 nouvelles tentatives avec 1,5 seconde d'attente
1:alerts=discord://webhook_id/webhook_token?retry=2&wait=1.5
# Canal de secours -- pas besoin de nouvelles tentatives (comportement par défaut)
5:alerts=mailto://user:pass@example.com
ParamètreTypeContraintesPar défaut
retryint0 à 100
waitfloat0.0 à 20.0 ; les entiers sont promus en flottant0.5

Les valeurs invalides (nombres négatifs, chaînes non numériques, inf, nan) sont rejetées et reviennent à la valeur par défaut de l’asset (voir Valeurs par Défaut Globales ci-dessous).


Le paramètre optional marque un service comme “souhaitable mais non indispensable”. Lorsque optional=yes est activé sur un service, un échec de livraison pour ce service est silencieusement absorbé — l’appel global à notify() retourne quand même True, tant que chaque service requis (non optionnel) a réussi.

Ce paramètre est utile pour tout service que vous souhaitez notifier opportunistement : envoyer la notification si le point de terminaison est disponible, mais ne pas considérer son absence comme un échec aux yeux de l’application appelante.

Exemple concret — écrans d’affichage domestiques

Section intitulée « Exemple concret — écrans d’affichage domestiques »

Imaginons que votre système domotique possède quatre écrans Kodi avec le tag media. Vous souhaitez afficher une notification sur les écrans actuellement allumés, mais vous ne voulez pas qu’un écran éteint déclenche une alerte d’astreinte à trois heures du matin.

# Les quatre écrans sont optionnels -- leur indisponibilité n'est jamais une erreur.
media=kodi://192.168.1.10
media=kodi://192.168.1.11
media=kodi://192.168.1.12
media=kodi://192.168.1.13

Pour les rendre optionnels, ajoutez ?optional=yes à chaque URL :

media=kodi://192.168.1.10?optional=yes
media=kodi://192.168.1.11?optional=yes
media=kodi://192.168.1.12?optional=yes
media=kodi://192.168.1.13?optional=yes
# Retourne True même si tous les écrans sont éteints ou inaccessibles.
apobj.notify(body="Le dîner est prêt", tag="media")
Fenêtre de terminal
apprise --config=config.yml --tag="media" --body="Le dîner est prêt"

Le paramètre est par service. Vous pouvez librement mélanger des services optionnels et requis au sein du même groupe de tags. Le résultat global suit ces règles :

Services dans le lotRésultat
Tous requis, tous réussissentTrue
Tous requis, un ou plusieurs échouentFalse
Tous optionnels, tous échouentTrue
Tous optionnels, tous réussissentTrue
Mélange requis/optionnels ; tous les requis réussissentTrue
Mélange requis/optionnels ; au moins un requis échoueFalse

En résumé : les échecs optionnels sont invisibles pour l’appelant. Seul l’échec d’un service requis peut faire retourner False à notify().

Activer optional=yes ne saute pas le service et ne contourne pas la logique de tentatives. La livraison est toujours tentée, et si retry est supérieur à zéro, Apprise retentera le nombre de fois configuré avant d’abandonner. Ce n’est qu’après l’épuisement de toutes les tentatives qu’Apprise vérifie le paramètre optional pour décider d’absorber ou non l’échec.

# Ce service est tenté jusqu'à 4 fois (tentative initiale + 3 nouvelles tentatives)
# avec une pause de 2 secondes entre chacune. Si les quatre tentatives échouent,
# l'échec est silencieusement absorbé grâce à optional=yes.
media=kodi://192.168.1.10?optional=yes&retry=3&wait=2
- kodi://192.168.1.10:
tag: media
optional: yes
retry: 3
wait: 2

Vous pouvez définir ce paramètre directement sur n’importe quel objet service chargé, ou le passer en argument lors de la construction manuelle d’objets service :

import apprise
apobj = apprise.Apprise()
# Option 1 -- paramètre de requête URL (fonctionne avec tous les chargeurs de config)
apobj.add("kodi://192.168.1.10?optional=yes")
# Option 2 -- définir l'attribut directement après chargement
obj = apprise.Apprise.instantiate("kodi://192.168.1.10")
obj.optional = True
apobj.add(obj)
# Option 3 -- argument mot-clé du constructeur
from apprise.plugins.NotifyJSON import NotifyJSON
svc = NotifyJSON(host="192.168.1.10", optional=True)
apobj.add(svc)
# Les trois options produisent le même comportement : les échecs sont silencieusement absorbés.
result = apobj.notify(body="Le dîner est prêt")
print(result) # True même si chaque point de terminaison était inaccessible

Journal de débogage non critique associé à un canal d’alerte requis :

# Requis : l'ingénieur d'astreinte doit toujours être averti.
alerts=slack://tokenA/tokenB/tokenC
# Optionnel : enregistrer dans le canal de diagnostic interne s'il est disponible,
# mais ne jamais considérer son absence comme un échec.
alerts=json://diagnostics.internal/api/events?optional=yes
# Retourne True si Slack a réussi, quel que soit l'état du journal JSON.
apobj.notify(body="Base de données inaccessible", tag="alerts")

Groupe de tags entièrement optionnel — diffusion au mieux :

# Notifier les trois écrans s'ils sont accessibles ; ne jamais échouer s'ils ne le sont pas.
affichages=kodi://192.168.1.10?optional=yes
affichages=kodi://192.168.1.11?optional=yes
affichages=kodi://192.168.1.12?optional=yes
# Retourne toujours True quel que soit le nombre d'écrans ayant répondu.
apobj.notify(body="Mouvement détecté", tag="affichages")

Si vous souhaitez que chaque service d’une session Apprise partage les mêmes valeurs par défaut de tentatives et d’attente sans modifier chaque URL, définissez-les sur AppriseAsset :

from apprise import Apprise, AppriseAsset
asset = AppriseAsset(
default_service_retry=2, # jusqu'à 2 nouvelles tentatives en cas d'échec avant d'abandonner
default_service_wait=3.0, # attendre 3 secondes entre les tentatives
)
apobj = Apprise(asset=asset)
apobj.add("slack://tokenA/tokenB/tokenC")
apobj.add("discord://webhook_id/webhook_token")
apobj.notify(body="Valeurs par défaut retry/wait appliquées à chaque service")

Les valeurs par URL (?retry= / ?wait= ou clés YAML) remplacent les valeurs par défaut de l’asset pour ce service spécifique uniquement. Tous les autres services utilisent toujours les valeurs par défaut de l’asset.


Un :N en fin de valeur de filtre de tag remplace le nombre de tentatives pour chaque service correspondant, pour cet appel uniquement. La configuration permanente du service n’est pas modifiée.

# Réessayer chaque service correspondant jusqu'à 5 fois pour cet appel uniquement
apobj.notify(body="Alerte critique", tag="alerts:5")
# Correspondance exclusive priorité 2 ET jusqu'à 3 tentatives par service
apobj.notify(body="Priorité 2 uniquement", tag="2:alerts:3")
Fenêtre de terminal
# Équivalents CLI
apprise --config=config.yml --tag="alerts:5" --body="Alerte critique"
apprise --config=config.yml --tag="2:alerts:3" --body="Priorité 2 uniquement"

Le suffixe :N suit les mêmes règles de validation que le paramètre URL retry. Sa spécification n’affecte pas wait — chaque service utilise toujours sa valeur d’attente configurée entre les tentatives.

Apprise supporte deux façons de combiner des noms de tags dans un seul appel à notify.

MotifSignificationExemple
OR — plusieurs valeurs de tag séparéesCorrespond aux services portant l’un ou l’autre des tags listésPlusieurs indicateurs --tag dans la CLI ; une liste de chaînes de tags en Python
AND — plusieurs noms de tags dans une seule entrée de filtreCorrespond uniquement aux services portant tous les tags listésUn seul --tag "a, b" dans la CLI ; une liste imbriquée en Python

Avec OR, chaque nom de tag forme une chaîne d’escalade indépendante. Toutes les chaînes doivent trouver un groupe de priorité entièrement réussi avant que notify() retourne True. Un suffixe :N sur un token ne s’applique qu’aux services correspondant à ce token ; les autres ne sont pas affectés.

# OR -- les services devops obtiennent retry=3 ; management obtient retry=2.
# Chaque chaîne d'escalade de tag s'exécute indépendamment.
apobj.notify(body="Alerte équipe", tag=["devops:3", "management:2"])
Fenêtre de terminal
# OR -- plusieurs indicateurs --tag ; chacun est une chaîne indépendante.
apprise --config=config.yml \
--tag="devops:3" \
--tag="management:2" \
--body="Alerte équipe"

Si tous les services devops réussissent dans leur groupe de priorité le plus bas, Apprise envoie quand même la chaîne management — les deux chaînes sont indépendantes l’une de l’autre.

Avec AND, les services doivent porter tous les tags du groupe pour être sélectionnés. Les services correspondant à un filtre AND partagent une seule chaîne d’escalade.

# AND -- le service doit porter "devops" ET "management" pour correspondre.
apobj.notify(body="Alerte combinée", tag=[["devops", "management"]])
Fenêtre de terminal
# AND -- valeurs séparées par des virgules dans un seul indicateur --tag.
apprise --config=config.yml --tag="devops, management" --body="Alerte combinée"

L’exemple suivant combine les trois fonctionnalités : escalade par priorité, tentatives/attente par service, et remplacement du nombre de tentatives par appel.

# --- Canaux principaux (priorité 1) ---
# jusqu'à 2 nouvelles tentatives, 2 secondes d'attente entre chacune
1:alerts=slack://tokenA/tokenB/tokenC?retry=2&wait=2
1:alerts=discord://webhook_id/webhook_token?retry=2&wait=2
# --- Canal secondaire (priorité 5 -- secours) ---
# Une seule tentative suffit ; les canaux principaux ont déjà réessayé
5:alerts=mailto://user:pass@pagerduty.example.com

Les exemples ci-dessous illustrent des schemas de notification courants.

Tag unique avec deux niveaux d’escalade — les valeurs 2 et 50 montrent que les numeros sont arbitraires ; seul l’ordre importe.

# Premier essai (numero inferieur = urgence superieure)
2:alertes=slack://tokenA/tokenB/tokenC
# Secours ; s'execute uniquement si la priorite 2 echoue
50:alertes=mailto://user:pass@example.com
# Chaine d'escalade : Slack d'abord, e-mail seulement en cas d'echec
apobj.notify(body="Alerte", tag="alertes")
Fenêtre de terminal
apprise --config=config.yml --tag="alertes" --body="Alerte"

FonctionnalitéOù le configurerPortée
Priorité de tagN:nomdetag dans le fichier de configuration (TEXT ou YAML)Par définition de service
Nombre de tentatives par serviceParamètre URL ?retry=N ou clé YAML retry:Par définition de service
Délai inter-tentative par serviceParamètre URL ?wait=S ou clé YAML wait:Par définition de service
Service optionnelParamètre URL ?optional=yes ou clé YAML optional: yesPar définition de service
Valeurs par défaut globalesAppriseAsset(default_service_retry=N, default_service_wait=S)Tous les services de la session
Envoi en escaladenotify(tag="nomdetag") ou --tag nomdetag (sans préfixe de priorité)Un appel notify() / CLI
Envoi exclusifnotify(tag="N:nomdetag") ou --tag N:nomdetagUn appel notify() / CLI
Remplacement des tentativesnotify(tag="nomdetag:N") ou --tag nomdetag:NUn appel notify() / CLI
Envoi OR multi-tagsnotify(tag=["tagA", "tagB"]) ou --tag tagA --tag tagBUn appel notify() / CLI
Envoi AND multi-tagsnotify(tag=[["tagA", "tagB"]]) ou --tag "tagA, tagB"Un appel notify() / CLI
Envoi OR multi-tags avec tentatives par tagnotify(tag=["tagA:N", "tagB:M"]) ou --tag tagA:N --tag tagB:MUn appel notify() / CLI
Arret anticipe sur echec de chaineAppriseAsset(abort_on_chain_failure=True)Tous les services de la session
Questions ou commentaires ?

Documentation

Vous avez repéré une faute de frappe ou une erreur ? Signalez-la ou proposez une correction .

Problèmes Techniques

Vous rencontrez un problème avec le code ? Ouvrez un ticket sur GitHub :

Conçu avec amour depuis le Canada