# -*- test-case-name: mv3d.test.service.test_log -*- # Copyright (C) 2012 Mortal Coil Games # See LICENSE for details. """ Implementation of the log service which collects logs into a searchable database. @author: mike """ import os from zope.interface import implements from twisted.spread import pb from mv3d.server.iserver import ILogService from mv3d.server.persist import SQLiteStore from mv3d.server.service import checkServicePermissions, viewed from mv3d.util.log import LogMessage, LogServiceObserver, LogSubscription from mv3d.util.conductor import parseInterfaceConfig, parsePermissionConfig from mv3d.net.security import Securable, requirePermissions from twisted.application.service import Service class LogService(Service, Securable): """ Log service that collects logs into the database and provides search functionality """ implements(ILogService) logCount = 0 def __init__(self): Securable.__init__(self) self.store = SQLiteStore() self.store.open() self.subscriptions = [] self.interfaces = dict() self.config = {} def configure(self, name, cfg): """ Configure the log service. """ self.config.update(dict(cfg.items(name))) if cfg.has_option(name, "store"): self.store = SQLiteStore() fname = cfg.get(name, "store") if os.path.abspath(fname) != fname: fname = os.path.join(self.parent.dataRoot, fname) dirname = os.path.dirname(fname) if not os.path.exists(dirname): os.makedirs(dirname) self.store.open(fname) if cfg.has_option(name, "interfaces"): parseInterfaceConfig(cfg.get(name, "interfaces"), self) if cfg.has_option(name, "grantPermissions"): parsePermissionConfig(cfg.get(name, "grantPermissions"), self, True) if cfg.has_option(name, "denyPermissions"): parsePermissionConfig(cfg.get(name, "denyPermissions"), self, False) def startService(self): """ Service is starting up. """ Service.startService(self) if self.parent.logObserver is None: self.parent.logObserver = LogServiceObserver(self.parent, "self/%s" % self.name) self.parent.logObserver.start() def query(self, filter=None, offset=0, limit= -1, order=None): """ Query the log for messages that match the filter and return up to limit starting at offset and sorted by the specified order. """ return LogMessage.query(self.store, filter, offset, limit, order) def subscribe(self, filter): """ Add a new subscription with the given filter. """ self.subscriptions.append(LogSubscription(filter)) return self.subscriptions[-1] def unsubscribe(self, subscriptionID): """ Unsubscribe by subscription ID. This should only be called from LogSubscription.unsubscribe. """ for sub in self.subscriptions: if sub.subscriptionID == subscriptionID: self.subscriptions.remove(sub) return raise ValueError("No such subscription with ID %d" % subscriptionID) def log(self, logMessage): """ Enter a new log message. """ self.logCount += 1 logMessage.save(self.store) for subscription in self.subscriptions: subscription.log(logMessage) def getConfig(self): """ Returns a dict with the config of this player service. """ return self.config def getStatistics(self): """ Return a dict with some helpful stats """ return dict(logCount=self.logCount) @requirePermissions("reference") def getInterface(self, _client, protocol): """ Hand out public interfaces """ return self.interfaces[protocol] def isLocal(self): """ We are local so return true """ return True class LogServiceView(pb.Viewable): """ Remotely viewable interface for the LogService. """ def __init__(self, service): self.service = service def getProtocol(self): """ Return what protocol we implement """ return "pb" @checkServicePermissions("read") @viewed def view_query(self, client, filter=None, offset=0, limit= -1, order=None): """ Query the log for messages that match the filter and return up to limit starting at offset and sorted by the specified order. """ @checkServicePermissions("read") @viewed def view_subscribe(self, client, filter): """ Add a new subscription with the given filter. """ @checkServicePermissions("read") @viewed def view_unsubscribe(self, client, subscriptionID): """ Unsubscribe by subscription ID. This should only be called from LogSubscription.unsubscribe. """ @checkServicePermissions("modify") @viewed def view_log(self, client, logMessage): """ Enter a new log message. """ @checkServicePermissions("read") @viewed def view_getConfig(self, client): """ Returns a dict with the config of this player service. """ @checkServicePermissions("read") @viewed def view_getStatistics(self, client): """ Retrieves the statistics from the realm server """