Fonctions de log en ocaml

Anne ocaml

Dès qu’un programme prend un peu d’ampleur, on a rapidement envie d’unifier les fonctions servant à donner des messages, ne serait ce que pour y ajouter un préfixe facilement identifiable.

Affichage avec continuation

Une méthode fréquente est d’utiliser les fonction d’affichage avec continuation :

let info1 msg
  let pp _fmt
    let txt = Format.flush_str_formatter () in
    Format.printf "@[<hov4>[my_prog] %s@]@." txt
  in Format.kfprintf pp Format.str_formatter msg

ou encore plus simple :

let info2 msg
  let pp txt = Format.printf "@[<hov4>[my_prog] %s@]@." txt
  in Format.ksprintf pp msf

Ces deux méthodes fonctionnent, mais elles ont un défaut, c’est que comme on passe par une chaîne de caractères intermédiaire, on perd une partie du formatage, comme l’indentation par exemple.

Une amélioration est de faire :

let info3 msg
  let fmt = Format.std_formatter in (* ou Format.err_formatter *)
  Format.fprintf fmt "@[<hov4>[my_prog] ";
  let pp fmt = Format.fprintf fmt "@]@."
  in Format.kfprintf pp fmt msg

Ainsi, la boite est bien ouverte avant l’affichage du message, et tout se passe bien.

Erreurs et Warning

On peut ensuite écrire par exemple :

let warning fmt = info3 ("Warning: "^^fmt)

Par contre, pour une erreur, je ne vois pas comment faire pour réutiliser la fonction info. Il faut donc utiliser le même principe :

let error n msg
  let fmt = Format.err_formatter in
  Format.fprintf fmt "@[<hov4>[my_prog] ";
  let pp fmt = Format.printf "@]@." ; exit n
  in Format.kfprintf pp fmt msg

Debug

Pour faire un affichage conditionnel, pour déboguer par exemple, on utilise la fonction ifprintf qui consomme les arguments sans rien faire :

let debug_level = 2
let debug n fmt
  if n <= debug_level then info fmt
  else Format.ifprintf Format.str_formatter fmt

Failwith

Une autre fonction bien pratique est :

let failwithf fmt = Printf.ksprintf failwith fmt

Elle permet d'écrire par exemple :

failwithf "%s:%d: erreur !" fichier ligne

Curiosités

Dans la donc du module Printf, on trouve deux formats étranges dont je n’ai pas encore compris l’utilité :

  Format.printf "format string substitution: %( %s%s %)@."
    "@[nom: %s @\nprenom: %s@]" "x" "y";
  Format.printf
    "convert a format string argument to its type digest: %{ %s%s %}@."
    "nom: %s - prenom: %s";

Ça donne :

format string substitution: nom: x
                            prenom: y
convert a format string argument to its type digest: %s%s

A la limite, j’imagine bien que la première peut servir à faire de l’internationalisation, mais la seconde ???

Voir aussi :