调试 Google Data API 客户端:从程序内部探索流量

Jeffrey Scudder,Google Data API 团队
2007 年 6 月

简介

有时,没有比查看通过线路传输的内容更好的方法了。在编写使用 Google Data API 等 Web 服务的软件时,尤其如此,因为许多操作都涉及发出 HTTP 请求。如果其他方法都失败了,您可以查看实际传输和接收的字节,验证程序是否按预期运行。许多 Google Data API 的客户端库都具有调试模式,可显示 HTTP 流量。如果您无法访问 WireSharkFiddler 等数据包嗅探器,此功能尤其有用。

我数不清有多少次,我本以为自己的程序是正确的,但检查数据包跟踪后才发现,其中多了一个换行符或 HTTP 标头命名错误。在不查看 HTTP 流量的情况下针对 Web 服务进行编程,就像闭着眼睛尝试穿针一样。

不过,您可能会遇到数据包嗅探器不可用或无法处理加密数据包的情况。别担心,您可以利用一些程序内日志记录机制来规避此限制。通过利用这些日志记录功能,您可以查看部分(如果不是全部)交换的数据,即使是加密的 HTTPS 数据或远程运行的代码也是如此。

在本文中,我使用 Google Data API 客户端库Java.NETPython 编写了示例诊断代码。在每个示例中,我都开启了日志记录或调试功能,使用客户端登录进行身份验证,然后获取我的 Google 电子表格列表并打印出它们的标题。

Java

您可以使用 java.util.logging 类为客户端库中的几个关键对象设置日志记录级别(并相应地公开流量数据)。在下面的示例中,我选择查看 HTTP 标头和 XML 解析器的活动,以全面了解通过网络传输的内容。

Google Data Java 客户端库具有单独的类来处理 HTTP 请求和 XML 解析;因此,我需要创建两个 Logger 对象,每个类一个:com.google.gdata.client.http.HttpGDataRequest 处理 HTTP 流量,而 com.google.gdata.util.XmlParser 负责 XML 解析。

记录器实例将记录 HttpGDataRequestXmlParser 的活动,您可以控制每个记录器输出的详细程度。在此演示中,我选择查看 HttpGDataRequestXmlParser 对象生成的所有事件。

创建并配置记录器后,我需要告知它们在收到来自其类的事件时应执行的操作。目前,我希望将所有日志记录信息输出到控制台,因此我创建了一个 ConsoleHandler 并将其添加到我的两个 Logger 中。

以下是我的示例代码:

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

运行此程序时,您会在控制台上看到类似如下所示的内容(我省略了一些不太有趣的部分):

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

这些日志可能会非常大,因此您可能需要更谨慎地设置记录器的级别。您还可以创建 FileHandler 而不是 ConsoleHandler,以便存储日志数据以供日后使用。

当然,如果您不喜欢 Java,也可以尝试 .NET。

.NET

如需在 .NET 客户端库中捕获 HTTP 流量,您可以使用 GDataLoggingRequestFactory 替换客户端中的默认请求工厂。

.NET 库中的 HTTP 请求由每个服务对象内的 GDataRequestFactory 创建。常规请求工厂不执行任何日志记录,但 GDataLoggingRequestFactoryGDataRequestFactory 的子类)内置了日志记录功能。您可以通过设置 CombinedFileName 来指定日志文件的完整路径。

设置请求工厂后,您需要通过设置服务对象的 RequestFactory 来替换服务对象中的请求工厂。 您的代码可能如下所示:

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

生成的日志文件包含 XML 请求和响应。下面是一个简短的示例,我已使用 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>

但也许您非常喜欢脚本语言,并且更喜欢使用 Python。

Python

如需在 Python 客户端库中捕获 HTTP 流量,您可以在 HTTP 客户端中开启调试模式,将 HTTP 标头流量回显到控制台。服务对象有一个调试成员,您可以将其设置为 True

将调试设置为 true 会在服务对象中包含的基础 HTTPRequest 对象中设置调试标志。

以下示例将回显您请求列出电子表格时从电子表格服务器发送的 HTTP 标头。

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

您会在控制台上看到类似如下的内容:

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

当您执行其他操作(例如插入或更新)时,您会在控制台中看到相应的请求数据。

总结

本简短教程演示了如何将基本日志记录功能添加到使用 Google Data API 客户端库的 Java、.NET 或 Python 程序中。如果您需要调试 HTTP 交换,但无法访问数据包嗅探器,这些技巧可能会很有用。这些示例只是冰山一角。这些语言中的许多日志记录机制都比此处显示的机制强大得多。如果您想详细了解日志记录或 Google Data API,请查看下面的资源列表。

本文中介绍的客户端库可在以下页面中找到:

相关知识库内容:

讨论组:我们有许多讨论组,随着更多 Google Data API 的推出,讨论组数量还会增加。我们会积极监控群组。

如果您有任何问题或建议,欢迎随时与我联系。加入讨论组,然后开始发帖。