Skip to content

Commit f519699

Browse files
authored
update statshandler logging message (#6051)
### Description The recommendation in `StatsHandler` of setting the global logging config may not work in some cases (e.g. google colab and #5960) https://docs.python.org/3/library/logging.html#logging.basicConfig https://github.com/Project-MONAI/MONAI/blob/3eef61e82883e9bc6c08d26954fc86286df7d1b3/monai/handlers/stats_handler.py#L55 in general we shouldn't recommend changing the root logger directly. This PR changes the message to tune the module level loggers, updated example message will be: ``` .the effective log level of ignite.engine.engine.Engine higher than INFO, StatsHandler may not generate logs, please use the following code before running the engine to enable it: import logging logging.getLogger('ignite.engine.engine.Engine').setLevel(logging.INFO) ``` the updated commands are tested in the colab example: https://colab.research.google.com/drive/1boqy7ENpKrqaJoxFlbHIBnIODAs1Ih1T ### Types of changes <!--- Put an `x` in all the boxes that apply, and remove the not applicable items --> - [x] Non-breaking change (fix or new feature that would not break existing functionality). - [ ] Breaking change (fix or new feature that would cause existing functionality to change). - [x] New tests added to cover the changes. - [ ] Integration tests passed locally by running `./runtests.sh -f -u --net --coverage`. - [x] Quick tests passed locally by running `./runtests.sh --quick --unittests --disttests`. - [x] In-line docstrings updated. - [ ] Documentation updated, tested `make html` command in the `docs/` folder. --------- Signed-off-by: Wenqi Li <[email protected]>
1 parent c731aaa commit f519699

File tree

3 files changed

+30
-19
lines changed

3 files changed

+30
-19
lines changed

monai/apps/utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ def get_logger(
5858
(https://docs.python.org/3/library/logging.html#formatter-objects).
5959
`logger_handler` can be used to add an additional handler.
6060
"""
61+
adds_stdout_handler = module_name is not None and module_name not in logging.root.manager.loggerDict
6162
logger = logging.getLogger(module_name)
6263
logger.propagate = False
6364
logger.setLevel(logging.INFO)
64-
handler = logging.StreamHandler(sys.stdout)
65-
formatter = logging.Formatter(fmt=fmt, datefmt=datefmt)
66-
handler.setFormatter(formatter)
67-
logger.addHandler(handler)
65+
if adds_stdout_handler: # don't add multiple stdout or add to the root
66+
handler = logging.StreamHandler(sys.stdout)
67+
formatter = logging.Formatter(fmt=fmt, datefmt=datefmt)
68+
handler.setFormatter(formatter)
69+
logger.addHandler(handler)
6870
if logger_handler is not None:
6971
logger.addHandler(logger_handler)
7072
return logger

monai/handlers/stats_handler.py

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
import torch
2020

21+
from monai.apps import get_logger
2122
from monai.config import IgniteInfo
22-
from monai.utils import is_scalar, min_version, optional_import
23+
from monai.utils import deprecated_arg_default, is_scalar, min_version, optional_import
2324

2425
Events, _ = optional_import("ignite.engine", IgniteInfo.OPT_IMPORT_VERSION, min_version, "Events")
2526
if TYPE_CHECKING:
@@ -39,11 +40,11 @@ class StatsHandler:
3940
It can be used for any Ignite Engine(trainer, validator and evaluator).
4041
And it can support logging for epoch level and iteration level with pre-defined loggers.
4142
42-
Note that if `name` arg is None, will leverage `engine.logger` as default logger directly, otherwise,
43-
get logger from `logging.getLogger(name)`, we can setup a logger outside first with the same `name`.
44-
As the default log level of `RootLogger` is `WARNING`, may need to call
45-
`logging.basicConfig(stream=sys.stdout, level=logging.INFO)` before running this handler to enable
46-
the stats logging.
43+
Note that if ``name`` is None, this class will leverage `engine.logger` as the logger, otherwise,
44+
``logging.getLogger(name)`` is used. In both cases, it's important to make sure that the logging level is at least
45+
``INFO``. To change the level of logging, please call ``import ignite; ignite.utils.setup_logger(name)``
46+
(when ``name`` is not None) or ``engine.logger = ignite.utils.setup_logger(engine.logger.name, reset=True)``
47+
(when ``name`` is None) before running the engine with this handler attached.
4748
4849
Default behaviors:
4950
- When EPOCH_COMPLETED, logs ``engine.state.metrics`` using ``self.logger``.
@@ -52,18 +53,20 @@ class StatsHandler:
5253
5354
Usage example::
5455
55-
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
56+
import ignite
57+
import monai
5658
57-
trainer = SupervisedTrainer(...)
58-
StatsHandler(name="train_stats").attach(trainer)
59+
trainer = ignite.engine.Engine(lambda x, y: [0.0]) # an example trainer
60+
monai.handlers.StatsHandler(name="train_stats").attach(trainer)
5961
60-
trainer.run()
62+
trainer.run(range(3), max_epochs=4)
6163
6264
More details of example is available in the tutorial:
6365
https://github.com/Project-MONAI/tutorials/blob/master/modules/engines/unet_training_dict.py.
6466
6567
"""
6668

69+
@deprecated_arg_default("name", old_default=None, new_default="StatsHandler", since="1.1", replaced="1.3")
6770
def __init__(
6871
self,
6972
iteration_log: bool | Callable[[Engine, int], bool] = True,
@@ -122,7 +125,7 @@ def __init__(
122125
self.state_attributes = state_attributes
123126
self.tag_name = tag_name
124127
self.key_var_format = key_var_format
125-
self.logger = logging.getLogger(name) # if `name` is None, will default to `engine.logger` when attached
128+
self.logger = get_logger(name) # type: ignore
126129
self.name = name
127130

128131
def attach(self, engine: Engine) -> None:
@@ -135,10 +138,14 @@ def attach(self, engine: Engine) -> None:
135138
"""
136139
if self.name is None:
137140
self.logger = engine.logger
138-
if self.logger.getEffectiveLevel() > logging.INFO or logging.root.getEffectiveLevel() > logging.INFO:
141+
if self.logger.getEffectiveLevel() > logging.INFO:
142+
suggested = f"\n\nimport ignite\nignite.utils.setup_logger('{self.logger.name}', reset=True)"
143+
if self.logger.name != engine.logger.name:
144+
suggested += f"\nignite.utils.setup_logger('{engine.logger.name}', reset=True)"
145+
suggested += "\n\n"
139146
warnings.warn(
140-
"the effective log level of engine logger or RootLogger is higher than INFO, may not record log,"
141-
" please call `logging.basicConfig(stream=sys.stdout, level=logging.INFO)` to enable it."
147+
f"the effective log level of {self.logger.name} is higher than INFO, StatsHandler may not output logs,"
148+
f"\nplease use the following code before running the engine to enable it: {suggested}"
142149
)
143150
if self.iteration_log and not engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):
144151
event = Events.ITERATION_COMPLETED

tests/test_handler_stats.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@ def _train_func(engine, batch):
254254

255255
# set up testing handler
256256
stats_handler = StatsHandler(name=None, tag_name=key_to_print)
257-
stats_handler.attach(engine)
257+
engine.logger.setLevel(logging.WARNING)
258+
with self.assertWarns(Warning): # engine logging level warn
259+
stats_handler.attach(engine)
258260
# leverage `engine.logger` to print info
259261
engine.logger.setLevel(logging.INFO)
260262
level = logging.root.getEffectiveLevel()

0 commit comments

Comments
 (0)