def configure_with(self, writer): self._ensure_state(State.disconnected) twisted_log.addObserver(self.on_twisted_log) self._writer = IJournalWriter(writer) self._set_state(State.connected) self._schedule_flush()
class Journaler(log.Logger, common.StateMachineMixin): implements(IJournaler, ILogKeeper) log_category = 'journaler' _error_handler = error_handler # FIXME: at some point switch to False and remove this attribute should_keep_on_logging_to_flulog = True def __init__(self, logger): log.Logger.__init__(self, self) common.StateMachineMixin.__init__(self, State.disconnected) self._writer = None self._flush_task = None self._cache = EntriesCache() self._notifier = defer.Notifier() def configure_with(self, writer): self._ensure_state(State.disconnected) twisted_log.addObserver(self.on_twisted_log) self._writer = IJournalWriter(writer) self._set_state(State.connected) self._schedule_flush() def close(self, flush_writer=True): def set_disconnected(): self._writer = None self._set_state(State.disconnected) try: twisted_log.removeObserver(self.on_twisted_log) except ValueError: # it should be safe to call close() multiple times, # in this case we are not registered as the observer anymore pass d = self._close_writer(flush_writer) d.addCallback(defer.drop_param, set_disconnected) return d ### IJournaler ### def get_connection(self, externalizer): externalizer = IExternalizer(externalizer) instance = JournalerConnection(self, externalizer) return instance def prepare_record(self): return Record(self) @in_state(State.connected) def get_histories(self): return self._writer.get_histories() @in_state(State.connected) def get_entries(self, history): return self._writer.get_entries(history) def insert_entry(self, **data): self._cache.append(data) self._schedule_flush() return self._notifier.wait('flush') @in_state(State.connected) def get_filename(self): return self._writer.get_filename() def is_idle(self): if len(self._cache) > 0: return False if self._writer: return self._writer.is_idle() return True ### ILogObserver provider ### def on_twisted_log(self, event_dict): edm = event_dict['message'] if not edm: if event_dict['isError'] and 'failure' in event_dict: fail = event_dict['failure'] self.error("A twisted traceback occurred. Exception: %r.", fail.value) if flulog.getCategoryLevel("twisted") < flulog.WARN: self.debug( "Run with debug level >= 2 to see the traceback.") else: self.error("%s", fail.getTraceback()) ### ILogKeeper Methods ### def do_log(self, level, object, category, format, args, depth=-1, file_path=None, line_num=None): level = int(level) if category is None: category = 'feat' if level > flulog.getCategoryLevel(category): return if file_path is None and line_num is None: file_path, line_num = flulog.getFileLine(where=-depth-2) if args: message = format % args else: message = str(format) data = dict( entry_type='log', level=level, log_name=object, category=category, file_path=file_path, line_num=line_num, message=message, timestamp=int(time.time())) self.insert_entry(**data) if self.should_keep_on_logging_to_flulog: flulog.doLog(level, object, category, format, args, where=depth, filePath=file_path, line=line_num) ### private ### def _schedule_flush(self): if self._flush_task is None: self._flush_task = time.call_next(self._flush) @in_state(State.connected) def _flush(self): entries = self._cache.fetch() if entries: d = self._writer.insert_entries(entries) d.addCallbacks(defer.drop_param, self._flush_error, callbackArgs=(self._flush_complete, )) return d else: self._flush_complete() def _flush_complete(self): if self._cache.is_locked(): self._cache.commit() self._flush_task = None self._notifier.callback('flush', None) if len(self._cache) > 0: self._schedule_flush() def _flush_error(self, fail): self._cache.rollback() fail.raiseException() def _close_writer(self, flush_writer=True): d = defer.succeed(None) if self._writer: d.addCallback(defer.drop_param, self._writer.close, flush=flush_writer) return d