diff --git a/agent/README.txt b/agent/README.txt index 092857a..8406555 100644 --- a/agent/README.txt +++ b/agent/README.txt @@ -307,6 +307,11 @@ Configuration - log format(s) - log file(s) (or other forms of persistence) + >>> logger = agent.logger + >>> logger.log(dict(object='job', event='start')) + >>> print logger[-1] + 20... event:start object:job + Software Loader =============== diff --git a/agent/core.py b/agent/core.py index 97cb4d9..a6dafe7 100644 --- a/agent/core.py +++ b/agent/core.py @@ -27,6 +27,7 @@ from zope.interface import implements from loops.agent.interfaces import IAgent from loops.agent.config import Configurator from loops.agent.crawl import filesystem +from loops.agent.log import Logger from loops.agent.schedule import Scheduler, Stopper from loops.agent.transport import base @@ -48,11 +49,12 @@ class Agent(object): transportTypes = transportTypes def __init__(self, conf=None): - config = self.config = Configurator('ui', 'crawl', 'transport') + config = self.config = Configurator('ui', 'crawl', 'transport', 'logger') config.load(conf) self.scheduler = Scheduler(self) self.stopper = Stopper() self.stopper.scheduler = self.scheduler + self.logger = Logger(self) def scheduleJobsFromConfig(self): config = self.config @@ -71,7 +73,7 @@ class Agent(object): transporter = factory(self) # TODO: configure transporter or - better - # set up transporter(s) just once - job.successors.append(transporter.jobFactory(transporter)) + job.successors.append(transporter.createJob()) job.repeat = info.repeat or 0 self.scheduler.schedule(job, info.starttime or int(time())) # TODO: remove job from config diff --git a/agent/interfaces.py b/agent/interfaces.py index 462b097..f53a977 100644 --- a/agent/interfaces.py +++ b/agent/interfaces.py @@ -92,14 +92,30 @@ class IScheduledJob(Interface): class ILogger(Interface): - """ Collection of log records. + """ Ordered collection (list) of log records. """ + externalLoggers = Attribute('A collection of logger objects ' + 'to which the logging records should be written.') + + def log(data): + """ Record the information given by the ``data`` argument + (a mapping). + """ + class ILogRecord(Interface): """ """ + format = Attribute('An optional format string for rendering the ' + 'recorded information.') + + def __str__(): + """ Return a string representation suitable for writing on a + log file. + """ + class ICrawlingJob(IScheduledJob): """ Collects resources. diff --git a/agent/log.py b/agent/log.py index 1e3126e..8d6f7c7 100644 --- a/agent/log.py +++ b/agent/log.py @@ -22,6 +22,40 @@ Log information management. $Id$ """ +import time from zope.interface import implements + from loops.agent.interfaces import ILogger, ILogRecord + +class LogRecord(object): + + implements(ILogRecord) + + format = None + timeFormat = '%Y-%m-%dT%H:%S' + + def __init__(self, data): + self.data = data + self.timeStamp = time.time() + + def __str__(self): + msg = [str(time.strftime(self.timeFormat, time.localtime(self.timeStamp)))] + for k in sorted(self.data): + msg.append('%s:%s' % (str(k), str(self.data[k]))) + return ' '.join(msg) + + +class Logger(list): + + implements(ILogger) + + recordFactory = LogRecord + + def __init__(self, agent, externalLoggers=[]): + self.agent = agent + self.externalLoggers = externalLoggers + + def log(self, data): + self.append(self.recordFactory(data)) + diff --git a/agent/schedule.py b/agent/schedule.py index 89c3c23..89e31eb 100644 --- a/agent/schedule.py +++ b/agent/schedule.py @@ -50,8 +50,8 @@ class Scheduler(object): reactor.callLater(startTime-int(time()), job.run) return startTime - def getJobsToExecute(startTime=None): - return [j for j in self.queue.values() if (startTime or 0) <= j.startTime] + def getJobsToExecute(startTime=0): + return [j for j in self.queue.values() if startTime <= j.startTime] class Job(object):