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
Expose metaflow logger and monitor via singleton #1794
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some initial comments.
metaflow/task.py
Outdated
pass | ||
system_current.logger.log( | ||
{ | ||
"log_type": "ERROR", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we have constants for these?
metaflow/metaflow_metrics_manager.py
Outdated
|
||
@contextmanager | ||
def measure(self, metric_name, qualifer_name=None): | ||
timer, counter = self.monitor.get_measure_metrics(metric_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we ideally make this look symetric for both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can also just return a single "payload". Internally, the send_metric would know to stop the timer for example.
e35dd85
to
a5984a2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few initial comments but I think this goes in the direction we agreed to so we shouldn't be too far.
metaflow/__init__.py
Outdated
@@ -100,6 +100,12 @@ class and related decorators. | |||
# current runtime singleton | |||
from .metaflow_current import current | |||
|
|||
# system monitor runtime singleton |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add a comment like (for internal metaflow use only). It's kind of explicit from the name but we can be extra explicit :)
metaflow/cli.py
Outdated
@@ -1066,6 +1068,10 @@ def start( | |||
if decospecs: | |||
decorators._attach_decorators(ctx.obj.flow, decospecs) | |||
|
|||
# We create an instance of SystemMonitor and SystemLogger respectively |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, I would move this up to where ctx.obj.monitor and event_logger are created.
|
||
@property | ||
def flow_name(self): | ||
return self._flow_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assumes that flow_name will not be called prior to flow which may not be correct. In general, I would have one init method that inits all 4 (flow, flow_name, environment and logger) and controlled by one single flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is still not addressed I think.
metaflow/metaflow_system_logger.py
Outdated
return self._logger | ||
|
||
@logger.setter | ||
def logger(self, logger): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we don't really want this to be settable independently do we? can we just keep set_logger and have things set directly on the _ names? Or is there a use you are thinking of of setting things independently?
from typing import Dict, Any | ||
|
||
|
||
class SystemLogger(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: please add type hints where appropriate.
from contextlib import contextmanager | ||
|
||
|
||
class SystemMonitor(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same comments as for system_logger
metaflow/task.py
Outdated
"ts": round(time.time()), | ||
} | ||
logger.log(msg) | ||
with _system_monitor.count("metaflow.task.start"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would keep things the same here -- ie: use self.event_logger and self.logger
metaflow/task.py
Outdated
"flow_name": self.flow.name, | ||
} | ||
logger.log(tsk_msg) | ||
print("I am here in metaflow") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: remove.
metaflow/task.py
Outdated
"runtime": round(end), | ||
} | ||
logger.log(msg) | ||
_system_monitor.gauge("metaflow.task.duration", duration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A measure would be more appropriate here than a gauge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question is should we be measuring the task duration only until the task "ends" or consider the portion dealing with metadata register as part of the task duration as well?
If it's the former then it would be difficult to implement using the existing context manager construct - hence why we use the gauge metric.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good point so we may need to add non-context specific ones for this. The issue is that a gauge isn't the same as a measure. Gauge are supposed to measure levels of things (like # of machines in a pool, etc) not disparate point measurements like this.
from typing import Dict, Any, Optional, Union | ||
|
||
|
||
class SystemLogger(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving this and monitor under metaflow/system
so that we can have a rather clean import - from metaflow.system import monitor
rather than from .metaflow_system_monitor import _system_monitor
or from metaflow import _system_monitor
|
||
class SystemLogger(object): | ||
def __init__(self): | ||
self._flow = None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why both _flow
and _flow_name
?
from typing import Dict, Any, Optional, Union | ||
|
||
|
||
class SystemLogger(object): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that the implementation of this file depends on its usage outside of the flow context. Can you add comments in the code with the use cases so that it is easier to review and maintain going forward?
self._logger.start() | ||
return self._logger | ||
|
||
def set_logger( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other elements besides logger are being set in this method. consider renaming?
self._logger = LOGGING_SIDECARS[DEFAULT_EVENT_LOGGER]( | ||
flow=self.flow, env=self.environment | ||
) | ||
self._debug("Started logger outside of a flow") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be cleaner if self._logger
is started when logger
is constructed?
"project_flow_name": current.get("project_flow_name"), | ||
"trace_id": trace_id or None, | ||
} | ||
self.event_logger.send( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why use send
instead of log
? iirc send
was a workaround for a very specific use case (I need to dig through the slack conversations) and we would want to avoid introducing it too much in the code base.
"traceback": traceback.format_exc(), | ||
} | ||
) | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace it with something like event_logger.log(msg="", type="")
.
"event_value": 1, | ||
} | ||
) | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here - why is a pass
needed? is it to signify end of an indent block or something more? if the former, we can avoid introducing pass to maintain consistency of style in the code base
with self.monitor.measure("metaflow.task.duration"): | ||
try: | ||
with self.monitor.count("metaflow.task.start"): | ||
self.event_logger.log( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this log necessary?
Expose logger and monitor via a
system_current
singletonCurrently, users don't have the ability to use their implementation of logger and monitor sidecars in their own code. Additionally, if platform developers want to instrument their Metaflow extensions, they have to pass in the logger/monitor constructs to each one of their plugins, leading to code duplication.
This PR exposes two new singleton objects called
_system_logger
and_system_monitor
that can be used to access the monitor and the logger anywhere.metaflow.S3
, which is often used outside of a flow as well.Usage
The monitor/logger sidecar can be used in the following manner: