Como depurar clientes da API Google Data: como explorar o tráfego no programa

Jeffrey Scudder, equipe das APIs de dados do Google
junho de 2007

Introdução

Às vezes, não há substituto para ver o que acontece na rede. Isso ocorre principalmente ao escrever softwares que usam serviços da Web como as APIs de dados do Google, em que muitas operações envolvem a realização de solicitações HTTP. Quando todo o restante falha, é possível verificar se o programa está fazendo o que é esperado ver os bytes reais transmitidos e recebidos. Muitas das bibliotecas de cliente das APIs de dados do Google têm um modo de depuração que exibe o tráfego HTTP. Isso é especialmente útil quando você não tem acesso a um pacote MapReduce, como WireShark ou GoDaddy.

Não consigo contar o número de vezes que posso dizer que meu programa estava correto, apenas para descobrir ao inspecionar um trace de pacote que houve um caractere de nova linha extra ou um cabeçalho HTTP com nome incorreto. Programar um serviço da Web sem analisar o tráfego HTTP pode ser uma tentativa de criar uma linha de execução com os olhos fechados.

No entanto, você pode se deparar com uma situação em que um detector de pacotes não está disponível ou é inadequado para lidar com pacotes criptografados. Não se preocupe, você pode contornar essa limitação aproveitando alguns mecanismos de geração de registros no programa. Ao utilizar esses recursos de geração de registros, é possível ver alguns ou todos os dados trocados, mesmo para dados HTTPS criptografados ou código de execução remota.

Neste artigo, escrevi um código de diagnóstico de exemplo em três linguagens usando as bibliotecas de cliente da API Google Data para Java, .NET e Python. Em cada exemplo, ativo a geração de registros ou a depuração, autentico usando o login de cliente e, em seguida, recebo uma lista das minhas planilhas do Google e imprimi os títulos delas.

Java

É possível usar as classes java.util.logging para definir os níveis de geração de registros (e, consequentemente, expor dados de tráfego) a alguns objetos-chave na biblioteca de cliente. No exemplo abaixo, escolhi analisar os cabeçalhos HTTP e as atividades do analisador XML para ter uma visão completa do que está sendo transmitido pela rede.

A biblioteca de cliente Java dos dados do Google tem classes separadas para lidar com solicitações HTTP e análise XML. Assim, precisamos criar dois objetos logger, um para cada classe: com.google.gdata.client.http.HttpGDataRequest gerencia o tráfego HTTP enquanto com.google.gdata.util.XmlParser é responsável pela análise XML.

As instâncias do logger vão gravar atividades para HttpGDataRequest e XmlParser, e você pode controlar o nível de detalhes da saída de cada um. Para essa demonstração, escolhi visualizar todos os eventos produzidos pelos objetos HttpGDataRequest e XmlParser.

Depois de criar e configurar meus loggers, preciso dizer o que eles devem fazer quando receberem um evento das classes deles. Por enquanto, quero gravar todas as informações de registro no console. Portanto, crio um ConsoleHandler e o adiciono aos dois loggers.

Veja um exemplo de código:

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

Ao executar o programa, você verá algo parecido com isto no console: Cortei algumas das partes menos interessantes:

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

Como esses registros podem ficar muito grandes, é possível ser mais seletivo em relação aos níveis dos registradores. Também é possível criar um FileHandler em vez de um ConsoleHandler para permitir que você armazene os dados de registro para uso posterior.

É claro que se Java não for sua bolsa, tente .NET.

.NET

Para capturar o tráfego HTTP na biblioteca de cliente .NET, é possível substituir a fábrica de solicitação padrão no cliente por um GDataLoggingRequestFactory.

As solicitações HTTP na biblioteca .NET são criadas pelo GDataRequestFactory que está dentro de cada objeto Service. As fábricas de solicitações normais não realizam nenhuma geração de registros, mas a GDataLoggingRequestFactory, que é uma subclasse de GDataRequestFactory, tem geração de registros integrada. É possível especificar o caminho completo do arquivo de registros definindo CombinedFileName.

Depois de configurar a fábrica de solicitações, substitua a fábrica de objetos no objeto Service definindo o RequestFactory do objeto de serviço. O código vai ficar assim:

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

O arquivo de registro resultante contém as solicitações e respostas XML. Veja um exemplo abreviado que formatei usando 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>

Mas talvez você goste muito de linguagens de script e prefira usar o Python.

Python

Para capturar o tráfego HTTP na biblioteca de cliente Python, você pode ecoar o tráfego de cabeçalho HTTP para o console ativando o modo de depuração no cliente HTTP. O objeto de serviço tem um membro de depuração que pode ser definido como True.

Definir a depuração como verdadeira definirá a sinalização de depuração no objeto HTTPRequest subjacente contido no objeto de serviço.

Veja um exemplo que reproduzirá os cabeçalhos HTTP enviados do servidor de planilhas ao solicitar uma lista das planilhas.

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

Você verá algo parecido com isto no seu 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

Ao executar outras operações, como inserir ou atualizar, você verá os dados de solicitações correspondentes ecoados no console.

Conclusão

Neste breve tutorial, ilustramos como adicionar a funcionalidade básica de geração de registros a um programa Java, .NET ou Python que usa as bibliotecas de cliente da API Google Data. Essas técnicas podem ser úteis se você precisar depurar trocas HTTP, mas não tiver acesso a um identificador de pacotes. Abordamos apenas alguns exemplos. Muitos dos mecanismos de geração de registros presentes nessas linguagens são muito mais poderosos do que o mostrado aqui. Se você quiser mais informações sobre a geração de registros ou as APIs de dados do Google, confira a lista de recursos abaixo.

As bibliotecas de cliente abordadas neste artigo podem ser encontradas nestas páginas:

Itens relacionados da base de conhecimento:

Grupos de discussão: temos alguns, e mais serão disponibilizados à medida que mais APIs de dados do Google forem lançadas. Monitoramos os grupos ativamente.

Se tiver dúvidas ou sugestões, entre em contato. Entre no grupo de discussão e comece a postar.