Debugging von Google Data API-Clients: Zugriffe innerhalb Ihres Programms untersuchen

Jeffrey Scudder, Google Data APIs-Team
Juni 2007

Einführung

Manchmal gibt es keinen Ersatz dafür, was über das Kabel hinausgeht. Dies gilt besonders für das Schreiben von Software, die Webdienste wie die Google Data APIs nutzt, bei denen viele Vorgänge mit HTTP-Anfragen verbunden sind. Wenn alles andere fehlschlägt, können Sie prüfen, ob Ihr Programm die von Ihnen erwarteten Aktionen ausführt. Sehen Sie sich dazu die tatsächlich übertragenen und empfangenen Byte an. Viele Clientbibliotheken für die Google Data APIs haben einen Debugging-Modus, in dem der HTTP-Traffic angezeigt wird. Dies ist besonders nützlich, wenn Sie keinen Zugriff auf einen Paket-Sniffer wie WireShark oder Fiddler haben.

Ich kann nicht messen, wie oft ich mein Programm für richtig hielt, und habe bei der Prüfung eines Paket-Trace festgestellt, dass es einen zusätzlichen Zeilenumbruch oder einen falsch benannten HTTP-Header gibt. Die Programmierung mit einem Webdienst, ohne den HTTP-Traffic zu betrachten, kann wie ein Versuch sein, eine Nadel mit geschlossenen Augen zu verbinden.

Es kann jedoch vorkommen, dass ein Paket-Sniffer nicht verfügbar ist oder nicht für verschlüsselte Pakete geeignet ist. Sie können diese Einschränkung umgehen, indem Sie Logging-Mechanismen ins Programm aufnehmen. Mit diesen Logging-Funktionen können Sie einige, wenn nicht sogar alle, ausgetauschten Daten sehen, auch für verschlüsselte HTTPS-Daten oder Code für die Remote-Ausführung.

Für diesen Artikel habe ich Beispiel-Diagnosecode in drei Sprachen mithilfe der Google Data API-Clientbibliotheken für Java, .NET und Python geschrieben. In jedem Beispiel aktiviere ich Logging oder Debugging, authentifizieren mich mit Client-Login, erhalte eine Liste meiner Google-Tabellen und drucke deren Titel aus.

Java

Sie können die java.util.logging-Klassen verwenden, um die Logging-Ebenen (und folglich die Trafficdaten) für einige wichtige Objekte in der Clientbibliothek festzulegen. Im folgenden Beispiel habe ich mir die HTTP-Header und die Aktivitäten des XML-Parsers angesehen, um einen vollständigen Überblick über die Übertragung zu erhalten.

Die Google Data-Java-Clientbibliothek hat separate Klassen für die Verarbeitung von HTTP-Anfragen und XML-Parsing. Daher muss ich zwei Logger-Objekte erstellen, eines für jede Klasse: com.google.gdata.client.http.HttpGDataRequest übernimmt den HTTP-Traffic, während com.google.gdata.util.XmlParser für das XML-Parsing verantwortlich ist.

Die Logger-Instanzen zeichnen Aktivitäten für HttpGDataRequest und XmlParser auf. Sie können die Detailebene der Ausgabe jedes Loggers steuern. Für diese Demo habe ich ausgewählt, dass alle Ereignisse angezeigt werden, die von den Objekten HttpGDataRequest und XmlParser erzeugt werden.

Nachdem ich meine Logger erstellt und konfiguriert habe, muss ich ihnen mitteilen, was zu tun ist, wenn sie ein Ereignis von ihren Klassen erhalten. Ich möchte zuerst alle Logging-Informationen in die Konsole schreiben. Daher erstelle ich ein ConsoleHandler und füge es beiden Loggern hinzu.

Hier ist mein Beispielcode:

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

Wenn du dieses Programm ausführst, siehst du in der Konsole in etwa Folgendes (Ich habe einige der weniger interessanten Teile ausgeschnitten):

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

Diese Logs können ziemlich groß werden. Daher sollten Sie die Protokollierungsebenen selektiv festlegen. Sie könnten auch FileHandler anstelle von ConsoleHandler erstellen, um die Logdaten für die spätere Verwendung zu speichern.

Wenn Java nicht Ihre Tasche ist, könnten Sie .NET ausprobieren.

.NET

Zum Erfassen des HTTP-Traffics in der .NET-Clientbibliothek können Sie die Standardanfrage-Factory im Client durch GDataLoggingRequestFactory ersetzen.

Die HTTP-Anfragen in der .NET-Bibliothek werden vom GDataRequestFactory erstellt, der sich in jedem Dienstobjekt befindet. Die normalen Anfrage-Factorys führen kein Logging aus, aber GDataLoggingRequestFactory ist eine Unterklasse von GDataRequestFactory und verfügt über integriertes Logging. Sie können den vollständigen Pfad der Logdatei angeben. Legen Sie dazu CombinedFileName fest.

Nachdem Sie Ihre Anfrage-Factory eingerichtet haben, ersetzen Sie die Request-Factory in Ihrem Serviceobjekt durch Festlegen von RequestFactory des Dienstobjekts. Ihr Code könnte dann so aussehen:

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

Die resultierende Protokolldatei enthält die XML-Anfragen und -Antworten. Hier ist ein abgekürztes Beispiel, das ich mit tidy formatiert habe.

<?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>

Vielleicht ist Ihnen aber auch die Skriptsprache wichtig und Sie bevorzugen Python.

Python

Um den HTTP-Traffic in der Python-Clientbibliothek zu erfassen, können Sie den HTTP-Header-Traffic auf die Konsole übertragen. Aktivieren Sie dazu den Debug-Modus im HTTP-Client. Das Dienstobjekt hat ein Debug-Mitglied, das Sie auf True setzen können.

Wenn Sie „debug“ auf „true“ setzen, wird das Debug-Flag im zugrunde liegenden HTTPRequest-Objekt festgelegt, das im Dienstobjekt enthalten ist.

Das folgende Beispiel gibt die HTTP-Header zurück, die vom Tabellenserver gesendet werden, wenn Sie eine Liste Ihrer Tabellen anfordern.

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

Die Konsole sollte in etwa so aussehen:

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

Wenn Sie weitere Vorgänge wie das Einfügen oder Aktualisieren ausführen, werden die entsprechenden Anfragedaten an Ihre Konsole übertragen.

Fazit

In dieser kurzen Anleitung wird veranschaulicht, wie Sie einem Java-, .NET- oder Python-Programm, das die Google Data API-Clientbibliotheken verwendet, grundlegende Logging-Funktionen hinzufügen können. Diese Techniken können nützlich sein, wenn Sie Fehler in HTTP-Exchanges beheben müssen, aber keinen Zugriff auf einen Paket-Sniffer haben. Ich habe die Oberfläche nur mit diesen Beispielen zerkratzt. Viele der in diesen Sprachen vorhandenen Logging-Mechanismen sind viel leistungsfähiger als die hier gezeigten. Weitere Informationen zum Logging und zu den Google Data APIs finden Sie in der Ressourcenliste unten.

Die in diesem Artikel behandelten Clientbibliotheken finden Sie auf den folgenden Seiten:

Zugehörige Knowledge Base-Elemente:

Diskussionsgruppen: Es gibt etliche solcher Gruppen, zu denen immer mehr hinzukommen. Wir beobachten die Gruppen aktiv.

Wenn Sie Fragen oder Anregungen haben, können Sie sich gern an uns wenden. Werfen Sie einen Blick in die Diskussionsgruppe und beginnen Sie mit dem Posten.