Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazyflow logging and fix default operator naming #2850

Merged
merged 16 commits into from
Jun 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions ilastik/ilastik_logging/default_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def get_default_config(
"maxBytes": 50e6, # 20 MB
"backupCount": 5,
"formatter": "verbose",
"encoding": "utf-8",
},
},
"root": {"handlers": root_handlers, "level": root_log_level},
Expand Down
6 changes: 3 additions & 3 deletions ilastik/ilastik_logging/logging_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
"ilastik.utility": { "level":"INFO" },
"ilastik.utility.exportingOperator": { "level":"INFO" },
"ilastik.utility.exportFile": { "level":"INFO" },
"lazyflow.signals": { "level":"INFO" },
"lazyflow.request": { "level":"INFO" },
"lazyflow.request.RequestLock": { "level":"INFO" },
"lazyflow.request.SimpleRequestCondition": { "level":"INFO" },
"lazyflow.graph": { "level":"INFO" },
"lazyflow.graph.Slot": { "level":"INFO" },
"lazyflow.slot.Slot": { "level":"INFO" },
"lazyflow.slot_debug": { "level":"INFO" },
"lazyflow.classifiers": { "level":"INFO" },
"lazyflow.operatorWrapper.OperatorWrapper": { "level":"INFO" },
"lazyflow.op_debug": { "level":"INFO" },
"lazyflow.operators.ioOperators": { "level":"INFO" },
"lazyflow.operators.ioOperators.opRESTfulVolumeReader": { "level":"INFO" },
"lazyflow.operators.opFeatureMatrixCache": { "level":"INFO" },
Expand Down
2 changes: 2 additions & 0 deletions ilastik/workflows/voxelSegmentation/classifierOperators.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ class OpSupervoxelwiseClassifierPredict(Operator):

PMaps = OutputSlot()

logger = logging.getLogger(__name__ + ".OpSupervoxelwiseClassifierPredict")

def __init__(self, *args, **kwargs):
super(OpSupervoxelwiseClassifierPredict, self).__init__(*args, **kwargs)

Expand Down
5 changes: 4 additions & 1 deletion lazyflow/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def call_when_setup_finished(self, fn):
fn()
else:
# Subscribe to the next completion.
logger.debug(f"Adding to queue in setup={id(self._sig_setup_complete)}: {fn}")
self._sig_setup_complete.subscribe(fn)

class SetupDepthContext(object):
Expand All @@ -79,6 +80,7 @@ def __enter__(self):
with self._graph._lock:
if self._graph._setup_depth == 0:
self._graph._sig_setup_complete = OrderedSignal()
logger.debug(f"New setupDepthContext={id(self._graph._sig_setup_complete)}")
self._graph._setup_depth += 1

def __exit__(self, *args):
Expand All @@ -88,7 +90,8 @@ def __exit__(self, *args):
self._graph._setup_depth -= 1
if self._graph._setup_depth == 0:
sig_setup_complete = self._graph._sig_setup_complete
# Reset.
logger.debug(f"Finish setupDepthContext={id(sig_setup_complete)}")
self._graph._sig_setup_complete = None
if sig_setup_complete:
logger.debug(f"Flushing queue of setupDepthContext={id(sig_setup_complete)}")
sig_setup_complete()
30 changes: 23 additions & 7 deletions lazyflow/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@
if isinstance(v, OutputSlot):
v.name = k
cls.outputSlots.append(v)

if "name" not in classDict:
cls.name = name # better default if subclass does not provide cls.name
return cls

def __call__(cls, *args, **kwargs):
Expand Down Expand Up @@ -173,9 +176,6 @@

"""

loggerName = __name__ + ".Operator"
logger = logging.getLogger(loggerName)

# definition of inputs slots
inputSlots = []

Expand All @@ -197,14 +197,16 @@
obj.outputs = OutputDict(obj)
return obj

def __init__(self, parent=None, graph=None):
def __init__(self, parent=None, graph=None, write_logs=False):
"""
Either parent or graph have to be given. If both are given
parent.graph has to be identical with graph.

:param parent: the parent operator; if None the instance is a
root operator
:param graph: a Graph instance
:param write_logs: Debugging feature. The operator will write debug logs if True.
Make sure the `lazyflow.op_debug` logger has level=DEBUG in the logging config.

