Repository: streamlit/streamlit

Issue:
Streamlit logger working on root
### Summary

Upon import, Streamlit adds a new **global** log handler that dumps logs in text format. Packages should not be doing that, because it might break the logging convention of the host systems. 
In our case for example, we dump logs in JSON format and push it all to our logging aggregation system. Streamlit's log message break the format and so it happens that the only service we can't debug properly is Streamlit.

### Steps to reproduce
Nothing special, logging comes out of the box.

**Expected behavior:**
Streamlit should attach its handler to a specific logger namespace (e.g. `streamlit`) instead of attaching it to the root logger.

**Actual behavior:**

Streamlit attaches a stream handler to the root logger

### Is this a regression?

That is, did this use to work the way you expected in the past?
no

### Debug info

- Streamlit version: 1.1.0
- Python version: 3.8
- Using Conda? PipEnv? PyEnv? Pex?
- OS version: Any
- Browser version: Irrelevant

---

Community voting on feature requests enables the Streamlit team to understand which features are most important to our users.

**If you'd like the Streamlit team to prioritize this feature request, please use the 👍 (thumbs up emoji) reaction in response to the initial post.**



Discussion:
Just to chime in on this, I don't think the team meant to affect the root logger. Especially since streamlit is often used with other libraries. 

I think what was intended was for the "root" of all streamlit. The offending code in streamlit/logger.py is:
```python
# streamlit/logger.py inside of def get_logger(name: str) -> logging.Logger
119     if name == "root":
120         print(f'{name} logger instantiated')
121         logger = logging.getLogger() # <--- returns python's root logger!!!
122     else:
123         print(f'{name} logger instantiated')
124         logger = logging.getLogger(name)
```
If we make it return `logging.getLogger("streamlit")` when `name  == "root"`, then I think the problem is resolved. Streamlit then nicely contains all its logging in the streamlit.* namespace and everyone can go back to configuring their logging appropriately.

It gets called from different places for different versions of streamlit. In 1.11.x this happens in 
```python
# streamlit/__init__.py:51
 50
 51 _LOGGER = _logger.get_logger("root")
 52
```

In streamlit 1.19.0 this happens in 

```python
# streamlit/type_util.py:63
 62
 63 _LOGGER = _logger.get_logger("root")
 64
```

```python
# streamlit/delta_generator.py:138
137         if env_util.is_repl():
138             logger.get_logger("root").warning(
139                 f"\n  {warning} to view a Streamlit app on a browser, use Streamlit in a file and\n  run it with the following command:\n\n    streamlit ru    n [FILE_NAME] [ARGUMENTS]"
140             )
141
142         elif not runtime.exists() and config.get_option(
143             "global.showWarningOnDirectExecution"
144         ):
145             script_name = sys.argv[0]
146
147             logger.get_logger("root").warning(
148                 f"\n  {warning} to view this Streamlit app on a browser, run it with the following\n  command:\n\n    streamlit run {script_name} [ARGUMENT    S]"
149             )

```

```python
# streamlit/delta_generator.py:147

```

Resolving patch:
diff --git a/lib/streamlit/logger.py b/lib/streamlit/logger.py
--- a/lib/streamlit/logger.py
+++ b/lib/streamlit/logger.py
@@ -117,7 +117,7 @@ def get_logger(name: str) -> logging.Logger:
         return _loggers[name]
 
     if name == "root":
-        logger = logging.getLogger()
+        logger = logging.getLogger("streamlit")
     else:
         logger = logging.getLogger(name)
 


Test patch:
diff --git a/lib/tests/streamlit/delta_generator_test.py b/lib/tests/streamlit/delta_generator_test.py
--- a/lib/tests/streamlit/delta_generator_test.py
+++ b/lib/tests/streamlit/delta_generator_test.py
@@ -55,7 +55,7 @@ class RunWarningTest(unittest.TestCase):
     @patch("streamlit.runtime.Runtime.exists", MagicMock(return_value=False))
     def test_run_warning_presence(self):
         """Using Streamlit without `streamlit run` produces a warning."""
-        with self.assertLogs(level=logging.WARNING) as logs:
+        with self.assertLogs("streamlit", level=logging.WARNING) as logs:
             delta_generator._use_warning_has_been_displayed = False
             st.write("Using delta generator")
             output = "".join(logs.output)
@@ -66,7 +66,7 @@ def test_run_warning_presence(self):
     def test_run_warning_absence(self):
         """Using Streamlit through the CLI results in a Runtime being instantiated,
         so it produces no usage warning."""
-        with self.assertLogs(level=logging.WARNING) as logs:
+        with self.assertLogs("streamlit", level=logging.WARNING) as logs:
             delta_generator._use_warning_has_been_displayed = False
             st.write("Using delta generator")
             # assertLogs is being used as a context manager, but it also checks
diff --git a/lib/tests/streamlit/logger_test.py b/lib/tests/streamlit/logger_test.py
--- a/lib/tests/streamlit/logger_test.py
+++ b/lib/tests/streamlit/logger_test.py
@@ -56,7 +56,7 @@ def test_set_log_level_by_constant(self):
         ]
         for k in data:
             logger.set_log_level(k)
-            self.assertEqual(k, logging.getLogger().getEffectiveLevel())
+            self.assertEqual(k, logging.getLogger("streamlit").getEffectiveLevel())
 
     def test_set_log_level_error(self):
         """Test streamlit.logger.set_log_level."""