Google Data API 클라이언트 디버깅: 프로그램 내에서 트래픽 탐색

Jeffrey Scudder, Google 데이터 API팀
2007년 6월

소개

때로는 네트워크를 통해 무언가를 보는 것에 대한 대안이 없습니다. 특히 많은 작업이 HTTP 요청을 수반하는 Google Data API와 같은 웹 서비스를 사용하는 소프트웨어를 작성할 때 그렇습니다. 다른 모든 방법이 실패하면 실제 전송 및 수신된 바이트를 확인하여 프로그램이 예상한 대로 실행되는지 확인할 수 있습니다. Google Data API용 클라이언트 라이브러리에는 대부분 HTTP 트래픽을 표시하는 디버깅 모드가 있습니다. 이 기능은 WireShark 또는 Fiddler와 같은 패킷 스니퍼에 액세스할 수 없는 경우에 특히 유용합니다.

프로그램이 정확하다고 확신할 수 있었던 횟수를 계산할 수 없습니다. 여분의 줄바꿈 문자나 이름이 잘못된 HTTP 헤더가 있는지 패킷 트레이스를 검사한 결과를 찾았습니다. HTTP 트래픽을 보지 않고 웹 서비스를 대상으로 프로그래밍하는 것은 눈을 감은 상태로 바늘을 끼우는 것과 같습니다.

그러나 패킷 스니퍼를 사용할 수 없거나 암호화된 패킷을 처리하기에 부적절할 수 있습니다. 일부 프로그램 내 로깅 메커니즘을 활용하면 이러한 한계를 극복할 수 있습니다. 이러한 로깅 기능을 활용하면 암호화된 HTTPS 데이터나 원격 실행 코드의 경우에도 교환된 데이터의 일부(전부는 아님)를 확인할 수 있습니다.

이 문서에서는 자바, .NET, Python용 Google Data API 클라이언트 라이브러리를 사용하여 3개 언어로 샘플 진단 코드를 작성했습니다. 각 예에서는 로깅 또는 디버깅을 사용 설정하고 클라이언트 로그인을 사용하여 인증한 다음 Google 스프레드시트 목록을 가져와 제목을 출력합니다.

자바

java.util.logging 클래스를 사용하여 클라이언트 라이브러리에서 몇 가지 주요 객체의 로깅 수준을 설정하고 따라서 트래픽 데이터를 노출할 수 있습니다. 아래 예시에서는 HTTP 헤더와 XML 파서의 활동을 확인하여 네트워크를 통해 전달되는 내용의 전체 뷰를 선택했습니다.

Google 데이터 자바 클라이언트 라이브러리에는 HTTP 요청과 XML 파싱을 처리하는 별도의 클래스가 있습니다. 따라서 클래스마다 하나씩 두 개의 로거 객체를 만들어야 합니다. com.google.gdata.client.http.HttpGDataRequest는 HTTP 트래픽을 처리하는 반면 com.google.gdata.util.XmlParser는 XML 파싱을 담당합니다.

로거 인스턴스는 HttpGDataRequestXmlParser의 활동을 기록하며 각 로거 출력의 세부정보 수준을 제어할 수 있습니다. 이 데모에서는 HttpGDataRequest 객체와 XmlParser 객체에서 생성된 모든 이벤트를 보도록 선택했습니다.

로거를 만들고 구성한 후에는 수업의 이벤트를 수신할 때 로거를 어떻게 해야 하는지 알려야 합니다. 지금은 모든 로깅 정보를 콘솔에 쓰고 있으므로 ConsoleHandler를 만들어 두 로거에 추가합니다.

다음은 샘플 코드입니다.

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

이러한 로그는 매우 클 수 있으므로 로거 수준을 더 신중하게 설정하는 것이 좋습니다. ConsoleHandler 대신 FileHandler를 만들어 나중에 사용할 수 있도록 로그 데이터를 저장할 수도 있습니다.

물론 Java를 사용할 수 없다면 .NET을 사용해 볼 수도 있습니다.

.NET

.NET 클라이언트 라이브러리에서 HTTP 트래픽을 캡처하려면 클라이언트의 기본 요청 팩토리를 GDataLoggingRequestFactory로 바꿉니다.

.NET 라이브러리의 HTTP 요청은 각 서비스 객체 내에 있는 GDataRequestFactory에 의해 생성됩니다. 일반 요청 팩토리는 로깅을 실행하지 않지만 GDataRequestFactory의 서브클래스인 GDataLoggingRequestFactory에는 로깅이 내장되어 있습니다. 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로 설정할 수 있는 디버그 구성원이 있습니다.

debug를 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 클라이언트 라이브러리를 사용하는 자바, .NET 또는 Python 프로그램에 기본 로깅 기능을 추가하는 방법을 보여줍니다. 이러한 기법은 HTTP 교환을 디버그해야 하지만 패킷 스니퍼에 대한 액세스 권한이 없는 경우에 유용할 수 있습니다. 이 예시로는 표면만 긁었습니다. 이러한 언어로 제공되는 많은 로깅 메커니즘은 여기에 표시된 것보다 훨씬 더 강력합니다. 로깅 또는 Google 데이터 API에 대한 자세한 내용은 아래 리소스 목록을 참조하세요.

이 문서에서 다루는 클라이언트 라이브러리는 다음 페이지에서 찾을 수 있습니다.

관련 기술 자료 항목:

토론방: 꽤 많은 그룹이 있으며 더 많은 Google 데이터 API가 출시되면 더 많은 그룹이 추가될 예정입니다. 그룹을 적극적으로 모니터링합니다.

질문이나 제안이 있으면 언제든지 알려 주시기 바랍니다. 토론 그룹으로 이동하여 게시를 시작하세요.