Skip to content

Commit

Permalink
Improved structure between monitor application and main application.
Browse files Browse the repository at this point in the history
  • Loading branch information
jbohman committed Jun 9, 2010
1 parent a38167e commit 2f07cd3
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 37 deletions.
6 changes: 4 additions & 2 deletions config.yaml
Expand Up @@ -9,5 +9,7 @@ cassandra_port: 9160

# List of paths (files and directories) to monitor
paths:
- /var/log/:
recursive: True
- name: ~/coding/cassandra/access.log
recursive: False
- name: /var/log/testing/
recurisve: True
45 changes: 38 additions & 7 deletions logsandra/logsandra.py
Expand Up @@ -3,17 +3,40 @@
import os
import optparse
import time
import multiprocessing
import logging

# Local imports
import monitor
import config
import utils
from utils.daemon import Daemon


class Application(Daemon):

def monitor(self):
reader = monitor.Reader(False)
watcher = monitor.Watcher(self.settings['paths'], reader.callback)
watcher.loop()

# TODO: setup application here, monitor + pylon webservice
def run(self):
while 1:

# Setup logging
logging.basicConfig(filename=self.settings['logfile_name'], level=logging.DEBUG)

# Test to see if settings is present
if not hasattr(self, 'settings'):
print 'No settings, exiting...'
sys.exit(1)

# Start monitor process
self.monitor_process = multiprocessing.Process(target=self.monitor)
self.monitor_process.start()

self.running = True
while self.running:
time.sleep(10)


Expand All @@ -28,11 +51,18 @@ def run(self):
parser.add_option('--config-file', dest='config_file', metavar='FILE', default=default_config_file)
parser.add_option('--working-directory', dest='working_directory', metavar='DIRECTORY', default=default_working_directory)
parser.add_option('--pid-file', dest='pid_file', metavar='FILE', default='/tmp/logsandra.pid')
parser.add_option('--application-data-directory', dest='application_data_directory', default=utils.application_data_directory('logsandra'))
(options, args) = parser.parse_args()

settings = config.parse(options.config_file)
application = Application(options.pid_file)
application.settings = settings
if not os.path.isdir(options.application_data_directory):
os.makedirs(options.application_data_directory)

output_file = os.path.join(options.application_data_directory, 'logsandra.log')

application = Application(options.pid_file, stdout=output_file, stderr=output_file)
application.settings = config.parse(options.config_file)
application.settings['application_data_directory'] = options.application_data_directory
application.settings['logfile_name'] = output_file

if len(args) == 1:
if args[0] == 'start':
Expand All @@ -42,10 +72,11 @@ def run(self):
elif args[0] == 'restart':
application.restart()
else:
print "Unknown command"
print 'Unknown command'
sys.exit(2)

sys.exit(0)
else:
print parser.get_usage()
sys.exit(2)
application.run()
#print parser.get_usage()
#sys.exit(2)
1 change: 1 addition & 0 deletions logsandra/monitor/__init__.py
@@ -0,0 +1 @@
from monitor import Reader, Watcher
16 changes: 7 additions & 9 deletions logsandra/monitor/monitor.py
Expand Up @@ -4,10 +4,10 @@
from optparse import OptionParser

# Try to import pyinotify handler, else standard handler
try:
from watchers.inotify import InotifyWatcher as Watcher
except ImportError:
from watchers.standard import StandardWatcher as Watcher
#try:
# from watchers.inotify import InotifyWatcher as Watcher
#except ImportError:
from watchers.standard import StandardWatcher as Watcher


class Reader(object):
Expand Down Expand Up @@ -42,7 +42,7 @@ def callback(self, filename):
usage = 'usage: %prog [options] path [path ...]'
parser = OptionParser(usage=usage)
parser.add_option('-r', '--rescan-freq', dest='rescan_freq', help='rescan frequency in seconds', metavar='SECONDS', default=20)
parser.add_option('-u', '--update-freq', dest='update_freq', help='update frequnecy in seconds', metavar='SECONDS', default=0)
parser.add_option('-u', '--update-freq', dest='update_freq', help='update frequnecy in seconds', metavar='SECONDS', default=10)
parser.add_option('-t', '--tail', action='store_true', dest='tail', help='start reading from the bottom only', default=False)
parser.add_option('--recursive', action='store_true', dest='recursive')
(options, args) = parser.parse_args()
Expand All @@ -51,12 +51,10 @@ def callback(self, filename):
print "Need atleast one path (file or directory) to monitor, see --help"
sys.exit(1)

