ניפוי באגים בלקוחות של Google Data API: בדיקת התנועה מתוך התוכנית

ג'פרי סקאדר (Jeffrey Scudder), צוות Google Data APIs
יוני 2007

מבוא

לפעמים אין תחליף לצפייה בנתונים שמועברים בכבל. זה נכון במיוחד כשכותבים תוכנה שמשתמשת בשירותי אינטרנט כמו Google Data APIs, שבהם הרבה פעולות כוללות שליחת בקשות HTTP. אם כל השאר נכשל, אפשר לוודא שהתוכנית עושה את מה שציפיתם על ידי בדיקת הבייטים ששודרו ושהתקבלו בפועל. לרבות מספריות הלקוח של Google Data APIs יש מצב ניפוי באגים שמציג את תעבורת ה-HTTP. האפשרות הזו שימושית במיוחד אם אין לכם גישה לכלי לניתוח חבילות כמו WireShark או Fiddler.

אני לא יכול לספור את הפעמים שנשבעתי שהתוכנית שלי נכונה, רק כדי לגלות אחרי בדיקה של מעקב מנות שיש תו נוסף של שורה חדשה, או כותרת HTTP עם שם שגוי. תכנות מול שירות אינטרנט בלי לבדוק את תעבורת ה-HTTP דומה לניסיון להשחיל חוט במחט עם עיניים עצומות.

עם זאת, יכול להיות שתיתקלו במצב שבו אין אפשרות להשתמש בכלי לניתוח חבילות או שהכלי לא מתאים לטיפול בחבילות מוצפנות. אל דאגה – אפשר לעקוף את ההגבלה הזו באמצעות מנגנונים מסוימים של רישום ביומן בתוך התוכנית. באמצעות מתקני הרישום ביומן האלה, אתם יכולים לראות חלק מהנתונים שהועברו, אם לא את כולם, גם אם מדובר בנתוני HTTPS מוצפנים או בקוד שמופעל מרחוק.

לצורך המאמר הזה, כתבתי קוד אבחון לדוגמה ב-3 שפות באמצעות ספריות הלקוח של Google Data API ל-Java, ל-‎.NET ול-Python. בכל דוגמה, אני מפעיל רישום ביומן או ניפוי באגים, מבצע אימות באמצעות כניסה של לקוח, ואז מקבל רשימה של גיליונות אלקטרוניים ב-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.

מופעים של כלי הרישום יתעדו פעילויות עבור HttpGDataRequest ו-XmlParser, ותוכלו לשלוט ברמת הפירוט של הפלט של כל כלי רישום. לצורך ההדגמה הזו, בחרתי להציג את כל האירועים שנוצרו על ידי האובייקטים HttpGDataRequest ו-XmlParser.

אחרי שאצור ואגדיר את ה-Loggers, אצטרך להגדיר להם מה לעשות כשהם מקבלים אירוע מהכיתות שלהם. בשלב הזה, אני רוצה לכתוב את כל פרטי הרישום במסוף, אז אני יוצר 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

כדי לתעד את תעבורת ה-HTTP בספריית הלקוח של ‎ .NET, אפשר להחליף את יצרן הבקשות שמוגדר כברירת מחדל בלקוח ב-GDataLoggingRequestFactory.

בקשות ה-HTTP בספריית ‎ .NET נוצרות על ידי GDataRequestFactory שנמצא בתוך כל אובייקט Service. הפקטורים הרגילים של הבקשות לא מבצעים רישום ביומן, אבל GDataLoggingRequestFactory, שהוא מחלקת משנה של GDataRequestFactory, כולל רישום ביומן. כדי לציין את הנתיב המלא של קובץ היומן, מגדירים את 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

כדי לתעד את תעבורת ה-HTTP בספריית הלקוח של Python, אפשר להפעיל את מצב הניפוי באגים בלקוח ה-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

כשמבצעים פעולות נוספות, כמו הוספה או עדכון, נתוני הבקשה המתאימים מוצגים במסוף.

סיכום

במדריך הקצר הזה הסברנו איך להוסיף פונקציונליות בסיסית של רישום ביומן לתוכנית Java,‏ ‎.NET או Python שמשתמשת בספריות הלקוח של Google Data API. הטכניקות האלה יכולות להיות שימושיות אם אתם צריכים לנפות באגים ב-HTTP Exchanges, אבל אין לכם גישה לכלי לניתוח חבילות. הדוגמאות האלה הן רק קצה הקרחון. הרבה ממנגנוני הרישום ביומן שקיימים בשפות האלה הם הרבה יותר חזקים ממה שמוצג כאן. אם אתם רוצים לקבל מידע נוסף על רישום ביומן או על Google Data APIs, תוכלו לעיין ברשימת המקורות שבהמשך.

ספריות הלקוח שמוזכרות במאמר הזה מופיעות בדפים הבאים:

פריטים קשורים במאגר הידע:

קבוצות דיון: יש לנו לא מעט קבוצות דיון, ועוד יתווספו ככל שייפרסו עוד ממשקי Google Data API. אנחנו עוקבים באופן פעיל אחרי הקבוצות.

אם יש לך שאלות או הצעות, אשמח לשמוע ממך. אפשר להצטרף לקבוצת הדיון ולפרסם הודעות.