Depuração de clientes da API Google Data: como analisar o tráfego no seu 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 passa pelo fio. Isso é especialmente verdadeiro ao escrever software que usa serviços da Web, como as APIs Google Data, em que muitas operações envolvem fazer solicitações HTTP. Quando tudo mais falhar, verifique se o programa está fazendo o que você espera conferindo os bytes transmitidos e recebidos. Muitas das bibliotecas de cliente das APIs de dados do Google têm um modo de depuração que mostra o tráfego HTTP. Isso é especialmente útil quando você não tem acesso a um sniffer de pacotes como WireShark ou Fiddler.

Não consigo contar quantas vezes jurei que meu programa estava correto, apenas para descobrir, ao inspecionar um rastreamento de pacotes, que havia 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 é como tentar passar uma agulha com os olhos fechados.

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

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

Java

Você pode usar as classes java.util.logging para definir os níveis de geração de registros (e, consequentemente, expor dados de tráfego) para alguns objetos principais 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 do Google Data tem classes separadas para processar solicitações HTTP e análise XML. Portanto, preciso criar dois objetos Logger, um para cada classe: com.google.gdata.client.http.HttpGDataRequest processa o tráfego HTTP, enquanto com.google.gdata.util.XmlParser é responsável pela análise XML.

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

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

Este é meu 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 esse programa, você verá algo assim 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

Esses registros podem ficar muito grandes. Por isso, é recomendável ser mais seletivo ao definir os níveis dos gravadores. Você também pode criar um FileHandler em vez de um ConsoleHandler para armazenar os dados de registro e usar depois.

Se Java não for sua praia, tente o .NET.

.NET

Para capturar o tráfego HTTP na biblioteca de cliente .NET, substitua a fábrica de solicitações 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 de serviço. As fábricas de solicitações normais não realizam nenhum registro, mas a GDataLoggingRequestFactory, que é uma subclasse da GDataRequestFactory, tem registro integrado. É possível especificar o caminho completo do arquivo de registro definindo CombinedFileName.

Depois de configurar a fábrica de solicitações, substitua-a no objeto de serviço 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. Confira 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 Python.

Python

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

Definir "debug" como "true" vai definir a flag de depuração no objeto HTTPRequest subjacente, que está contido no objeto de serviço.

Confira um exemplo que vai repetir os cabeçalhos HTTP enviados do servidor de planilhas quando você pedir uma lista das suas 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

E você vai encontrar algo assim no 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

À medida que você realiza outras operações, como uma inserção ou atualização, os dados de solicitação correspondentes são mostrados no console.

Conclusão

Este breve tutorial mostrou como adicionar funcionalidades básicas de geração de registros a um programa em 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 sniffer de pacotes. Esses exemplos são apenas uma pequena parte do que é possível fazer. Muitos dos mecanismos de geração de registros presentes nessas linguagens são muito mais poderosos do que o mostrado aqui. Se quiser mais informações sobre o registro em log 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 vários, e mais serão lançados à medida que mais APIs de dados do Google forem disponibilizadas. Monitoramos ativamente os grupos.

Se você tiver dúvidas ou sugestões, entre em contato. Participe do grupo de discussão e comece a postar.