Browse Source

Using logging throughout the bot now

tags/v0.1^2
Ben Kurtovic 13 years ago
parent
commit
d08c56f594
10 changed files with 74 additions and 55 deletions
  1. +3
    -3
      .gitignore
  2. +3
    -2
      bot/classes/connection.py
  3. +9
    -8
      bot/commands/__init__.py
  4. +1
    -1
      bot/config.py
  5. +5
    -3
      bot/frontend.py
  6. +29
    -23
      bot/main.py
  7. +13
    -12
      bot/tasks/__init__.py
  8. +4
    -1
      bot/watcher.py
  9. +4
    -0
      bot/wiki/__init__.py
  10. +3
    -2
      bot/wiki/site.py

+ 3
- 3
.gitignore View File

@@ -4,12 +4,12 @@
# Ignore bot-specific config file: # Ignore bot-specific config file:
config.json config.json


# Ignore logs directory:
logs/

# Ignore cookies file: # Ignore cookies file:
.cookies .cookies


# Ignore statistics file:
statistics.txt

# Ignore OS X's crud: # Ignore OS X's crud:
.DS_Store .DS_Store




+ 3
- 2
bot/classes/connection.py View File

@@ -12,12 +12,13 @@ class Connection(object):
"""A class to interface with IRC.""" """A class to interface with IRC."""
def __init__(self, host=None, port=None, nick=None, ident=None, def __init__(self, host=None, port=None, nick=None, ident=None,
realname=None):
realname=None, logger=None):
self.host = host self.host = host
self.port = port self.port = port
self.nick = nick self.nick = nick
self.ident = ident self.ident = ident
self.realname = realname self.realname = realname
self.logger = logger


# A lock to prevent us from sending two messages at once: # A lock to prevent us from sending two messages at once:
self.lock = threading.Lock() self.lock = threading.Lock()
@@ -50,7 +51,7 @@ class Connection(object):
# Ensure that we only send one message at a time with a blocking lock: # Ensure that we only send one message at a time with a blocking lock:
with self.lock: with self.lock:
self.sock.sendall(msg + "\r\n") self.sock.sendall(msg + "\r\n")
print " %s" % msg
self.logger.debug(msg)


def say(self, target, msg): def say(self, target, msg):
"""Send a private message to a target on the server.""" """Send a private message to a target on the server."""


+ 9
- 8
bot/commands/__init__.py View File

@@ -7,9 +7,9 @@ This package provides the IRC "commands" used by the bot's front-end component.
In __init__, you can find some functions used to load and run these commands. In __init__, you can find some functions used to load and run these commands.
""" """


import logging
import os import os
import sys import sys
import traceback


from classes import BaseCommand from classes import BaseCommand
import config import config
@@ -23,6 +23,9 @@ base_dir = os.path.join(config.root_dir, "bot", "commands")
# is an instance of the command's class: # is an instance of the command's class:
_commands = {} _commands = {}


# Logger for this module:
logger = logging.getLogger("tasks")

def _load_command(connection, filename): def _load_command(connection, filename):
"""Try to load a specific command from a module, identified by file name. """Try to load a specific command from a module, identified by file name.


@@ -39,8 +42,7 @@ def _load_command(connection, filename):
try: try:
__import__(name) __import__(name)
except: except:
print "Couldn't load file {0}:".format(filename)
traceback.print_exc()
logger.exception("Couldn't load file {0}".format(filename))
return return


command = sys.modules[name].Command(connection) command = sys.modules[name].Command(connection)
@@ -48,7 +50,7 @@ def _load_command(connection, filename):
return return


_commands[command.name] = command _commands[command.name] = command
print "Added command {0}...".format(command.name)
logger.debug("Added command {0}".format(command.name))


def load(connection): def load(connection):
"""Load all valid commands into the _commands global variable. """Load all valid commands into the _commands global variable.
@@ -67,8 +69,8 @@ def load(connection):
except AttributeError: except AttributeError:
pass # The file is doesn't contain a command, so just move on pass # The file is doesn't contain a command, so just move on


msg = "Found {0} commands: {1}."
print msg.format(len(_commands), ", ".join(_commands.keys()))
msg = "Found {0} commands: {1}"
logger.info(msg.format(len(_commands), ", ".join(_commands.keys())))


def get_all(): def get_all():
"""Return our dict of all loaded commands.""" """Return our dict of all loaded commands."""
@@ -85,6 +87,5 @@ def check(hook, data):
try: try:
command.process(data) command.process(data)
except: except:
print "Error executing command '{0}':".format(data.command)
traceback.print_exc()
logger.exception("Error executing command '{0}'".format(data.command))
break break

