Le Monitoring et l’Alerting sont des composants essentiels de l’exploitation productive des applications ABAP Cloud. Tandis que ABAP Environment Monitoring couvre les bases, cet article se concentre sur les techniques avancees : metriques personnalisees, endpoints Health Check, configuration des alertes et integration d’outils de monitoring externes.
Apercu de l’architecture de monitoring
Une solution de monitoring complete pour ABAP Cloud comprend plusieurs couches :
+-----------------------------------------------------------------------------+| Architecture de Monitoring ABAP Cloud || || +------------------------------------------------------------------------+ || | Outils de Monitoring Externes | || | | || | +-------------+ +-------------+ +-------------+ +-------------+ | || | | Datadog | | Dynatrace | | Splunk | | Grafana | | || | | | | | | | | | | || | +-------------+ +-------------+ +-------------+ +-------------+ | || +------------------------------------------------------------------------+ || | || v || +------------------------------------------------------------------------+ || | Services SAP Cloud | || | | || | +---------------------+ +---------------------+ | || | | SAP Cloud ALM | | SAP Cloud Logging | | || | | - Operations | | - Stockage Logs | | || | | - Analytics | | - Export Metriques | | || | | - Alerting | | - Tableaux de bord | | || | +---------------------+ +---------------------+ | || +------------------------------------------------------------------------+ || | || v || +------------------------------------------------------------------------+ || | Application ABAP Cloud | || | | || | +---------------------+ +---------------------+ | || | | Metriques Custom | | APIs Health Check | | || | | - KPIs Business | | - Liveness | | || | | - Performance | | - Readiness | | || | | - Taux d'erreurs | | - Dependency | | || | +---------------------+ +---------------------+ | || | | || | +---------------------+ +---------------------+ | || | | Logs Application | | Evenements Metier | | || | | - Framework BALI | | - Evenements RAP | | || | | - Logs Structures | | - Evenements Custom| | || | +---------------------+ +---------------------+ | || +------------------------------------------------------------------------+ |+-----------------------------------------------------------------------------+Metriques personnalisees avec SAP Cloud Logging
Comprendre les types de metriques
| Type de metrique | Description | Exemple |
|---|---|---|
| Counter | Compte les evenements | Nombre de commandes traitees |
| Gauge | Valeur instantanee | Longueur actuelle de la file |
| Histogram | Distribution des valeurs | Temps de reponse |
| Timer | Duree d’execution | Temps de traitement par lot |
Collecter et stocker les metriques
" Classe de metriques personnalisees pour ABAP CloudCLASS zcl_custom_metrics DEFINITION PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_custom_metrics.
TYPES: BEGIN OF ty_metric, name TYPE string, value TYPE decfloat34, unit TYPE string, dimensions TYPE string_table, timestamp TYPE timestamp, END OF ty_metric, ty_metrics TYPE STANDARD TABLE OF ty_metric WITH EMPTY KEY.
METHODS: " Counter : Compte les evenements increment_counter IMPORTING iv_name TYPE string iv_value TYPE i DEFAULT 1 it_labels TYPE string_table OPTIONAL.
METHODS: " Gauge : Definit la valeur actuelle set_gauge IMPORTING iv_name TYPE string iv_value TYPE decfloat34 iv_unit TYPE string OPTIONAL.
METHODS: " Timer : Mesure la duree d'execution start_timer IMPORTING iv_name TYPE string RETURNING VALUE(rv_timer_id) TYPE guid_32.
METHODS: stop_timer IMPORTING iv_timer_id TYPE guid_32.
METHODS: " Exporter les metriques (pour outils externes) export_metrics RETURNING VALUE(rv_json) TYPE string.
METHODS: " Ecrire les metriques dans Application Log flush_to_log RAISING cx_bali_runtime.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO zcl_custom_metrics.
DATA: mt_metrics TYPE ty_metrics, mt_timers TYPE HASHED TABLE OF timestamp WITH UNIQUE KEY table_line.
TYPES: BEGIN OF ty_timer_entry, timer_id TYPE guid_32, name TYPE string, start_ts TYPE timestamp, END OF ty_timer_entry.
DATA: mt_active_timers TYPE HASHED TABLE OF ty_timer_entry WITH UNIQUE KEY timer_id.
ENDCLASS.
CLASS zcl_custom_metrics IMPLEMENTATION.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW zcl_custom_metrics( ). ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD increment_counter. GET TIME STAMP FIELD DATA(lv_timestamp).
" Rechercher le compteur existant ou en creer un nouveau DATA(lv_found) = abap_false. LOOP AT mt_metrics ASSIGNING FIELD-SYMBOL(<ls_metric>) WHERE name = iv_name. <ls_metric>-value = <ls_metric>-value + iv_value. <ls_metric>-timestamp = lv_timestamp. lv_found = abap_true. EXIT. ENDLOOP.
IF lv_found = abap_false. APPEND VALUE #( name = iv_name value = iv_value unit = 'count" dimensions = it_labels timestamp = lv_timestamp ) TO mt_metrics. ENDIF. ENDMETHOD.
METHOD set_gauge. GET TIME STAMP FIELD DATA(lv_timestamp).
" Toujours ecraser le gauge READ TABLE mt_metrics ASSIGNING FIELD-SYMBOL(<ls_metric>) WITH KEY name = iv_name.
IF sy-subrc = 0. <ls_metric>-value = iv_value. <ls_metric>-unit = COND #( WHEN iv_unit IS NOT INITIAL THEN iv_unit ELSE <ls_metric>-unit ). <ls_metric>-timestamp = lv_timestamp. ELSE. APPEND VALUE #( name = iv_name value = iv_value unit = COND #( WHEN iv_unit IS NOT INITIAL THEN iv_unit ELSE 'unit' ) timestamp = lv_timestamp ) TO mt_metrics. ENDIF. ENDMETHOD.
METHOD start_timer. " Generer l'ID du timer TRY. rv_timer_id = cl_system_uuid=>create_uuid_x16_static( ). CATCH cx_uuid_error. rv_timer_id = |TIMER_{ sy-datum }{ sy-uzeit }|. ENDTRY.
GET TIME STAMP FIELD DATA(lv_start).
INSERT VALUE #( timer_id = rv_timer_id name = iv_name start_ts = lv_start ) INTO TABLE mt_active_timers. ENDMETHOD.
METHOD stop_timer. READ TABLE mt_active_timers INTO DATA(ls_timer) WITH KEY timer_id = iv_timer_id.
IF sy-subrc = 0. GET TIME STAMP FIELD DATA(lv_end).
" Calculer la duree en millisecondes DATA(lv_duration_sec) = cl_abap_tstmp=>subtract( tstmp1 = lv_end tstmp2 = ls_timer-start_ts ). DATA(lv_duration_ms) = lv_duration_sec * 1000.
" Enregistrer comme metrique APPEND VALUE #( name = |{ ls_timer-name }_duration_ms| value = lv_duration_ms unit = 'milliseconds" timestamp = lv_end ) TO mt_metrics.
DELETE mt_active_timers WHERE timer_id = iv_timer_id. ENDIF. ENDMETHOD.
METHOD export_metrics. " Exporter les metriques en JSON (compatible avec Prometheus/Datadog) DATA(lt_export) = VALUE string_table( ).
LOOP AT mt_metrics INTO DATA(ls_metric). DATA(lv_dimensions) = concat_lines_of( table = ls_metric-dimensions sep = ',' ).
APPEND |{{ | && |"name": "{ ls_metric-name }", | && |"value": { ls_metric-value }, | && |"unit": "{ ls_metric-unit }", | && |"dimensions": "{ lv_dimensions }", | && |"timestamp": { ls_metric-timestamp } | && |}}| TO lt_export. ENDLOOP.
rv_json = |[{ concat_lines_of( table = lt_export sep = ',' ) }]|. ENDMETHOD.
METHOD flush_to_log. " Ecrire les metriques dans Application Log DATA(lo_log) = cl_bali_log=>create_with_header( header = cl_bali_header_setter=>create( object = 'ZMETRICS" subobject = 'CUSTOM" external_id = |METRICS_{ sy-datum }_{ sy-uzeit }| ) ).
LOOP AT mt_metrics INTO DATA(ls_metric). lo_log->add_item( cl_bali_message_setter=>create( severity = if_bali_constants=>c_severity_status text = |{ ls_metric-name }: { ls_metric-value } { ls_metric-unit }| ) ). ENDLOOP.
lo_log->save( ).
" Vider les metriques apres export CLEAR mt_metrics. ENDMETHOD.
ENDCLASS.Utiliser les metriques dans le code
" Exemple : Collecter des metriques dans une application RAPCLASS zcl_order_processor DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION. METHODS: process_orders IMPORTING it_orders TYPE ty_order_tab RAISING cx_failed.
ENDCLASS.
CLASS zcl_order_processor IMPLEMENTATION.
METHOD process_orders. DATA(lo_metrics) = zcl_custom_metrics=>get_instance( ).
" Demarrer le timer DATA(lv_timer) = lo_metrics->start_timer( 'order_processing' ).
" Traitement DATA(lv_success_count) = 0. DATA(lv_error_count) = 0.
LOOP AT it_orders INTO DATA(ls_order). TRY. " Traiter la commande process_single_order( ls_order ). lv_success_count = lv_success_count + 1.
" Incrementer le compteur lo_metrics->increment_counter( iv_name = 'orders_processed_total" it_labels = VALUE #( ( |status:success| ) ( |type:{ ls_order-type }| ) ) ).
CATCH cx_root INTO DATA(lx_error). lv_error_count = lv_error_count + 1.
lo_metrics->increment_counter( iv_name = 'orders_processed_total" it_labels = VALUE #( ( |status:error| ) ( |type:{ ls_order-type }| ) ) ). ENDTRY. ENDLOOP.
" Arreter le timer lo_metrics->stop_timer( lv_timer ).
" Longueur actuelle de la file comme Gauge DATA(lv_pending) = get_pending_order_count( ). lo_metrics->set_gauge( iv_name = 'orders_pending_queue" iv_value = lv_pending iv_unit = 'orders" ).
" Taux d'erreur comme Gauge DATA(lv_error_rate) = COND decfloat34( WHEN lines( it_orders ) > 0 THEN lv_error_count * 100 / lines( it_orders ) ELSE 0 ). lo_metrics->set_gauge( iv_name = 'orders_error_rate_percent" iv_value = lv_error_rate iv_unit = 'percent" ).
" Ecrire les metriques dans le log TRY. lo_metrics->flush_to_log( ). CATCH cx_bali_runtime. " Ignorer les erreurs de log ENDTRY. ENDMETHOD.
ENDCLASS.Endpoints Health Check
Les Health Checks sont essentiels pour l’orchestration de conteneurs et les load balancers. Dans ABAP Cloud, nous les implementons comme Service OData ou HTTP Handler.
Types de Health Check
| Type de Check | Objectif | Frequence |
|---|---|---|
| Liveness | L’application fonctionne | Toutes les 10-30 secondes |
| Readiness | L’application peut traiter les requetes | Toutes les 5-10 secondes |
| Startup | L’application a demarre | Une fois au demarrage |
| Deep | Toutes les dependances disponibles | Toutes les 1-5 minutes |
Implementer le service Health Check
" HTTP Handler pour les Health ChecksCLASS zcl_health_check_handler DEFINITION PUBLIC FINAL CREATE PUBLIC.
INTERFACES if_http_service_extension.
PUBLIC SECTION. TYPES: BEGIN OF ty_health_status, status TYPE string, timestamp TYPE timestamp, version TYPE string, checks TYPE string_table, END OF ty_health_status.
TYPES: BEGIN OF ty_check_result, name TYPE string, status TYPE string, message TYPE string, latency TYPE i, END OF ty_check_result, ty_check_results TYPE STANDARD TABLE OF ty_check_result WITH EMPTY KEY.
PRIVATE SECTION. METHODS: check_liveness RETURNING VALUE(rs_result) TYPE ty_check_result.
METHODS: check_readiness RETURNING VALUE(rs_result) TYPE ty_check_result.
METHODS: check_database RETURNING VALUE(rs_result) TYPE ty_check_result.
METHODS: check_external_service IMPORTING iv_service_name TYPE string RETURNING VALUE(rs_result) TYPE ty_check_result.
METHODS: run_all_checks RETURNING VALUE(rt_results) TYPE ty_check_results.
METHODS: build_response IMPORTING it_checks TYPE ty_check_results RETURNING VALUE(rv_json) TYPE string.
ENDCLASS.
CLASS zcl_health_check_handler IMPLEMENTATION.
METHOD if_http_service_extension~handle_request. " Evaluer le chemin de la requete DATA(lv_path) = request->get_header_field( '~path_info' ).
DATA(lt_checks) = VALUE ty_check_results( ). DATA(lv_http_status) = 200.
CASE lv_path. WHEN '/health/live' OR '/health/liveness'. " Seulement le check Liveness APPEND check_liveness( ) TO lt_checks.
WHEN '/health/ready' OR '/health/readiness'. " Readiness : Liveness + Base de donnees APPEND check_liveness( ) TO lt_checks. APPEND check_database( ) TO lt_checks.
WHEN '/health/deep' OR '/health'. " Tous les checks lt_checks = run_all_checks( ).
WHEN OTHERS. " Chemin inconnu response->set_status( code = 404 reason = 'Not Found' ). response->set_text( '{"error": "Unknown health endpoint"}' ). RETURN. ENDCASE.
" Determiner le statut global LOOP AT lt_checks INTO DATA(ls_check) WHERE status <> 'healthy'. lv_http_status = 503. " Service Unavailable EXIT. ENDLOOP.
" Construire la reponse DATA(lv_json) = build_response( lt_checks ).
response->set_status( code = lv_http_status reason = COND #( WHEN lv_http_status = 200 THEN 'OK' ELSE 'Service Unavailable" ) ). response->set_header_field( name = 'Content-Type' value = 'application/json' ). response->set_text( lv_json ). ENDMETHOD.
METHOD check_liveness. " Check simple : L'application fonctionne rs_result-name = 'liveness'. rs_result-status = 'healthy'. rs_result-message = 'Application is running'. rs_result-latency = 0. ENDMETHOD.
METHOD check_readiness. " Verifier si l'application est prete rs_result-name = 'readiness'.
TRY. " Exemple : Configuration chargee ? DATA(lv_config) = zcl_config_manager=>get_instance( )->is_initialized( ).
IF lv_config = abap_true. rs_result-status = 'healthy'. rs_result-message = 'Application is ready'. ELSE. rs_result-status = 'unhealthy'. rs_result-message = 'Configuration not loaded'. ENDIF.
CATCH cx_root INTO DATA(lx_error). rs_result-status = 'unhealthy'. rs_result-message = lx_error->get_text( ). ENDTRY. ENDMETHOD.
METHOD check_database. " Verifier la connectivite de la base de donnees rs_result-name = 'database'.
DATA(lv_start) = sy-uzeit.
TRY. " Executer une requete simple SELECT SINGLE @abap_true FROM t000 INTO @DATA(lv_result).
IF sy-subrc = 0. rs_result-status = 'healthy'. rs_result-message = 'Database connection OK'. ELSE. rs_result-status = 'unhealthy'. rs_result-message = 'Database query returned no result'. ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_db). rs_result-status = 'unhealthy'. rs_result-message = |Database error: { lx_db->get_text( ) }|. ENDTRY.
" Calculer la latence (simplifie) DATA(lv_end) = sy-uzeit. rs_result-latency = lv_end - lv_start. ENDMETHOD.
METHOD check_external_service. " Verifier un service externe rs_result-name = iv_service_name.
TRY. " Appel HTTP vers le service externe DATA(lo_destination) = cl_http_destination_provider=>create_by_cloud_destination( i_name = iv_service_name ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination( i_destination = lo_destination ).
DATA(lo_request) = lo_client->get_http_request( ). lo_request->set_uri_path( '/health' ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ). DATA(lv_status) = lo_response->get_status( )-code.
lo_client->close( ).
IF lv_status = 200. rs_result-status = 'healthy'. rs_result-message = |{ iv_service_name } is available|. ELSE. rs_result-status = 'degraded'. rs_result-message = |{ iv_service_name } returned status { lv_status }|. ENDIF.
CATCH cx_root INTO DATA(lx_error). rs_result-status = 'unhealthy'. rs_result-message = |{ iv_service_name }: { lx_error->get_text( ) }|. ENDTRY. ENDMETHOD.
METHOD run_all_checks. " Executer tous les Health Checks APPEND check_liveness( ) TO rt_results. APPEND check_readiness( ) TO rt_results. APPEND check_database( ) TO rt_results.
" Verifier les services externes (depuis la configuration) DATA(lt_services) = VALUE string_table( ( 'S4_BACKEND' ) ( 'DOCUMENT_SERVICE' ) ).
LOOP AT lt_services INTO DATA(lv_service). APPEND check_external_service( lv_service ) TO rt_results. ENDLOOP. ENDMETHOD.
METHOD build_response. " Construire la reponse JSON DATA(lv_overall_status) = 'healthy'. DATA(lt_check_json) = VALUE string_table( ).
LOOP AT it_checks INTO DATA(ls_check). IF ls_check-status <> 'healthy'. lv_overall_status = ls_check-status. ENDIF.
APPEND |{{ "name": "{ ls_check-name }", | && |"status": "{ ls_check-status }", | && |"message": "{ ls_check-message }", | && |"latency_ms": { ls_check-latency } }}| TO lt_check_json. ENDLOOP.
GET TIME STAMP FIELD DATA(lv_timestamp).
rv_json = |{{ | && |"status": "{ lv_overall_status }", | && |"timestamp": "{ lv_timestamp }", | && |"checks": [{ concat_lines_of( table = lt_check_json sep = ',' ) }] | && |}}|. ENDMETHOD.
ENDCLASS.Exemple de reponse Health Check
{ "status": "healthy", "timestamp": "20260214143022", "checks": [ { "name": "liveness", "status": "healthy", "message": "Application is running", "latency_ms": 0 }, { "name": "database", "status": "healthy", "message": "Database connection OK", "latency_ms": 5 }, { "name": "S4_BACKEND", "status": "healthy", "message": "S4_BACKEND is available", "latency_ms": 120 } ]}Configurer les regles d’alerte
Concept d’alerte
Les alertes doivent etre classees par urgence :
| Severite | Description | Temps de reaction | Notification |
|---|---|---|---|
| Critical | Systeme non disponible | Immediat | PagerDuty, SMS |
| High | Fonctionnalite limitee | < 1 heure | Email + Slack |
| Medium | Problemes de performance | < 4 heures | |
| Low | Informatif | Jour ouvrable suivant | Ticket |
Implementer Alert Manager
" Alert Manager pour ABAP CloudCLASS zcl_alert_manager DEFINITION PUBLIC FINAL CREATE PRIVATE.
PUBLIC SECTION. CLASS-METHODS: get_instance RETURNING VALUE(ro_instance) TYPE REF TO zcl_alert_manager.
TYPES: BEGIN OF ENUM ty_severity STRUCTURE severity, critical, high, medium, low, END OF ENUM ty_severity STRUCTURE severity.
TYPES: BEGIN OF ty_alert, id TYPE guid_32, name TYPE string, severity TYPE ty_severity, message TYPE string, source TYPE string, timestamp TYPE timestamp, resolved TYPE abap_bool, resolved_at TYPE timestamp, END OF ty_alert, ty_alerts TYPE STANDARD TABLE OF ty_alert WITH KEY id.
TYPES: BEGIN OF ty_alert_rule, name TYPE string, condition TYPE string, threshold TYPE decfloat34, severity TYPE ty_severity, cooldown TYPE i, " Secondes entre alertes enabled TYPE abap_bool, END OF ty_alert_rule, ty_alert_rules TYPE STANDARD TABLE OF ty_alert_rule WITH KEY name.
METHODS: " Declencher une alerte fire_alert IMPORTING iv_name TYPE string iv_severity TYPE ty_severity iv_message TYPE string iv_source TYPE string OPTIONAL.
METHODS: " Resoudre une alerte resolve_alert IMPORTING iv_alert_id TYPE guid_32.
METHODS: " Recuperer les alertes actives get_active_alerts RETURNING VALUE(rt_alerts) TYPE ty_alerts.
METHODS: " Verifier une metrique et eventuellement declencher une alerte check_threshold IMPORTING iv_metric_name TYPE string iv_value TYPE decfloat34.
METHODS: " Enregistrer une regle d'alerte register_rule IMPORTING is_rule TYPE ty_alert_rule.
PRIVATE SECTION. CLASS-DATA: go_instance TYPE REF TO zcl_alert_manager.
DATA: mt_active_alerts TYPE ty_alerts, mt_rules TYPE ty_alert_rules, mt_last_fired TYPE HASHED TABLE OF timestamp WITH UNIQUE KEY table_line.
METHODS: send_notification IMPORTING is_alert TYPE ty_alert.
METHODS: log_alert IMPORTING is_alert TYPE ty_alert.
ENDCLASS.
CLASS zcl_alert_manager IMPLEMENTATION.
METHOD get_instance. IF go_instance IS NOT BOUND. go_instance = NEW zcl_alert_manager( ). ENDIF. ro_instance = go_instance. ENDMETHOD.
METHOD fire_alert. " Generer l'ID de l'alerte DATA(lv_alert_id) = CONV guid_32( cl_system_uuid=>create_uuid_x16_static( ) ).
GET TIME STAMP FIELD DATA(lv_timestamp).
DATA(ls_alert) = VALUE ty_alert( id = lv_alert_id name = iv_name severity = iv_severity message = iv_message source = COND #( WHEN iv_source IS NOT INITIAL THEN iv_source ELSE sy-repid ) timestamp = lv_timestamp resolved = abap_false ).
" Ajouter aux alertes actives APPEND ls_alert TO mt_active_alerts.
" Journaliser l'alerte log_alert( ls_alert ).
" Envoyer la notification (selon la severite) send_notification( ls_alert ). ENDMETHOD.
METHOD resolve_alert. READ TABLE mt_active_alerts ASSIGNING FIELD-SYMBOL(<ls_alert>) WITH KEY id = iv_alert_id.
IF sy-subrc = 0. GET TIME STAMP FIELD <ls_alert>-resolved_at. <ls_alert>-resolved = abap_true.
" Optionnel : Notification de resolution DATA(ls_resolved_alert) = <ls_alert>. ls_resolved_alert-message = |RESOLU : { <ls_alert>-message }|. send_notification( ls_resolved_alert ). ENDIF. ENDMETHOD.
METHOD get_active_alerts. rt_alerts = FILTER #( mt_active_alerts WHERE resolved = abap_false ). ENDMETHOD.
METHOD check_threshold. " Rechercher la regle pour la metrique READ TABLE mt_rules INTO DATA(ls_rule) WITH KEY name = iv_metric_name.
IF sy-subrc <> 0 OR ls_rule-enabled = abap_false. RETURN. ENDIF.
" Verifier le seuil DATA(lv_exceeded) = abap_false.
CASE ls_rule-condition. WHEN 'GT'. lv_exceeded = xsdbool( iv_value > ls_rule-threshold ). WHEN 'GE'. lv_exceeded = xsdbool( iv_value >= ls_rule-threshold ). WHEN 'LT'. lv_exceeded = xsdbool( iv_value < ls_rule-threshold ). WHEN 'LE'. lv_exceeded = xsdbool( iv_value <= ls_rule-threshold ). WHEN 'EQ'. lv_exceeded = xsdbool( iv_value = ls_rule-threshold ). ENDCASE.
IF lv_exceeded = abap_true. " Verifier le cooldown " (Implementation simplifiee - en production avec table/cache)
fire_alert( iv_name = iv_metric_name iv_severity = ls_rule-severity iv_message = |{ iv_metric_name }: { iv_value } { ls_rule-condition } { ls_rule-threshold }| ). ENDIF. ENDMETHOD.
METHOD register_rule. " Mettre a jour une regle existante ou en ajouter une nouvelle READ TABLE mt_rules ASSIGNING FIELD-SYMBOL(<ls_rule>) WITH KEY name = is_rule-name.
IF sy-subrc = 0. <ls_rule> = is_rule. ELSE. APPEND is_rule TO mt_rules. ENDIF. ENDMETHOD.
METHOD send_notification. " Envoyer la notification selon la severite CASE is_alert-severity. WHEN severity-critical. " PagerDuty / SMS " Appel HTTP vers API PagerDuty " send_pagerduty_alert( is_alert ).
WHEN severity-high. " Email + Slack " send_slack_notification( is_alert ). " send_email( is_alert ).
WHEN severity-medium. " Email uniquement " send_email( is_alert ).
WHEN severity-low. " Uniquement log (deja fait) ENDCASE.
" En plus : Integration SAP Cloud ALM " (si configure) ENDMETHOD.
METHOD log_alert. " Ecrire l'alerte dans Application Log TRY. DATA(lo_log) = cl_bali_log=>create_with_header( header = cl_bali_header_setter=>create( object = 'ZALERT" subobject = CONV #( is_alert-severity ) external_id = |ALERT_{ is_alert-id }| ) ).
DATA(lv_severity) = SWITCH if_bali_constants=>ty_severity( is_alert-severity WHEN severity-critical THEN if_bali_constants=>c_severity_error WHEN severity-high THEN if_bali_constants=>c_severity_error WHEN severity-medium THEN if_bali_constants=>c_severity_warning WHEN severity-low THEN if_bali_constants=>c_severity_status ).
lo_log->add_item( cl_bali_message_setter=>create( severity = lv_severity text = |[{ is_alert-name }] { is_alert-message }| ) ).
lo_log->save( ).
CATCH cx_bali_runtime. " Ignorer les erreurs de log ENDTRY. ENDMETHOD.
ENDCLASS.Bonnes pratiques pour le monitoring SLA
Definitions SLA
| Metrique | Objectif | Methode de mesure |
|---|---|---|
| Disponibilite | 99.9% | Uptime / Temps total |
| Temps de reponse | P95 < 2s | 95e percentile des temps de reponse |
| Taux d’erreur | < 0.1% | Erreurs / Total requetes |
| MTTR | < 1 heure | Mean Time To Recovery |
Checklist des bonnes pratiques de monitoring
| Domaine | Bonne pratique | Priorite |
|---|---|---|
| Metriques | Definir et mesurer les KPIs business | Haute |
| Metriques | Metriques techniques (CPU, Memoire, Reponse) | Haute |
| Metriques | Ajouter des labels/dimensions aux metriques | Moyenne |
| Health Checks | Separer Liveness et Readiness | Haute |
| Health Checks | Verifier les dependances | Moyenne |
| Health Checks | Configurer les timeouts | Moyenne |
| Alerting | Definir les niveaux de severite | Haute |
| Alerting | Cooldown entre les alertes | Moyenne |
| Alerting | Documenter les chemins d’escalade | Haute |
| Dashboards | Tableau de bord synthese avec KPIs | Haute |
| Dashboards | Possibilites de drill-down | Moyenne |
| SLA | Documenter les objectifs SLA | Haute |
| SLA | Rapports SLA automatiques | Moyenne |
| Integration | Connecter les outils externes | Moyenne |
Sujets complementaires
- ABAP Environment Monitoring - Monitoring de base avec Fiori Apps
- Application Logging - Journalisation detaillee
- HTTP Client - Appeler des APIs externes
Resume
Un monitoring et alerting efficaces pour ABAP Cloud requierent :
- Metriques personnalisees - Definir et collecter les KPIs business et techniques
- Endpoints Health Check - Implementer les checks Liveness, Readiness et Deep
- Regles d’alerte - Configurer les seuils et niveaux de severite
- Tableaux de bord - Visualisation claire des metriques les plus importantes
- Integration externe - Connecter des outils comme Datadog, Slack ou PagerDuty
- Monitoring SLA - Verifier et documenter la conformite en continu
La combinaison de SAP Cloud ALM, Application Logging et outils de monitoring externes permet une surveillance complete et une reaction rapide aux problemes.