Added logging support into tweepy.
authorJosh Roesslein <jroesslein@gmail.com>
Thu, 24 Sep 2009 19:43:45 +0000 (14:43 -0500)
committerJosh Roesslein <jroesslein@gmail.com>
Thu, 24 Sep 2009 19:43:45 +0000 (14:43 -0500)
CHANGES
tweepy/__init__.py
tweepy/api.py
tweepy/binder.py
tweepy/logging.py [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index ee2151424c920f25c523c75a560dc4ae1cfa10ee..9eb8de01750431fe9055cb47aabca70ccc17739d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -19,6 +19,10 @@ during upgrade will be listed here.
       get_authorization_url() now takes a boolean that when
       true uses the "sign in with twitter" flow.
       See http://apiwiki.twitter.com/Sign-in-with-Twitter
++ Logging
+    + Added TweepyLogger interface which allows applications
+      to collect log messages from Tweepy for debugging purposes.
+    + Dummy, console, and file loggers available
 + Examples
     + Appengine demo (oauth)
 + Documentation of each method in api.py
index 29c9771a409c0dd10bd03362d4d354943f5d4cab..e9ed4f16b5ccfb395dd4d7c4f06fcab7ddfc8527 100644 (file)
@@ -13,6 +13,7 @@ from . api import API
 from . cache import Cache, MemoryCache, FileCache, MemCache
 from . auth import BasicAuthHandler, OAuthHandler
 from . streaming import Stream, StreamListener
+from . logging import TweepyLogger, DummyLogger, ConsoleLogger, FileLogger
 
 # Global, unauthenticated instance of API
 api = API()
index 8e0038bc038f4e9b8339c8cdc114929229959cd4..e5c1f92806375b1eda15f3c6f1f5a8bc5a742bf0 100644 (file)
@@ -8,6 +8,7 @@ import mimetypes
 from . binder import bind_api
 from . error import TweepError
 from . auth import BasicAuthHandler, OAuthHandler
+from . logging import DummyLogger
 from tweepy.parsers import *
 
 
@@ -15,7 +16,7 @@ class API(object):
     """Twitter API"""
 
     def __init__(self, auth_handler=None, host='twitter.com', cache=None,
-            secure=False, api_root='', validate=True):
+            secure=False, api_root='', validate=True, logger=DummyLogger()):
         # you may access these freely
         self.auth_handler = auth_handler
         self.host = host
@@ -23,6 +24,7 @@ class API(object):
         self.cache = cache
         self.secure = secure
         self.validate = validate
+        self.logger = logger
 
         # not a good idea to touch these
         self._username = None
index 89626a89448bd995b7f5c10b901ca4d6fb0b7c18..ef48d098c67c7d913ebe610794b0110b253fcca7 100644 (file)
@@ -17,10 +17,16 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
         if require_auth and not api.auth_handler:
             raise TweepError('Authentication required!')
 
+        # Log some useful infomation
+        api.logger.debug('Starting request...')
+        api.logger.debug('  path: %s' % path)
+        api.logger.debug('  method: %s' % method)
+
         # check for post_data parameter
         if 'post_data' in kargs:
             post_data = kargs['post_data']
             del kargs['post_data']
+            api.logger.debug('  post data: %s' % post_data)
         else:
             post_data = None
 
@@ -30,6 +36,7 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
             del kargs['headers']
         else:
             headers = {}
+        api.logger.debug('  headers: %s' % headers)
 
         # build parameter dict
         if allowed_param:
@@ -51,6 +58,7 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
             if len(args) > 0 or len(kargs) > 0:
                 raise TweepError('This method takes no parameters!')
             parameters = None
+        api.logger.debug('  parameters: %s' % parameters)
 
         # Build url with parameters
         if parameters:
@@ -67,7 +75,10 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
 
         # Apply authentication
         if api.auth_handler:
-            api.auth_handler.apply_auth(scheme + _host + url, method, headers, parameters)
+            api.auth_handler.apply_auth(
+                    scheme + _host + url,
+                    method, headers, parameters
+            )
 
         # Check cache if caching enabled and method is GET
         if api.cache and method == 'GET':
@@ -80,6 +91,7 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
                         result._api = api
                 else:
                     cache_result._api = api
+                api.logger.debug("Cache hit!")
                 return cache_result
 
         # Open connection
@@ -94,6 +106,9 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
 
         # Get response
         resp = conn.getresponse()
+        api.logger.debug('Received response...')
+        api.logger.debug('  headers: %s' % resp.getheaders())
+        api.logger.debug('  status code: %s' % resp.status)
 
         # If an error was returned, throw an exception
         if resp.status != 200:
@@ -101,12 +116,14 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
                 error_msg = parse_error(resp.read())
             except Exception:
                 error_msg = "Twitter error response: status code = %s" % resp.status
+            api.logger.error('  Error: %s' % error_msg)
             raise TweepError(error_msg)
 
         # Pass returned body into parser and return parser output
         try:
             out = parser(resp.read(), api)
         except Exception:
+            api.logger.error("  parse error!")
             raise TweepError("Failed to parse returned data")
 
         conn.close()
@@ -126,6 +143,9 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
         # store result in cache
         if api.cache and method == 'GET':
             api.cache.store(url, out)
+            api.logger.debug("  caching result")
+
+        api.logger.debug('request done.')
 
         return out
 
diff --git a/tweepy/logging.py b/tweepy/logging.py
new file mode 100644 (file)
index 0000000..dc57c6f
--- /dev/null
@@ -0,0 +1,58 @@
+# Tweepy
+# Copyright 2009 Joshua Roesslein
+# See LICENSE
+
+class TweepyLogger(object):
+
+    DEBUG = 1
+    WARNING = 2
+    ERROR = 3
+
+    def debug(self, message):
+        """Output a debug log message"""
+        self.log(TweepyLogger.DEBUG, message)
+
+    def warning(self, message):
+        """Output warning log message"""
+        self.log(TweepyLogger.WARNING, message)
+
+    def error(self, message):
+        """Output error log message"""
+        self.log(TweepyLogger.ERROR, message)
+
+    def log(self, level, message):
+        """Implement this method to handle log messages"""
+        raise NotImplementedError
+
+    def format(self, message):
+        """Override this method to apply custom formating of messages"""
+        return message
+
+class DummyLogger(TweepyLogger):
+    """This logger just discards log messages"""
+
+    def log(self, level, message):
+        return
+
+class ConsoleLogger(TweepyLogger):
+    """Outputs log messages to stdout"""
+
+    def __init__(self, active_log_level=TweepyLogger.DEBUG):
+        self.active_log_level = active_log_level
+
+    def log(self, level, message):
+        if level <= self.active_log_level:
+            print message
+
+class FileLogger(TweepyLogger):
+    """Outputs log message to file"""
+
+    def __init__(self, filepath, active_log_level=TweepyLogger.DEBUG):
+        self.active_log_level = active_log_level
+        self.file = open(filepath, 'w')
+
+    def log(self, level, message):
+        if level <= self.active_log_level:
+            self.file.write(message + '\n')
+            self.file.flush()
+