Débogage des clients de l'API Google Data: exploration du trafic à partir de votre programme

Jeffrey Scudder, équipe Google Data APIs
Juin 2007

Introduction

Parfois, il n'y a pas de substitut pour voir ce qui passe par-dessus le fil. Cela est particulièrement vrai lorsque vous écrivez des logiciels qui utilisent des services Web tels que les API Google Data, où de nombreuses opérations impliquent l'envoi de requêtes HTTP. Lorsque toutes les autres solutions ont échoué, vous pouvez vérifier que votre programme fait bien ce que vous attendiez en affichant les octets réellement transmis et reçus. De nombreuses bibliothèques clientes pour les API Google Data disposent d'un mode de débogage qui affiche le trafic HTTP. Cela est particulièrement utile lorsque vous n'avez pas accès à un outil de détection de paquets tel que WireShark ou Fiddler.

Je ne peux pas compter le nombre de fois où j'aurais pu jurer que mon programme était correct, mais seulement après avoir inspecté une trace de paquet, qu'il y avait un caractère de nouvelle ligne supplémentaire ou un en-tête HTTP mal nommé. Programmer à l'aide d'un service Web sans regarder le trafic HTTP, c'est comme essayer de faire passer une aiguille, les yeux fermés.

Cependant, vous pouvez vous retrouver dans une situation où un outil de détection de paquets n'est pas disponible ou est inadéquat pour traiter des paquets chiffrés. Ne vous inquiétez pas : vous pouvez contourner cette limitation en exploitant certains mécanismes de journalisation en programme. En utilisant ces fonctionnalités de journalisation, vous pouvez voir une partie, voire la totalité, des données échangées, même pour des données HTTPS chiffrées ou du code d'exécution à distance.

Pour cet article, j'ai rédigé des exemples de code de diagnostic en trois langages à l'aide des bibliothèques clientes de l'API Google Data pour Java, .NET et Python. Dans chaque exemple, j'active la journalisation ou le débogage, je m'authentifie à l'aide de la connexion client, puis j'obtiens une liste de mes feuilles de calcul Google et j'imprime leurs titres.

Java

Vous pouvez utiliser les classes java.util.logging pour définir les niveaux de journalisation (et donc exposer les données de trafic) pour quelques objets clés de la bibliothèque cliente. Dans l'exemple ci-dessous, j'ai choisi d'examiner les en-têtes HTTP et les activités de l'analyseur XML pour obtenir une vue complète de ce qui transite sur le réseau.

La bibliothèque cliente Java de Google comporte des classes distinctes pour gérer les requêtes HTTP et l'analyse XML. Je dois donc créer deux objets Logger, un pour chaque classe : com.google.gdata.client.http.HttpGDataRequest gère le trafic HTTP, tandis que com.google.gdata.util.XmlParser est responsable de l'analyse XML.

Les instances d'enregistreur enregistrent les activités pour HttpGDataRequest et XmlParser, et vous pouvez contrôler le niveau de détail de la sortie de chaque enregistreur. Pour cette démonstration, j'ai choisi d'afficher tous les événements produits par les objets HttpGDataRequest et XmlParser.

Une fois mes enregistreurs enregistrés et configurés, je dois leur indiquer ce qu'ils doivent faire lorsqu'ils reçoivent un événement de leurs classes. Pour l'instant, je veux écrire toutes les informations de journalisation dans la console. Je vais donc créer un ConsoleHandler et l'ajouter aux deux enregistreurs.

Voici mon exemple de code:

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.logging.*;

public class PrintSpreadsheetsWithLogging {
   
   
public static void main(String [] args) throws AuthenticationException,
                                                   
ServiceException, IOException {
       
// Configure the logging mechanisms.
       
Logger httpLogger = Logger.getLogger("com.google.gdata.client.http.HttpGDataRequest");
        httpLogger
.setLevel(Level.ALL);
       
Logger xmlLogger = Logger.getLogger("com.google.gdata.util.XmlParser");
        xmlLogger
.setLevel(Level.ALL);
       
// Create a log handler which prints all log events to the console.
       
ConsoleHandler logHandler = new ConsoleHandler();
        logHandler
.setLevel(Level.ALL);
        httpLogger
.addHandler(logHandler);
        xmlLogger
.addHandler (logHandler);
       
       
SpreadsheetService service = new SpreadsheetService("testing-loggingExampleApp-1");
        service
.setUserCredentials(email, password);
     
       
// Get a list of your spreadsheets.
        URL metafeedUrl
= new URL("http://spreadsheets.google.com/feeds/spreadsheets/private/full ");
       
SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);
     
       
// Print the title of each spreadsheet.
       
List spreadsheets = feed.getEntries();
       
for (int i = 0; i < spreadsheets.size(); i++) {
         
SpreadsheetEntry entry = (SpreadsheetEntry)spreadsheets.get(i);
         
System.out.println("\t" + entry.getTitle().getPlainText());
       
}
   
}
}

Lorsque vous exécutez ce programme, un message semblable au suivant s'affiche dans la console (j'ai retiré certaines des sections les moins intéressantes):

Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setPrivateHeader
FINER: Authorization: <Not Logged>
Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setHeader
FINER: User-Agent: ...
...
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINE: 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Date: Thu, 07 Jun 2007 17:25:24 GMT
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: null: HTTP/1.1 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Content-Type: application/atom+xml; charset=UTF-8
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Last-Modified: Thu, 07 Jun 2007 17:25:22 GMT
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element id
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element id
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element title
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINER: Attribute type='text'
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element title
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element entry
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element feed