+ 1
- 1
bot/config.py View File

@@ -70,7 +70,7 @@ def _setup_logging():
stream_handler = logging.StreamHandler() stream_handler = logging.StreamHandler()


main_handler.setLevel(logging.INFO) main_handler.setLevel(logging.INFO)
error_handler.setLevel(logging.ERROR)
error_handler.setLevel(logging.WARNING)
debug_handler.setLevel(logging.DEBUG) debug_handler.setLevel(logging.DEBUG)
stream_handler.setLevel(logging.DEBUG) stream_handler.setLevel(logging.DEBUG)




+ 5
- 3
bot/frontend.py View File

@@ -9,6 +9,7 @@ of BaseCommand in irc/base_command.py. All command classes are automatically
imported by irc/command_handler.py if they are in irc/commands. imported by irc/command_handler.py if they are in irc/commands.
""" """


import logging
import re import re


import config import config
@@ -18,6 +19,7 @@ from classes import Connection, Data, BrokenSocketException
__all__ = ["get_connection", "startup", "main"] __all__ = ["get_connection", "startup", "main"]


connection = None connection = None
logger = logging.getLogger("frontend")
sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z") sender_regex = re.compile(":(.*?)!(.*?)@(.*?)\Z")


def get_connection(): def get_connection():
@@ -25,7 +27,7 @@ def get_connection():
connection, but don't actually connect yet.""" connection, but don't actually connect yet."""
cf = config.irc["frontend"] cf = config.irc["frontend"]
connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"], connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"],
cf["realname"])
cf["realname"], logger)
return connection return connection


def startup(conn): def startup(conn):
@@ -48,7 +50,7 @@ def main():
try: try:
read_buffer = read_buffer + connection.get() read_buffer = read_buffer + connection.get()
except BrokenSocketException: except BrokenSocketException:
print "Socket has broken on front-end; restarting bot..."
logger.warn("Socket has broken on front-end; restarting bot")
return return


lines = read_buffer.split("\n") lines = read_buffer.split("\n")
@@ -90,7 +92,7 @@ def _process_message(line):
# ordinary command): # ordinary command):
if data.msg in ["!restart", ".restart"]: if data.msg in ["!restart", ".restart"]:
if data.host in config.irc["permissions"]["owners"]: if data.host in config.irc["permissions"]["owners"]:
print "Restarting bot per owner request..."
logger.info("Restarting bot per owner request")
return True return True


# If we are pinged, pong back: # If we are pinged, pong back:


+ 29
- 23
bot/main.py View File

@@ -30,9 +30,9 @@ There is a "priority" system here:
Else, the bot will stop, as no components are enabled. Else, the bot will stop, as no components are enabled.
""" """


import logging
import threading import threading
import time import time
import traceback


import config import config
import frontend import frontend
@@ -49,13 +49,12 @@ def irc_watcher(f_conn=None):
while 1: # restart the watcher component if it breaks (and nothing else) while 1: # restart the watcher component if it breaks (and nothing else)
w_conn = watcher.get_connection() w_conn = watcher.get_connection()
w_conn.connect() w_conn.connect()
print # blank line to signify that the bot has finished starting up
try: try:
watcher.main(w_conn, f_conn) watcher.main(w_conn, f_conn)
except: except:
traceback.print_exc()
logging.exception("Watcher had an error")
time.sleep(5) # sleep a bit before restarting watcher time.sleep(5) # sleep a bit before restarting watcher
print "\nWatcher has stopped; restarting component..."
logging.warn("Watcher has stopped; restarting component")


def wiki_scheduler(): def wiki_scheduler():
"""Function to handle the wiki scheduler as another thread, or as the """Function to handle the wiki scheduler as another thread, or as the
@@ -77,12 +76,12 @@ def irc_frontend():
enabled.""" enabled."""
global f_conn global f_conn


print "Starting IRC frontend..."
logging.info("Starting IRC frontend")
f_conn = frontend.get_connection() f_conn = frontend.get_connection()
frontend.startup(f_conn) frontend.startup(f_conn)


if "wiki_schedule" in config.components: if "wiki_schedule" in config.components:
print "\nStarting wiki scheduler..."
logging.info("Starting wiki scheduler")
tasks.load() tasks.load()
t_scheduler = threading.Thread(target=wiki_scheduler) t_scheduler = threading.Thread(target=wiki_scheduler)
t_scheduler.name = "wiki-scheduler" t_scheduler.name = "wiki-scheduler"
@@ -90,7 +89,7 @@ def irc_frontend():
t_scheduler.start() t_scheduler.start()


if "irc_watcher" in config.components: if "irc_watcher" in config.components:
print "\nStarting IRC watcher..."
logging.info("Starting IRC watcher")
t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,)) t_watcher = threading.Thread(target=irc_watcher, args=(f_conn,))
t_watcher.name = "irc-watcher" t_watcher.name = "irc-watcher"
t_watcher.daemon = True t_watcher.daemon = True
@@ -105,40 +104,47 @@ def irc_frontend():
def run(): def run():
config.load() config.load()
try: try:
key = raw_input() # wait for our password decrypt key from the bot's
except EOFError: # wrapper, then decrypt passwords
# Wait for our password decrypt key from the bot's wrapper, then
# decrypt passwords:
key = raw_input()
except EOFError:
pass pass
else: else:
config.decrypt(key) config.decrypt(key)


enabled = config.components enabled = config.components


if "irc_frontend" in enabled: # make the frontend run on our primary
irc_frontend() # thread if enabled, and enable additional
# components through that function
if "irc_frontend" in enabled:
# Make the frontend run on our primary thread if enabled, and enable
# additional components through that function
irc_frontend()


elif "wiki_schedule" in enabled: # run the scheduler on the main
print "Starting wiki scheduler..." # thread, but also run the IRC
tasks.load() # watcher on another thread iff it
if "irc_watcher" in enabled: # is enabled
print "\nStarting IRC watcher..."
elif "wiki_schedule" in enabled:
# Run the scheduler on the main thread, but also run the IRC watcher on
# another thread iff it is enabled
logging.info("Starting wiki scheduler")
tasks.load()
if "irc_watcher" in enabled:
logging.info("Starting IRC watcher")
t_watcher = threading.Thread(target=irc_watcher) t_watcher = threading.Thread(target=irc_watcher)
t_watcher.name = "irc-watcher" t_watcher.name = "irc-watcher"
t_watcher.daemon = True t_watcher.daemon = True
t_watcher.start() t_watcher.start()
wiki_scheduler() wiki_scheduler()


elif "irc_watcher" in enabled: # the IRC watcher is our only enabled
print "Starting IRC watcher..." # component, so run its function only
irc_watcher() # and don't worry about anything else
elif "irc_watcher" in enabled:
# The IRC watcher is our only enabled component, so run its function
# only and don't worry about anything else:
logging.info("Starting IRC watcher")
irc_watcher()


else: # nothing is enabled!
print "No bot parts are enabled; stopping..."
else: # Nothing is enabled!
logging.critical("No bot parts are enabled; stopping")
exit(1) exit(1)


if __name__ == "__main__": if __name__ == "__main__":
try: try:
run() run()
except KeyboardInterrupt: except KeyboardInterrupt:
print "\nKeyboardInterrupt: stopping main bot loop."
logging.critical("KeyboardInterrupt: stopping main bot loop")
exit(1) exit(1)

+ 13
- 12
bot/tasks/__init__.py View File

@@ -7,11 +7,11 @@ This package provides the wiki bot "tasks" EarwigBot runs. Here in __init__,
you can find some functions used to load and run these tasks. you can find some functions used to load and run these tasks.
""" """


import logging
import os import os
import sys import sys
import threading import threading
import time import time
import traceback


from classes import BaseTask from classes import BaseTask
import config import config
@@ -25,6 +25,9 @@ base_dir = os.path.join(config.root_dir, "bot", "tasks")
# an instance of the task class: # an instance of the task class:
_tasks = {} _tasks = {}


# Logger for this module:
logger = logging.getLogger("commands")

def _load_task(filename): def _load_task(filename):
"""Try to load a specific task from a module, identified by file name.""" """Try to load a specific task from a module, identified by file name."""
global _tasks global _tasks
@@ -34,8 +37,7 @@ def _load_task(filename):
try: try:
__import__(name) __import__(name)
except: except:
print "Couldn't load file {0}:".format(filename)
traceback.print_exc()
logger.exception("Couldn't load file {0}:".format(filename))
return return


task = sys.modules[name].Task() task = sys.modules[name].Task()
@@ -44,18 +46,17 @@ def _load_task(filename):
return return


_tasks[task.name] = task _tasks[task.name] = task
print "Added task {0}...".format(task.name)
logger.debug("Added task {0}".format(task.name))


def _wrapper(task, **kwargs): def _wrapper(task, **kwargs):
"""Wrapper for task classes: run the task and catch any errors.""" """Wrapper for task classes: run the task and catch any errors."""
try: try:
task.run(**kwargs) task.run(**kwargs)
except: except:
error = "Task '{0}' raised an exception and had to stop:"
print error.format(task.name)
traceback.print_exc()
error = "Task '{0}' raised an exception and had to stop"
logger.exception(error.format(task.name))
else: else:
print "Task '{0}' finished without error.".format(task.name)
logger.info("Task '{0}' finished without error".format(task.name))


def load(): def load():
"""Load all valid tasks from bot/tasks/, into the _tasks variable.""" """Load all valid tasks from bot/tasks/, into the _tasks variable."""
@@ -70,7 +71,7 @@ def load():
except AttributeError: except AttributeError:
pass # The file is doesn't contain a task, so just move on pass # The file is doesn't contain a task, so just move on


print "Found {0} tasks: {1}.".format(len(_tasks), ', '.join(_tasks.keys()))
logger.info("Found {0} tasks: {1}".format(len(_tasks), ', '.join(_tasks.keys())))


def schedule(now=time.gmtime()): def schedule(now=time.gmtime()):
"""Start all tasks that are supposed to be run at a given time.""" """Start all tasks that are supposed to be run at a given time."""
@@ -87,13 +88,13 @@ def schedule(now=time.gmtime()):
def start(task_name, **kwargs): def start(task_name, **kwargs):
"""Start a given task in a new thread. Pass args to the task's run() """Start a given task in a new thread. Pass args to the task's run()
function.""" function."""
print "Starting task '{0}' in a new thread...".format(task_name)
logger.info("Starting task '{0}' in a new thread".format(task_name))