settings = {'freq': options.update_freq, 'rescan': options.rescan_freq}

entities = []
for arg in args:
entities.append({'name': args, 'recursive': options.recursive})
entities.append({'name': arg, 'recursive': options.recursive})

r = Reader(options.tail)
w = Watcher(settings, entities, r.callback)
w = Watcher(entities, r.callback, update_freq=options.update_freq, rescan_freq=options.rescan_freq)
w.loop()
8 changes: 5 additions & 3 deletions logsandra/monitor/watchers/inotify.py
Expand Up @@ -9,16 +9,18 @@ def process_IN_MODIFY(self, event):

class InotifyWatcher(object):

def __init__(self, settings, entities, callback):
self.settings = settings
def __init__(self, entities, callback, update_freq=0, rescan_freq=20):
self.update_freq = update_freq
self.rescan_freq = rescan_freq

self.entities = entities
self.callback = lambda x: callback(x.pathname)
self.wm = pyinotify.WatchManager()

pyinotify.log.setLevel(50)

def loop(self):
notifier = pyinotify.Notifier(self.wm, EventHandler(callback=self.callback), self.settings['freq'])
notifier = pyinotify.Notifier(self.wm, EventHandler(callback=self.callback), self.update_freq)
for entity in self.entities:
self.wm.add_watch(entity['name'], pyinotify.IN_MODIFY, rec=entity['recursive'])

Expand Down
28 changes: 13 additions & 15 deletions logsandra/monitor/watchers/standard.py
Expand Up @@ -4,10 +4,13 @@

class StandardWatcher(object):

def __init__(self, settings, entities, callback):
self.settings = settings
def __init__(self, entities, callback, update_freq=10, rescan_freq=20):
self.update_freq = update_freq
self.rescan_freq = rescan_freq

self.entities = entities
self.callback = callback

self.files = {}

for filename in self._find_files_generator():
Expand All @@ -18,7 +21,7 @@ def __init__(self, settings, entities, callback):
def loop(self):
while True:
current_time = time.time()
if (current_time - self._last_rescan_time) > self.settings['rescan']:
if (current_time - self._last_rescan_time) > self.rescan_freq:
self._last_rescan_time = self._rescan()

reference_time = time.time()
Expand All @@ -28,9 +31,9 @@ def loop(self):
self.files[filename] = new_mtime
self.callback(filename)

if self.settings['freq'] > 0:
if self.update_freq > 0:
current_time = time.time()
sleep = self.settings['freq'] - (current_time - reference_time)
sleep = self.update_freq - (current_time - reference_time)
if sleep > 0:
time.sleep(sleep)

Expand All @@ -51,8 +54,11 @@ def _find_files_generator(self):
yield filename
# Is file
else:
filename = os.path.join(os.path.abspath(entity['name']), entity['name'])
yield filename
if os.path.exists(os.path.expanduser(entity['name'])):
filename = os.path.abspath(os.path.expanduser(entity['name']))
yield filename
else:
raise AttributeError('Invalid path, cannot monitor it')

def _rescan(self):
tempfiles = {}
Expand All @@ -73,11 +79,3 @@ def _mtime(self, filename):
return os.stat(filename).st_mtime
except os.error:
return False


if __name__ == '__main__':
def callback(x):
print x

sw = StandardWatcher({'freq': 1, 'rescan': 10}, [{'name': '.', 'recursive': False}], callback)
sw.loop()
12 changes: 12 additions & 0 deletions logsandra/utils/__init__.py
@@ -0,0 +1,12 @@
import sys
import os

def application_data_directory(appname):
if sys.platform == 'darwin':
from AppKit import NSSearchPathForDirectoriesInDomains
appdata = path.join(NSSearchPathForDirectoriesInDomains(14, 1, True)[0], appname)
elif sys.platform == 'win32':
appdata = os.path.join(os.environ['APPDATA'], appname)
else:
appdata = os.path.expanduser(os.path.join("~", "." + appname))
return appdata
2 changes: 1 addition & 1 deletion logsandra/utils/daemon.py
Expand Up @@ -9,7 +9,7 @@
import atexit
from signal import SIGTERM

class Daemon:
class Daemon(object):
"""
A generic daemon class.
Expand Down

0 comments on commit 2f07cd3

Please sign in to comment.