Ces journaux peuvent être assez volumineux. Par conséquent, nous vous conseillons de faire preuve de sélectivité pour définir les niveaux des enregistreurs. Vous pouvez également créer un objet FileHandler au lieu d'un objet ConsoleHandler afin de pouvoir stocker les données de journal pour une utilisation ultérieure.

Bien sûr, si Java n'est pas votre sac, vous pouvez essayer .NET.

.NET

Pour capturer le trafic HTTP dans la bibliothèque cliente .NET, vous pouvez remplacer la fabrique de requêtes par défaut du client par un GDataLoggingRequestFactory.

Les requêtes HTTP de la bibliothèque .NET sont créées par le GDataRequestFactory qui se trouve dans chaque objet Service. Les usines de requêtes normales n'effectuent aucune journalisation, mais la journalisation est intégrée à GDataLoggingRequestFactory, qui est une sous-classe de GDataRequestFactory. Vous pouvez spécifier le chemin d'accès complet du fichier journal en définissant CombinedFileName.

Après avoir configuré votre fabrique de requêtes, vous devez la remplacer dans votre objet Service en définissant le RequestFactory de l'objet de service. Votre code peut se présenter comme suit:

using System;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;

namespace LogginTest
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
SpreadsheetsService service = new SpreadsheetsService("-exampleApp-1");
            service
.setUserCredentials(email, password);

           
Google.GData.Client.GDataLoggingRequestFactory factory = new GDataLoggingRequestFactory("wise", "SpreadsheetsLoggingTest");
            factory
.MethodOverride = true;
            factory
.CombinedLogFileName = "c:\\temp\\xmllog.log";
           
Console.WriteLine("Log file name:" + factory.CombinedLogFileName);
           
            service
.RequestFactory = factory;

           
SpreadsheetQuery query = new SpreadsheetQuery();
           
SpreadsheetFeed feed = service.Query(query);

           
Console.WriteLine("Your spreadsheets:");
           
foreach (SpreadsheetEntry entry in feed.Entries)
           
{
               
Console.WriteLine(entry.Title.Text);
           
}

           
Console.ReadKey();
       
}
   
}
}

Le fichier journal résultant contient les requêtes et réponses XML. Voici un exemple abrégé que j'ai formaté à l'aide de tidy.

<?xml version='1.0' encoding='utf-8'?>

<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>
  http://spreadsheets.google.com/feeds/spreadsheets/private/full</id>
  <updated>2007-06-07T22:05: 02.674Z</updated>
  <link rel='self' type='application/atom+xml'
  href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'>

  </link>
  ...
  <entry>
    <updated>2007-03-28T17:28:57.250Z</updated>
    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    <title type='text'>events</title>

    <content type='text'>events</content>
    ...
  </entry>
  <entry>
    <updated>2007-05-25T22:11:08.200Z</updated>

    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    </category>
    <title type='text'>UnitTest</title>
    <content type='text'>UnitTest</content>
    ...
  </entry>

  ...
</feed>

Mais vous préférez les langages de script, et vous préférez utiliser Python.

Python

Pour capturer le trafic HTTP dans la bibliothèque cliente Python, vous pouvez répercuter le trafic de l'en-tête HTTP vers la console en activant le mode de débogage dans le client HTTP. L'objet de service possède un membre de débogage que vous pouvez définir sur True.

Si vous définissez le débogage sur "true", l'option de débogage est définie dans l'objet HTTPRequest sous-jacent contenu dans l'objet de service.

Voici un exemple qui renvoie les en-têtes HTTP envoyés par le serveur de feuilles de calcul lorsque vous demandez une liste de vos feuilles de calcul.

#!/usr/bin/python

import gdata.spreadsheet.service

client
= gdata.spreadsheet.service.SpreadsheetsService()
client
.debug = True

client
.ClientLogin(email, password)

feed
= client.GetSpreadsheetsFeed()

for entry in feed.entry:
 
print entry.title.text

Un message semblable au suivant s'affiche sur votre console:

reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/atom+xml; charset=UTF-8
header: Last-Modified: Thu, 07 Jun 2007 18:22:35 GMT
header: Cache-Control: max-age=0, must-revalidate, private
header: Transfer-Encoding: chunked
...
header: Date: Thu, 07 Jun 2007 18:22:35 GMT
header: Server: GFE/1.3

Lorsque vous effectuez des opérations supplémentaires, telles qu'une insertion ou une mise à jour, les données de requête correspondantes sont renvoyées à votre console.

Conclusion

Ce tutoriel rapide montre comment ajouter une fonctionnalité de journalisation de base dans un programme Java, .NET ou Python utilisant les bibliothèques clientes de l'API Google Data. Ces techniques peuvent être utiles si vous devez déboguer des places de marché HTTP, mais que vous n'avez pas accès à un outil de détection de paquets. Je n'ai effleuré la surface qu'avec ces exemples. La plupart des mécanismes de journalisation présents dans ces langages sont bien plus puissants que ceux présentés ici. Si vous souhaitez en savoir plus sur la journalisation ou les API Google Data, consultez la liste des ressources ci-dessous.

Les bibliothèques clientes abordées dans cet article sont disponibles sur les pages suivantes:

Éléments de la base de connaissances associée:

Groupes de discussion: nous avons beaucoup d'API et d'autres seront bientôt déployées, car d'autres API Google Data seront déployées. Nous surveillons activement les groupes.

Si vous avez des questions ou des suggestions, n'hésitez pas à nous contacter. Rejoignez le groupe de discussion et commencez à publier des posts.