try: try:
task = _tasks[task_name] task = _tasks[task_name]
except KeyError: except KeyError:
error = "Couldn't find task '{0}': bot/tasks/{0}.py does not exist."
print error.format(task_name)
error = "Couldn't find task '{0}': bot/tasks/{0}.py does not exist"
logger.error(error.format(task_name))
return return


task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs)) task_thread = threading.Thread(target=lambda: _wrapper(task, **kwargs))


+ 4
- 1
bot/watcher.py View File

@@ -10,11 +10,14 @@ being started (located in tasks/) or messages being sent to channels on the IRC
frontend. frontend.
""" """


import logging

import config import config
from classes import Connection, RC, BrokenSocketException from classes import Connection, RC, BrokenSocketException
import rules import rules


frontend_conn = None frontend_conn = None
logger = logging.getLogger("watcher")


def get_connection(): def get_connection():
"""Return a new Connection() instance with connection information. """Return a new Connection() instance with connection information.
@@ -23,7 +26,7 @@ def get_connection():
""" """
cf = config.irc["watcher"] cf = config.irc["watcher"]
connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"], connection = Connection(cf["host"], cf["port"], cf["nick"], cf["ident"],
cf["realname"])
cf["realname"], logger)
return connection return connection


def main(connection, f_conn=None): def main(connection, f_conn=None):


