Audit instances SOA 11g 3/4 : BPEL via SQL/Java

Voici le 3ème article de la série concernant les audits des instances SOA. Dans le premier article, nous avons pu voir 2 alternatives à la console pour obtenir les audits/logs des instances SOA. Nous avons ensuite vu comment récupérer les audits de Mediator en pratique. Nous allons à présent faire un focus sur la méthode de récupération via SQL / Java des informations concernant les BPELs.

 
 

Correspondance des logs de la console et des tables

Tout d’abord, nous allons déterminer où se retrouvent les logs d’un BPEL visibles dans la console. Vous pouvez retrouver la liste des tables concernées et leur descriptif fonctionnel dans le 1er article.

schéma relation console / tables
schéma relation console / tables

Précisions sur les tables

Voici quelques précisions sur les tables que nous allons utiliser par la suite :

TABLE

DESCRIPTIF

COMPOSITE_INSTANCE Pour les exemples de requête, notre point d’entrée sera cette table, car elles sont basés sur l’identifiant d’une instance de composite
CUBE_INSTANCE Cette table contient les instance de composants BPELElle nous sera notamment utile pour déterminer le numéro d’instance du composant BPEL.
AUDIT_TRAIL Cette contient notamment les traces principales du BPEL : les étapes de test, assignation, transformation…
AUDIT_DETAILS Si le contenu de variables BPEL devient trop important, on retrouvera ici certaines traces.En effet, pour des raisonsde performance, si la taille du message devient trop imporante, sa trace est stockée dans cette table. Dans ce cas, la console n’affiche pas le message mais un lien vers son contenu.Voir le détail du paramètre auditDetailThreshold pour plus de détail.
XML_DOCUMENT Cette table contenant les éléments XML de taille importante.En effet, pour des raisons de performance, si la taille du message devient trop imporante, sa trace est stockée dans cette table.Dans ce cas, la console n’affiche pas le message mais un lien vers son contenu. Voir le détail du paramètre largeDocumentThreshold pour plus de détail.

Ainsi qu’une représentation des jointures entre les tables (non exhaustif, représente les jointures utilisées dans les exemples, pourrait se faire par d’autres champs, notamment l’ECID) :

schéma relation console / tables
schéma relation console / tables

Lecture dans AUDIT_TRAIL

Dans cette table, les informations sont dans le champs LOG de type RAW. Elles peuvent potentiellement être sous plusieurs enregistrement. On va devoir dans un premier temps concaténer tous les éléments potentiels pour obtenir un Blob. Une fois ce Blob obtenu, il faudra décompresser les données pour les rendre lisibles.
Tout d’abord, l’obtention d’un Blob pour tous les champs log de type RAW pouvant exister pour une instance : on passe par la création d’une fonction PL/SQL avec l’utilisateur utilisé pour accéder à la base.

create or replace
FUNCTION get_audit_trail_log(cikey IN INTEGER) RETURN blob IS
CURSOR c_log(l_cikey INTEGER) IS
SELECT * FROM AUDIT_TRAIL atr WHERE cikey = l_cikey ORDER BY count_id;
bl BLOB;
BEGIN
dbms_lob.createtemporary (bl, TRUE);
FOR r_log IN c_log(cikey)
LOOP
dbms_lob.append (bl,r_log.log);
END LOOP;
RETURN(bl);
END;

Ensuite on va s’appuyer sur cette fonction pour notre requête, qui effectuera au passage la décompression :

SELECT utl_compress.lz_uncompress(get_audit_trail_log(ci.cikey)) fullaudit  FROM cube_instance ci, composite_instance cmpsti, audit_trail atr WHERE cmpsti.ID=ci.CMPST_ID and atr.cikey=ci.cikey and cmpsti.ID = myCompositeInstanceId;

A ce stade, on peut voir que les données sont lisibles (via SQL Developer en précisant la lecture en mode texte). Voici un extrait de code Java pour lire simplement le contenu du Blob obtenu après décompression. Ici l’exemple suppose que l’on a mis en place un PreparedStatement en amont avec la requête précédente :

// méthode pour récupérer le contenu texte du Blob
public String getTextFromBlob(Blob blob) throws Exception {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    InputStream is =  blob.getBinaryStream();
    byte[] buffer = new byte[256];
    int count = 0;
    while ((count = is.read(buffer)) > 0)
    {
        baos.write(buffer,0,count);
    }
    return baos.toString();
}
// Code extrait de la lecture de la classe où l’audit trail est récupéré  
ResultSet rset = stmt.executeQuery();  
while(rset.next())
{
    Blob blob = rset.getBlob("fullaudit") ;
    String auditDetail = getTextFromBlob(blob) ;
    System.out.println("BPEL Audit Trail : ");
    System.out.println(audit);
}

Le résultat ressemblera par exemple à ceci (raccourci) :

