Repository: modin-project/modin

Issue:
BUG: every layer of the modin stack logs the same error once
### Modin version checks

- [X] I have checked that this issue has not already been reported.

- [X] I have confirmed this bug exists on the latest released version of Modin.

- [X] I have confirmed this bug exists on the main branch of Modin. (In order to do this you can follow [this guide](https://modin.readthedocs.io/en/stable/getting_started/installation.html#installing-from-the-github-master-branch).)


### Reproducible Example

```python
from modin.config import Engine
from modin.config import LogMode
import modin.pandas as pd

LogMode.put('enable')
Engine.put('Python')
repr(pd.DataFrame([1]).applymap(lambda x: 1 + 'a'))
```


### Issue Description

I get multiple stack traces for the same exception because each logging layer within modin catches and logs the same exception: https://github.com/modin-project/modin/blob/9869832dbff51bf766936dadc38f8302bea47e81/modin/logging/logger_decorator.py#L135-L137


### Expected Behavior

We should only log the exception from the innermost layer

### Error Logs

This is my errror.log file in `.modin/logs/job_2023.06.05_15-09-16_dd47b098e4a9406a8d5f730975f74805/error.log`

<details>

```python-traceback

95899, 140704549500480, 2023-06-05,15:09:16.772512, STOP::PANDAS-API::DataFrame.__getattr__
Traceback (most recent call last):
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/pandas/dataframe.py", line 2503, in __getattr__
    raise err
  File "/Users/maheshvashishtha/software_sources/modin/modin/pandas/dataframe.py", line 2499, in __getattr__
    return object.__getattribute__(self, key)
AttributeError: 'DataFrame' object has no attribute '_cache'
95899, 140704549500480, 2023-06-05,15:09:16.774998, STOP::PANDAS-API::PandasDataframePartitionManager.map_partitions
Traceback (most recent call last):
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 58, in wait
    result = func(cls, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 518, in map_partitions
    [
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/execution/python/implementations/pandas_on_python/partitioning/partition.py", line 118, in apply
    return self.__constructor__(func(self._data.copy(), *args, **kwargs))
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 51, in <lambda>
    lambda x: function(x, *args, **kwargs), *call_args, **call_kwds
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9653, in applymap
    return self.apply(infer).__finalize__(self, "applymap")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9568, in apply
    return op.apply().__finalize__(self, method="apply")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 764, in apply
    return self.apply_standard()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 891, in apply_standard
    results, res_index = self.apply_series_generator()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 907, in apply_series_generator
    results[i] = self.f(v)
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9651, in infer
    return lib.map_infer(x.astype(object)._values, func, ignore_na=ignore_na)
  File "pandas/_libs/lib.pyx", line 2924, in pandas._libs.lib.map_infer
  File "<ipython-input-1-9a2b80c3907c>", line 7, in <lambda>
    repr(pd.DataFrame([1]).applymap(lambda x: 1 + 'a'))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
95899, 140704549500480, 2023-06-05,15:09:16.778186, STOP::PANDAS-API::PandasDataframe.map
Traceback (most recent call last):
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 376, in run_f_on_minimally_updated_metadata
    result = f(self, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/dataframe.py", line 1949, in map
    new_partitions = self._partition_mgr_cls.map_partitions(self._partitions, func)
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 58, in wait
    result = func(cls, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 518, in map_partitions
    [
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/execution/python/implementations/pandas_on_python/partitioning/partition.py", line 118, in apply
    return self.__constructor__(func(self._data.copy(), *args, **kwargs))
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 51, in <lambda>
    lambda x: function(x, *args, **kwargs), *call_args, **call_kwds
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9653, in applymap
    return self.apply(infer).__finalize__(self, "applymap")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9568, in apply
    return op.apply().__finalize__(self, method="apply")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 764, in apply
    return self.apply_standard()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 891, in apply_standard
    results, res_index = self.apply_series_generator()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 907, in apply_series_generator
    results[i] = self.f(v)
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9651, in infer
    return lib.map_infer(x.astype(object)._values, func, ignore_na=ignore_na)
  File "pandas/_libs/lib.pyx", line 2924, in pandas._libs.lib.map_infer
  File "<ipython-input-1-9a2b80c3907c>", line 7, in <lambda>
    repr(pd.DataFrame([1]).applymap(lambda x: 1 + 'a'))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
95899, 140704549500480, 2023-06-05,15:09:16.779518, STOP::PANDAS-API::PandasQueryCompiler.applymap
Traceback (most recent call last):
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 50, in caller
    query_compiler._modin_frame.map(
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 376, in run_f_on_minimally_updated_metadata
    result = f(self, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/dataframe.py", line 1949, in map
    new_partitions = self._partition_mgr_cls.map_partitions(self._partitions, func)
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 58, in wait
    result = func(cls, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 518, in map_partitions
    [
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/execution/python/implementations/pandas_on_python/partitioning/partition.py", line 118, in apply
    return self.__constructor__(func(self._data.copy(), *args, **kwargs))
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 51, in <lambda>
    lambda x: function(x, *args, **kwargs), *call_args, **call_kwds
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9653, in applymap
    return self.apply(infer).__finalize__(self, "applymap")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9568, in apply
    return op.apply().__finalize__(self, method="apply")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 764, in apply
    return self.apply_standard()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 891, in apply_standard
    results, res_index = self.apply_series_generator()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 907, in apply_series_generator
    results[i] = self.f(v)
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9651, in infer
    return lib.map_infer(x.astype(object)._values, func, ignore_na=ignore_na)
  File "pandas/_libs/lib.pyx", line 2924, in pandas._libs.lib.map_infer
  File "<ipython-input-1-9a2b80c3907c>", line 7, in <lambda>
    repr(pd.DataFrame([1]).applymap(lambda x: 1 + 'a'))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
95899, 140704549500480, 2023-06-05,15:09:16.779900, STOP::PANDAS-API::DataFrame.applymap
Traceback (most recent call last):
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/pandas/dataframe.py", line 376, in applymap
    query_compiler=self._query_compiler.applymap(
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 50, in caller
    query_compiler._modin_frame.map(
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/utils.py", line 376, in run_f_on_minimally_updated_metadata
    result = f(self, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/dataframe/dataframe.py", line 1949, in map
    new_partitions = self._partition_mgr_cls.map_partitions(self._partitions, func)
  File "/Users/maheshvashishtha/software_sources/modin/modin/logging/logger_decorator.py", line 134, in run_and_log
    result = obj(*args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 58, in wait
    result = func(cls, *args, **kwargs)
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 518, in map_partitions
    [
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/pandas/partitioning/partition_manager.py", line 519, in <listcomp>
    [part.apply(preprocessed_map_func) for part in row_of_parts]
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/execution/python/implementations/pandas_on_python/partitioning/partition.py", line 118, in apply
    return self.__constructor__(func(self._data.copy(), *args, **kwargs))
  File "/Users/maheshvashishtha/software_sources/modin/modin/core/dataframe/algebra/map.py", line 51, in <lambda>
    lambda x: function(x, *args, **kwargs), *call_args, **call_kwds
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9653, in applymap
    return self.apply(infer).__finalize__(self, "applymap")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9568, in apply
    return op.apply().__finalize__(self, method="apply")
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 764, in apply
    return self.apply_standard()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 891, in apply_standard
    results, res_index = self.apply_series_generator()
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/apply.py", line 907, in apply_series_generator
    results[i] = self.f(v)
  File "/Users/maheshvashishtha/anaconda3/envs/modin-dev/lib/python3.8/site-packages/pandas/core/frame.py", line 9651, in infer
    return lib.map_infer(x.astype(object)._values, func, ignore_na=ignore_na)
  File "pandas/_libs/lib.pyx", line 2924, in pandas._libs.lib.map_infer
  File "<ipython-input-1-9a2b80c3907c>", line 7, in <lambda>
    repr(pd.DataFrame([1]).applymap(lambda x: 1 + 'a'))
TypeError: unsupported operand type(s) for +: 'int' and 'str'


```

</details>


### Installed Versions

<details>

```
INSTALLED VERSIONS
------------------
commit           : 9869832dbff51bf766936dadc38f8302bea47e81
python           : 3.8.16.final.0
python-bits      : 64
OS               : Darwin
OS-release       : 22.5.0
Version          : Darwin Kernel Version 22.5.0: Mon Apr 24 20:51:50 PDT 2023; root:xnu-8796.121.2~5/RELEASE_X86_64
machine          : x86_64
processor        : i386
byteorder        : little
LC_ALL           : None
LANG             : en_US.UTF-8
LOCALE           : en_US.UTF-8

Modin dependencies
------------------
modin            : 0.22.0
ray              : 2.4.0
dask             : 2023.4.1
distributed      : 2023.4.1
hdk              : None

pandas dependencies
-------------------
pandas           : 1.5.3
numpy            : 1.24.3
pytz             : 2023.3
dateutil         : 2.8.2
setuptools       : 66.0.0
pip              : 23.0.1
Cython           : 0.29.34
pytest           : 7.3.1
hypothesis       : None
sphinx           : 7.0.0
blosc            : None
feather          : 0.4.1
xlsxwriter       : None
lxml.etree       : 4.9.2
html5lib         : None
pymysql          : None
psycopg2         : 2.9.6
jinja2           : 3.1.2
IPython          : 8.12.1
pandas_datareader: None
bs4              : 4.12.2
bottleneck       : None
brotli           : 1.0.9
fastparquet      : 2022.12.0
fsspec           : 2023.4.0
gcsfs            : None
matplotlib       : 3.7.1
numba            : None
numexpr          : 2.8.4
odfpy            : None
openpyxl         : 3.0.10
pandas_gbq       : 0.19.1
pyarrow          : 11.0.0
pyreadstat       : None
pyxlsb           : None
s3fs             : 2023.4.0
scipy            : 1.10.1
snappy           : None
sqlalchemy       : 1.4.45
tables           : 3.8.0
tabulate         : None
xarray           : 2023.1.0
xlrd             : 2.0.1
xlwt             : None
zstandard        : None
tzdata           : None
```

</details>



Discussion:

Resolving patch:
diff --git a/modin/logging/logger_decorator.py b/modin/logging/logger_decorator.py
--- a/modin/logging/logger_decorator.py
+++ b/modin/logging/logger_decorator.py
@@ -132,8 +132,17 @@ def run_and_log(*args: Tuple, **kwargs: Dict) -> Any:
             logger_level(start_line)
             try:
                 result = obj(*args, **kwargs)
-            except BaseException:
-                get_logger("modin.logger.errors").exception(stop_line)
+            except BaseException as e:
+                # Only log the exception if a deeper layer of the modin stack has not
+                # already logged it.
+                if not hasattr(e, "_modin_logged"):
+                    # use stack_info=True so that even if we are a few layers deep in
+                    # modin, we log a stack trace that includes calls to higher layers
+                    # of modin
+                    get_logger("modin.logger.errors").exception(
+                        stop_line, stack_info=True
+                    )
+                    e._modin_logged = True  # type: ignore[attr-defined]
                 raise
             finally:
                 logger_level(stop_line)
diff --git a/modin/utils.py b/modin/utils.py
--- a/modin/utils.py
+++ b/modin/utils.py
@@ -32,7 +32,7 @@
 import pandas
 import numpy as np
 
-from pandas.util._decorators import Appender
+from pandas.util._decorators import Appender  # type: ignore
 from pandas.util._print_versions import _get_sys_info, _get_dependency_info  # type: ignore[attr-defined]
 from pandas._typing import JSONSerializable
 


Test patch:
diff --git a/modin/test/test_logging.py b/modin/test/test_logging.py
--- a/modin/test/test_logging.py
+++ b/modin/test/test_logging.py
@@ -82,7 +82,31 @@ def func(do_raise):
 
     assert "func" in get_log_messages()["info"][0]
     assert "START" in get_log_messages()["info"][0]
-    assert "STOP" in get_log_messages("modin.logger.errors")["exception"][0]
+    assert get_log_messages("modin.logger.errors")["exception"] == [
+        "STOP::PANDAS-API::func"
+    ]
+
+
+def test_function_decorator_on_outer_function_6237(monkeypatch, get_log_messages):
+    @modin.logging.enable_logging
+    def inner_func():
+        raise ValueError()
+
+    @modin.logging.enable_logging
+    def outer_func():
+        inner_func()
+
+    with monkeypatch.context() as ctx:
+        # NOTE: we cannot patch in the fixture as mockin logger.getLogger()
+        # without monkeypatch.context() breaks pytest
+        mock_get_logger(ctx)
+
+        with pytest.raises(ValueError):
+            outer_func()
+
+    assert get_log_messages("modin.logger.errors")["exception"] == [
+        "STOP::PANDAS-API::inner_func"
+    ]
 
 
 def test_class_decorator(monkeypatch, get_log_messages):