+ 4
- 0
bot/wiki/__init__.py View File

@@ -10,6 +10,10 @@ written by Mr.Z-man, other than a similar purpose. We share no code.
Import the toolset with `import wiki`. Import the toolset with `import wiki`.
""" """


import logging
logger = logging.getLogger("wiki")
logger.addHandler(logging.NullHandler())

from wiki.constants import * from wiki.constants import *
from wiki.exceptions import * from wiki.exceptions import *
from wiki.functions import * from wiki.functions import *


+ 3
- 2
bot/wiki/site.py View File

@@ -16,6 +16,7 @@ try:
except ImportError: except ImportError:
oursql = None oursql = None


from wiki import logger
from wiki.category import Category from wiki.category import Category
from wiki.constants import * from wiki.constants import *
from wiki.exceptions import * from wiki.exceptions import *
@@ -169,7 +170,7 @@ class Site(object):


data = urlencode(params) data = urlencode(params)


print url, data # debug code
logger.debug("{0} -> {1}".format(url, data))


try: try:
response = self._opener.open(url, data) response = self._opener.open(url, data)
@@ -207,7 +208,7 @@ class Site(object):
raise SiteAPIError(e.format(self._max_retries)) raise SiteAPIError(e.format(self._max_retries))
tries += 1 tries += 1
msg = 'Server says: "{0}". Retrying in {1} seconds ({2}/{3}).' msg = 'Server says: "{0}". Retrying in {1} seconds ({2}/{3}).'
print msg.format(info, wait, tries, self._max_retries)
logger.info(msg.format(info, wait, tries, self._max_retries))
sleep(wait) sleep(wait)
return self._api_query(params, tries=tries, wait=wait*3) return self._api_query(params, tries=tries, wait=wait*3)
else: else:


Loading…
Cancel
Save