"""
if not (parent is None or isinstance(parent, Operator)):
Expand All @@ -228,6 +230,13 @@
if parent is not None:
parent._add_child(self)

self._debug_logger = None
if write_logs:
self._debug_logger = logging.getLogger(f"lazyflow.op_debug.{self.name}")
self._debug_logger.debug(

Check warning on line 236 in lazyflow/operator.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operator.py#L236

Added line #L236 was not covered by tests
f"Instantiated {self.name} {id(self)} with parent={self.parent.name if self.parent is not None else ''}"
)

self._initialized = False

self._condition = threading.Condition()
Expand Down Expand Up @@ -266,9 +275,6 @@

# continue initialization, when user overrides __init__
def _after_init(self):
# provide simple default name for lazy users
if self.name == Operator.name:
self.name = type(self).__name__
assert self.graph is not None, (
"Operator {}: self.graph is None, the parent ({})"
" given to the operator must have a valid .graph attribute!".format(self, self._parent)
Expand Down Expand Up @@ -393,6 +399,8 @@
if self._parent is not None:
del self._parent._children[self]

if self._debug_logger:
self._debug_logger.debug(f"Cleaning up {self.name} {id(self)}")

Check warning on line 403 in lazyflow/operator.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operator.py#L403

Added line #L403 was not covered by tests
# Disconnect ourselves and all children
self._disconnect()

Expand Down Expand Up @@ -489,6 +497,8 @@
self._condition.wait()

self._settingUp = True
if self._debug_logger:
self._debug_logger.debug(f"Starting setupOutputs on {self.name} {id(self)}")

# Keep a copy of the old metadata for comparison.
# We only trigger downstream changes if something really changed.
Expand All @@ -498,6 +508,8 @@
self.setupOutputs()
self._setup_count += 1

if self._debug_logger:
self._debug_logger.debug(f"Finished setupOutputs on {self.name} {id(self)}")

Check warning on line 512 in lazyflow/operator.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operator.py#L512

Added line #L512 was not covered by tests
self._settingUp = False
self._condition.notify_all()

Expand Down Expand Up @@ -588,6 +600,10 @@
# We are executing the operator. Incremement the execution
# count to protect against simultaneous setupOutputs()
# calls.
if self._debug_logger:
self._debug_logger.debug(
f"Executing {self.name} {id(self)} slot={slot.name} for roi={str(roi)} with {kwargs=}"
)
self._incrementOperatorExecutionCount()
return self.execute(slot, subindex, roi, result, **kwargs)
finally:
Expand Down
51 changes: 16 additions & 35 deletions lazyflow/operatorWrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
###############################################################################
# Python
import functools
import logging

# lazyflow
from lazyflow.operator import Operator
Expand All @@ -30,9 +29,6 @@
class OperatorWrapper(Operator):
name = "OperatorWrapper"

loggerName = __name__ + ".OperatorWrapper"
logger = logging.getLogger(loggerName)

def __init__(
self,
operatorClass,
Expand All @@ -42,6 +38,7 @@
graph=None,
promotedSlotNames=None,
broadcastingSlotNames=None,
write_logs=False,
):
"""Constructs a wrapper for the given operator. That is,
manages a list of copies of the original operator, and
Expand All @@ -63,39 +60,36 @@

:param graph: the graph operator to init each inner operator with

:param promotedSlotNames:

If provided, only those slots will be promoted when
:param promotedSlotNames: If provided, only those slots will be promoted when
replicated. All other slots will be replicated without
promotion, and their input values will be broadcasted to
all inner operators.

If not provided (i.e. promotedSlotNames=None), the default
behavior is to promote ALL replicated slots.

Note: Outputslots are always promoted, regardless of whether
or not they appear in the promotedSlotNames argument.

:param write_logs: Debugging feature. The wrapper and wrapped ops will write debug logs if True.
The wrapped operator's __init__ must also accept the write_logs kwarg.
Make sure the `lazyflow.op_debug` logger has level=DEBUG in the logging config.

"""
# Base class init
self._name = "Uninitialized OperatorWrapper"
super(OperatorWrapper, self).__init__(parent=parent, graph=graph)
if operator_args == None:
super(OperatorWrapper, self).__init__(parent=parent, graph=graph, write_logs=write_logs)
if operator_args is None:
operator_args = ()
if operator_kwargs == None:
if operator_kwargs is None:
operator_kwargs = {}
assert isinstance(operator_args, (tuple, list))
assert isinstance(operator_kwargs, dict)
if write_logs:
operator_kwargs.update({"write_logs": write_logs})

Check warning on line 85 in lazyflow/operatorWrapper.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operatorWrapper.py#L85

Added line #L85 was not covered by tests
self._createInnerOperator = functools.partial(operatorClass, parent=self, *operator_args, **operator_kwargs)

self._initialized = False

if operatorClass.name == "":
self._name = "Wrapped " + operatorClass.__name__
else:
self._name = "Wrapped " + operatorClass.name

self._customName = False
self.name = "Wrapped " + operatorClass.name
if self._debug_logger:
self._debug_logger.debug(f"Wrapper {id(self)} name={self.name}")

Check warning on line 92 in lazyflow/operatorWrapper.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operatorWrapper.py#L92

Added line #L92 was not covered by tests
k-dominik marked this conversation as resolved.
Show resolved Hide resolved

allInputSlotNames = set([s.name for s in operatorClass.inputSlots])

Expand Down Expand Up @@ -140,7 +134,6 @@
self.promotedSlotNames = promotedSlotNames

self.innerOperators = []
self.logger.log(logging.DEBUG, "wrapping operator '{}'".format(operatorClass.name))

# replicate input slot definitions
for innerSlot in sorted(operatorClass.inputSlots, key=lambda s: s._global_slot_id):
Expand Down Expand Up @@ -177,15 +170,6 @@
for s in list(self.outputs.values()):
assert len(s) == 0

@property
def name(self):
return self._name

@name.setter
def name(self, name):
self._name = name
self._customName = True

def __getitem__(self, key):
return self.innerOperators[key]

Expand Down Expand Up @@ -222,13 +206,10 @@
def _insertInnerOperator(self, index, length):
if len(self.innerOperators) >= length:
return self.innerOperators[index]
if self._debug_logger:
self._debug_logger.debug(f"Inserting inner operator at index {index}")

Check warning on line 210 in lazyflow/operatorWrapper.py

View check run for this annotation

Codecov / codecov/patch

lazyflow/operatorWrapper.py#L210

Added line #L210 was not covered by tests
op = self._createInnerOperator()

# Update our name (if the client didn't already give us a
# special one)
if self._customName is False:
self._name = "Wrapped " + op.name

# If anyone calls setValue() on one of these slots,
# forward the setValue call to the slot's upstream_slot (the
# outer slot on the operator wrapper)
Expand Down
2 changes: 2 additions & 0 deletions lazyflow/operators/classifierOperators.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,8 @@ class OpBaseClassifierPredict(Operator):

PMaps = OutputSlot()

logger = logging.getLogger(__name__ + ".OpBaseClassifierPredict")

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

Expand Down