Eseguire il debug dei client API Google Data: esplorare il traffico dall'interno del programma

Jeffrey Scudder, team delle API Google Data
Giugno 2007

Introduzione

A volte non c'è niente di meglio che vedere cosa viene trasmesso via cavo. Ciò è particolarmente vero quando si scrive software che utilizza servizi web come le API Google Data, in cui molte operazioni comportano l'invio di richieste HTTP. Quando tutto il resto fallisce, puoi verificare che il tuo programma stia facendo ciò che ti aspetti visualizzando i byte effettivamente trasmessi e ricevuti. Molte delle librerie client per le API di Google Data hanno una modalità di debug che mostra il traffico HTTP. Questa funzionalità è particolarmente utile quando non hai accesso a un packet sniffer come WireShark o Fiddler.

Non riesco a contare il numero di volte in cui avrei potuto giurare che il mio programma fosse corretto, solo per scoprire, dopo aver esaminato una traccia di pacchetti, che c'era un carattere di nuova riga aggiuntivo o un'intestazione HTTP con un nome errato. La programmazione in base a un servizio web senza esaminare il traffico HTTP può essere come cercare di infilare un ago con gli occhi chiusi.

Tuttavia, potresti trovarti in una situazione in cui uno sniffer di pacchetti non è disponibile o non è adeguato per gestire i pacchetti criptati. Non temere, puoi aggirare questa limitazione sfruttando alcuni meccanismi di logging nel programma. Utilizzando queste funzionalità di logging, puoi visualizzare alcuni, se non tutti, i dati scambiati, anche per i dati HTTPS criptati o il codice in esecuzione da remoto.

Per questo articolo, ho scritto un codice diagnostico di esempio in tre lingue utilizzando le librerie client dell'API Google Data per Java, .NET e Python. In ogni esempio, attivo la registrazione o il debug, eseguo l'autenticazione utilizzando l'accesso client e poi ottengo un elenco dei miei Fogli Google e stampo i relativi titoli.

Java

Puoi utilizzare le classi java.util.logging per impostare i livelli di logging (e di conseguenza esporre i dati sul traffico) per un paio di oggetti chiave nella libreria client. Nell'esempio seguente, ho scelto di esaminare le intestazioni HTTP e le attività del parser XML per avere una visione completa di ciò che viene trasmesso via cavo.

La libreria client Java di Google Data ha classi separate per gestire le richieste HTTP e l'analisi XML; pertanto, devo creare due oggetti Logger, uno per ogni classe: com.google.gdata.client.http.HttpGDataRequest gestisce il traffico HTTP, mentre com.google.gdata.util.XmlParser è responsabile dell'analisi XML.

Le istanze del logger registreranno le attività per HttpGDataRequest e XmlParser e potrai controllare il livello di dettaglio dell'output di ciascun logger. Per questa dimostrazione, ho scelto di visualizzare tutti gli eventi prodotti dagli oggetti HttpGDataRequest e XmlParser.

Una volta creati e configurati i miei logger, devo indicare loro cosa fare quando ricevono un evento dalle loro classi. Per il momento, voglio scrivere tutte le informazioni di logging nella console, quindi creo un ConsoleHandler e lo aggiungo a entrambi i miei logger.

Ecco il mio codice di esempio:

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());
       
}
   
}
}

Quando esegui questo programma, nella console viene visualizzato un risultato simile al seguente (ho omesso alcune delle parti meno interessanti):

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

Questi log possono diventare piuttosto grandi, quindi ti consigliamo di essere più selettivo nell'impostazione dei livelli dei logger. Puoi anche creare un FileHandler anziché un ConsoleHandler per archiviare i dati dei log per un utilizzo successivo.

Naturalmente, se Java non fa per te, puoi provare .NET.

.NET

Per acquisire il traffico HTTP nella libreria client .NET, puoi sostituire la factory di richieste predefinita nel client con un GDataLoggingRequestFactory.

Le richieste HTTP nella libreria .NET vengono create da GDataRequestFactory, che si trova all'interno di ogni oggetto Service. Le normali fabbriche di richieste non eseguono alcun logging, ma GDataLoggingRequestFactory, che è una sottoclasse di GDataRequestFactory, ha il logging integrato. Puoi specificare il percorso completo del file di log impostando CombinedFileName.

Dopo aver configurato la fabbrica di richieste, devi sostituirla nell'oggetto Service impostando RequestFactory dell'oggetto Service. Il codice potrebbe avere un aspetto simile al seguente:

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();
       
}
   
}
}

Il file di log risultante contiene le richieste e le risposte XML. Ecco un esempio abbreviato che ho formattato utilizzando 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>

Ma magari ti piacciono molto i linguaggi di scripting e preferisci usare Python.

Python

Per acquisire il traffico HTTP nella libreria client Python, puoi visualizzare l'intestazione HTTP nella console attivando la modalità di debug nel client HTTP. L'oggetto servizio ha un membro di debug che puoi impostare su True.

Se imposti debug su true, il flag di debug viene impostato nell'oggetto HTTPRequest sottostante contenuto nell'oggetto servizio.

Ecco un esempio che ripete le intestazioni HTTP inviate dal server dei fogli di lavoro quando richiedi un elenco dei tuoi fogli di lavoro.

#!/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

Nella console verrà visualizzato un risultato simile al seguente:

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

Man mano che esegui altre operazioni, come un inserimento o un aggiornamento, nella console vengono visualizzati i dati delle richieste corrispondenti.

Conclusione

Questo breve tutorial ha illustrato come aggiungere funzionalità di logging di base a un programma Java, .NET o Python che utilizza le librerie client dell'API Google Data. Queste tecniche possono essere utili se devi eseguire il debug degli scambi HTTP, ma non hai accesso a uno sniffer di pacchetti. Con questi esempi ho solo scalfito la superficie. Molti dei meccanismi di logging presenti in queste lingue sono molto più potenti di quelli mostrati qui. Se vuoi saperne di più sulla registrazione o sulle API Google Data, consulta l'elenco di risorse riportato di seguito.

Le librerie client trattate in questo articolo sono disponibili nelle seguenti pagine:

Articoli della knowledge base correlati:

Gruppi di discussione: ne abbiamo diversi e ne arriveranno altri man mano che verranno implementate altre API di Google Data. Monitoriamo attivamente i gruppi.

Se hai domande o suggerimenti, non esitare a contattarmi. Partecipa al gruppo di discussione e inizia a pubblicare.