<event sid="0" cat="2" n="0" date="2013-09-03T19:15:48.820+02:00" type="2"><message><![CDATA[Nouvelle instance du processus BPEL "1.0" initiée (numéro "BPELSample").]]></message></event><event sid="BpPrc0.1" cat="2" n="1" label="process" psid="0" date="2013-09-03T19:15:48.846+02:00" type="2"><message><![CDATA[_cr_]]></message></event>
…
<event partnerWSDL="" sid="BpSeq0.3" cat="2" wikey="320001-BpInv0-BpSeq0.3-3" state="5" n="7" label="Invoke" date="2013-09-03T19:15:51.940+02:00" type="2"><message><![CDATA[Opération bidirectionnelle (2-way) "execute" appelée sur le partenaire "MediatorEcho.MediatorEcho".]]></message><details><![CDATA[<messages>
<Invoke_execute_InputVariable><part name="request" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><singleString xmlns:inp1="http://xmlns.oracle.com/singleString" xmlns="http://xmlns.oracle.com/singleString">Easy</singleString></part></Invoke_execute_InputVariable>
<Invoke_execute_OutputVariable><part name="reply" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><singleString xmlns="http://xmlns.oracle.com/singleString">Team</singleString></part></Invoke_execute_OutputVariable></messages>]]></details></event>
…
<event sid="BpPrc0.1" cat="2" n="14" date="2013-09-03T19:15:51.966+02:00" type="2"><message><![CDATA[Instance de processus BPEL "320001" terminée]]></message></event>

On notera la présence dans notre exemple du XML de l’appel. Il n’a pas été externalisé dans les autres tables. S’il avait été délocalisé, nous aurions quelque chose ressemblant à :

<event partnerWSDL="" sid="BpSeq0.3" cat="2" wikey="330004-BpInv0-BpSeq0.3-3" state="5" n="7" label="Invoke" date="2013-09-04T11:44:21.604+02:00" type="2"><message><![CDATA[Opération bidirectionnelle (2-way) "execute" appelée sur le partenaire "MediatorEcho.MediatorEcho".]]></message><details id="2" /></event>

Ici l’ID fournie correspond au DETAIL_ID dans la table AUDIT_DETAILS, qui stockera le message.

Lecture dans AUDIT_DETAILS

Dans cette table les éléments sont stockés dans le champ BIN de type Blob. Ce champ est compressé, ce que la requête traitera.

SELECT utl_compress.lz_uncompress(ad.BIN) detail FROM CUBE_INSTANCE ci,AUDIT_DETAILS ad WHERE ci.cikey=ad.cikey and ci.CMPST_ID = myCompositeInstanceId;

Ne reste plus qu’à récupérer le contenu texte du blog via Java :

ResultSet rset = stmt.executeQuery();  
while(rset.next())
{
    Blob blob = rset.getBlob("detail") ;
    String auditDetail = getTextFromBlob(blob) ;
    System.out.println("BPEL Audit Trail Detail : ");
    System.out.println(audit);
}

A noter que si un élément est stocké dans la table XML_DOCUMENT, il sera référencé via DOC_REF. Cette clef permettra la récupération du document directement dans la table XML_DOCUMENT.

Lecture dans XML_DOCUMENT

Dans cette table les données sont dans le champ DOCUMENT de type Blob, ne sont pas compressées, mais en XML Binaire. Nous allons tout d’abord récupérer les données via notre requête :

select xml.* from cube_instance ci, audit_details ad, xml_document xml where ci.cikey=ad.cikey and xml.document_id=ad.doc_ref and ci.cmpst_id = myCompositeInstanceId;

Les données sont ensuite lisible après lecture du Blob en java grâce au code suivant par exemple :

     while(rset.next())
    {
        Blob blob = rset.getBlob("doc");
        XMLDOMImplementation domimpl = new XMLDOMImplementation();
        BinXMLProcessor proc = BinXMLProcessorFactory.createProcessor();
        BinXMLStream inpbin = proc.createBinXMLStream(blob);
        BinXMLDecoder dec = inpbin.getDecoder();
        InfosetReader xmlreader = dec.getReader();
        XMLDocument doc = (XMLDocument) domimpl.createDocument(xmlreader);
        System.out.println("BPEL XML Document : ");
        doc.print(System.out);
    }

Conclusion

Grace à ces éléments, vous avez pu voir comment récupérer directement en base les principaux éléments d’audit d’un BPEL (valide de 11.1.1.4 à 11.1.1.7).
Les requêtes fournies ici sont à titre d’exemple, elles peuvent remaniées pour mettre en place différents critères. La seule règle qu’il est fortement conseillé d’appliquer est de bien vérifier les plans d’exécution de vos requêtes, de bien utiliser les index. Une mauvaise requête peut être pénalisante pour la SOA-Suite de manière générale.
La prochaine fois nous vous présenterons un use case basé sur ces récupérations d’audits : définir les performances des différentes étapes d’une instance de composite !
Restez connecté !

Sources