Depuración de clientes de la API de datos de Google: explorar el tráfico desde el programa

Jeffrey Scudder, equipo de API de datos de Google
junio de 2007

Introducción

A veces, no hay sustituto para ver lo que pasa por el cable. Esto es especialmente cierto cuando se escribe software que usa servicios web como las API de datos de Google, en las cuales muchas operaciones implican realizar solicitudes HTTP. Cuando todo lo demás falla, puedes ver los bytes reales transmitidos y recibidos para verificar que el programa esté haciendo lo que esperas. Muchas de las bibliotecas cliente para las API de datos de Google tienen un modo de depuración que muestra el tráfico HTTP. Esto es muy útil cuando no tienes acceso a un detector de paquetes como WireShark o Fiddler.

No puedo contar la cantidad de veces que podría haber jurado que mi programa era correcto, solo para inspeccionar un seguimiento de paquete de que hubiera un carácter de salto de línea adicional o un encabezado HTTP con nombre incorrecto. Programar en un servicio web sin mirar el tráfico HTTP puede ser como tratar de enrollar la aguja con los ojos pegados.

Sin embargo, es posible que te encuentres en una situación en la que un detector de paquetes no está disponible o no es adecuado para lidiar con paquetes encriptados. No te preocupes. Puedes evitar esta limitación si usas algunos mecanismos de registro dentro del programa. Mediante el uso de estas instalaciones de registro, puedes ver algunos de los datos intercambiados, si no todos, incluso para los datos HTTPS encriptados o el código de ejecución remota.

En este artículo, se escribió un código de diagnóstico de muestra en 3 idiomas mediante las bibliotecas cliente de la API de datos de Google para Java, .NET y Python. En cada ejemplo, activo el registro o la depuración, realizo la autenticación mediante el acceso de clientes y, luego, obtengo una lista de mis hojas de cálculo de Google y se imprimen los títulos.

Java

Puedes usar las clases java.util.logging a fin de establecer los niveles de registro (y, en consecuencia, exponer los datos de tráfico) para un par de objetos clave en la biblioteca cliente. En el siguiente ejemplo, elegí ver los encabezados HTTP y las actividades del analizador de XML para obtener una vista completa de lo que se transmite por cable.

La biblioteca cliente de Java de datos de Google tiene clases separadas para manejar las solicitudes HTTP y el análisis XML. Por lo tanto, debo crear dos objetos Logger, uno para cada clase: com.google.gdata.client.http.HttpGDataRequest controla el tráfico HTTP, mientras que com.google.gdata.util.XmlParser es responsable del análisis XML.

Las instancias de registrador registrarán actividades para HttpGDataRequest y XmlParser, y puedes controlar el nivel de detalle de la salida de cada registrador. Para esta demostración, elegí ver todos los eventos que producen los objetos HttpGDataRequest y XmlParser.

Una vez que creé mis registradores y los configuré, debo decirles qué hacer cuando reciben un evento de sus clases. Por ahora, quiero escribir toda la información de registro en la consola, por lo que creo un ConsoleHandler y lo agrego a ambos registradores.

Este es mi código de muestra:

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

Cuando ejecutes este programa, verás algo como esto en la consola (recorté algunas de las partes menos interesantes):

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

Estos registros pueden ser bastante grandes, por lo que es posible que desees ser más selectivo cuando configures los niveles de los registradores. También puedes crear un FileHandler en lugar de un ConsoleHandler a fin de almacenar los datos de registro para usarlos más adelante.

Por supuesto, si Java no es tu mochila, puedes probar .NET.

.NET

Para capturar el tráfico HTTP en la biblioteca cliente de .NET, puedes reemplazar la fábrica de solicitudes predeterminada en el cliente por un GDataLoggingRequestFactory.

Las solicitudes HTTP en la biblioteca .NET se crean mediante el GDataRequestFactory que se encuentra dentro de cada objeto Service. Las fábricas de solicitudes normales no realizan ningún registro, pero GDataLoggingRequestFactory, que es una subclase de GDataRequestFactory, tiene un registro integrado. Puedes especificar la ruta de acceso completa del archivo de registro si configuras CombinedFileName.

Después de configurar la fábrica de solicitudes, debes reemplazar la fábrica de objetos de servicio por la configuración del RequestFactory del objeto de servicio. El código podría verse de la siguiente manera:

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

El archivo de registro resultante contiene las solicitudes y respuestas XML. A continuación, se muestra un ejemplo abreviado al que le asigné un formato con 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>

Pero tal vez te interesen mucho los lenguajes de programación y prefieres usar Python.

Python

Para capturar el tráfico HTTP en la biblioteca cliente de Python, puede hacer eco del tráfico del encabezado HTTP en la consola. Para ello, active el modo de depuración en el cliente HTTP. El objeto de servicio tiene un miembro de depuración que puedes configurar como True.

Si estableces la depuración como verdadera, se establecerá la marca de depuración en el objeto HTTPRequest subyacente que se encuentra en el objeto de servicio.

Este es un ejemplo que reproducirá los encabezados HTTP que envía el servidor de hojas de cálculo cuando solicitas una lista de tus hojas de cálculo.

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

Verás algo como esto en tu consola:

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

A medida que realices más operaciones, como una inserción o actualización, verás los datos de solicitud correspondientes en tu consola.

Conclusión

En este instructivo breve, se ilustra cómo puedes agregar la funcionalidad de registro básica en un programa de Java, .NET o Python que usa las bibliotecas cliente de la API de datos de Google. Estas técnicas pueden ser útiles si necesitas depurar intercambios HTTP, pero no tienes acceso a un detector de paquetes. Con estos ejemplos, solo rayé la superficie. Muchos de los mecanismos de registro presentes en estos lenguajes son mucho más potentes que los que se muestran aquí. Si deseas obtener más información sobre el registro o las API de datos de Google, consulta la lista de recursos a continuación.

Las bibliotecas cliente incluidas en este artículo se encuentran en las siguientes páginas:

Elementos relacionados de la base de conocimiento:

Grupos de discusión: tenemos algunos, y se sumarán más a las API de datos de Google. Supervisamos los grupos de forma activa.

Si tienes preguntas o sugerencias, será un placer conocerte. Participa en el grupo de discusión y comienza a publicar.