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

Jeffrey Scudder, צוות Google Data APIs
יוני 2007

מבוא

לפעמים אין תחליף. זה נכון במיוחד כשכותבים תוכנה שמשתמשת בשירותי אינטרנט כמו Google Data APIs, הרבה פעולות שקשורות לשליחת בקשות HTTP. אם שום דבר אחר לא יצליח, תוכלו לוודא שהתוכנית שלכם פועלת בהתאם לציפיות שלכם על ידי הצגת הבייטים שהועברו והתקבלו בפועל. לספריות לקוח רבות של ממשקי API של נתוני Google יש מצב ניפוי באגים, שמציג את תנועת ה-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, ולכן עליי ליצור שני אובייקטים של יומן רישום לכל מחלקה: com.google.gdata.client.http.HttpGDataRequest מטפל בתנועת HTTP בעוד com.google.gdata.util.XmlParser אחראי על ניתוח XML.

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

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

כמובן, אם Java אינו התיק שלך, תוכל לנסות את .NET

‎.NET

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

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

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

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

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

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

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