Monitoring et Alerting pour ABAP Cloud

Catégorie
DevOps
Publié
Auteur
Johannes

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 metriqueDescriptionExemple
CounterCompte les evenementsNombre de commandes traitees
GaugeValeur instantaneeLongueur actuelle de la file
HistogramDistribution des valeursTemps de reponse
TimerDuree d’executionTemps de traitement par lot

Collecter et stocker les metriques

" Classe de metriques personnalisees pour ABAP Cloud
CLASS 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 RAP
CLASS 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 CheckObjectifFrequence
LivenessL’application fonctionneToutes les 10-30 secondes
ReadinessL’application peut traiter les requetesToutes les 5-10 secondes
StartupL’application a demarreUne fois au demarrage
DeepToutes les dependances disponiblesToutes les 1-5 minutes

Implementer le service Health Check

" HTTP Handler pour les Health Checks
CLASS 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 :

SeveriteDescriptionTemps de reactionNotification
CriticalSysteme non disponibleImmediatPagerDuty, SMS
HighFonctionnalite limitee< 1 heureEmail + Slack
MediumProblemes de performance< 4 heuresEmail
LowInformatifJour ouvrable suivantTicket

Implementer Alert Manager

" Alert Manager pour ABAP Cloud
CLASS 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

MetriqueObjectifMethode de mesure
Disponibilite99.9%Uptime / Temps total
Temps de reponseP95 < 2s95e percentile des temps de reponse
Taux d’erreur< 0.1%Erreurs / Total requetes
MTTR< 1 heureMean Time To Recovery

Checklist des bonnes pratiques de monitoring

DomaineBonne pratiquePriorite
MetriquesDefinir et mesurer les KPIs businessHaute
MetriquesMetriques techniques (CPU, Memoire, Reponse)Haute
MetriquesAjouter des labels/dimensions aux metriquesMoyenne
Health ChecksSeparer Liveness et ReadinessHaute
Health ChecksVerifier les dependancesMoyenne
Health ChecksConfigurer les timeoutsMoyenne
AlertingDefinir les niveaux de severiteHaute
AlertingCooldown entre les alertesMoyenne
AlertingDocumenter les chemins d’escaladeHaute
DashboardsTableau de bord synthese avec KPIsHaute
DashboardsPossibilites de drill-downMoyenne
SLADocumenter les objectifs SLAHaute
SLARapports SLA automatiquesMoyenne
IntegrationConnecter les outils externesMoyenne

Sujets complementaires

Resume

Un monitoring et alerting efficaces pour ABAP Cloud requierent :

  1. Metriques personnalisees - Definir et collecter les KPIs business et techniques
  2. Endpoints Health Check - Implementer les checks Liveness, Readiness et Deep
  3. Regles d’alerte - Configurer les seuils et niveaux de severite
  4. Tableaux de bord - Visualisation claire des metriques les plus importantes
  5. Integration externe - Connecter des outils comme Datadog, Slack ou PagerDuty
  6. 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.