Sandy Walsh ac49955386 Adding debugger from oahu
Allows for per-trigger-definition debugging. Simply add
debug_level=1 to your trigger definition.

debug_level=2 gives detailed explainations of why your
stream didn't fire or trigger.

This way, you can leave the overall debug level at INFO
and still get details on the stream you're working on.

Yagi worker now uses idle() callback for debug updates.

and minor cleanups for pep8/HACKING

Change-Id: Id0af7a0adbcc47335ad250130958932c708b5c18
2014-10-02 20:50:31 +00:00

162 lines
4.2 KiB
Python

# Copyright (c) 2014 Dark Secret Software Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import abc
import logging
import six
logger = logging.getLogger(__name__)
class NoOpGroup(object):
def match(self):
return True
def mismatch(self, reason):
return False
def check(self, value, reason):
return value
class Group(object):
def __init__(self, name):
self._name = name # Group name
self._match = 0
self._mismatch = 0
self._reasons = {}
def match(self):
self._match += 1
return True
def mismatch(self, reason):
count = self._reasons.get(reason, 0)
self._reasons[reason] = count + 1
self._mismatch += 1
return False
def check(self, value, reason):
if value:
return self.match()
return self.mismatch(reason)
@six.add_metaclass(abc.ABCMeta)
class BaseDebugger(object):
@abc.abstractmethod
def reset(self):
pass
@abc.abstractmethod
def get_group(self, name):
pass
@abc.abstractmethod
def bump_counter(self, name, inc=1):
pass
@abc.abstractmethod
def get_debug_level(self):
pass
class NoOpDebugger(BaseDebugger):
def __init__(self, *args, **kwargs):
self.noop_group = NoOpGroup()
def reset(self):
pass
def get_group(self, name):
return self.noop_group
def bump_counter(self, name, inc=1):
pass
def get_debug_level(self):
return 0
class DetailedDebugger(BaseDebugger):
def __init__(self, name, debug_level):
super(DetailedDebugger, self).__init__()
self._name = name
self._debug_level = debug_level
self.reset()
def reset(self):
# If it's not a match or a mismatch it was a fatal error.
self._groups = {}
self._counters = {}
def get_group(self, name):
group = self._groups.get(name, Group(name))
self._groups[name] = group
return group
def bump_counter(self, name, inc=1):
self._counters[name] = self._counters.get(name, 0) + inc
def get_debug_level(self):
return self._debug_level
class DebugManager(object):
def __init__(self):
self._debuggers = {}
def get_debugger(self, trigger_def):
name = "n/a"
level = 2 # Default these unknowns to full debug.
if trigger_def is not None:
name = trigger_def.name
level = trigger_def.debug_level
debugger = self._debuggers.get(name)
if not debugger:
if level > 0:
debugger = DetailedDebugger(name, level)
else:
debugger = NoOpDebugger()
self._debuggers[name] = debugger
return debugger
def dump_group(self, debugger, group_name):
group = debugger.get_group(group_name)
logger.info("%s Criteria: %d checks, %d passed" %
(group._name,
group._match + group._mismatch, group._match))
if debugger.get_debug_level() > 1:
for kv in group._reasons.items():
logger.info(" - %s = %d" % kv)
def dump_counters(self, debugger):
for kv in debugger._counters.items():
logger.info("Counter \"%s\" = %d" % kv)
def dump_debuggers(self):
for debugger in self._debuggers.values():
if debugger.get_debug_level() == 0:
continue
logger.info("---- Trigger Definition: %s ----" % debugger._name)
for name in debugger._groups.keys():
self.dump_group(debugger, name)
self.dump_counters(debugger)
debugger.reset()
logger.info("----------------------------")