Repository: microsoft/debugpy

Issue:
`debugpy.log_to()` should be able to customize pydevd logging too
Right now `pydevd` only has the logging set through environment variables which are set at import time in `pydevd_constants`.

The problem is that when the user sets the logging using `debugpy.log_to` if `pydevd` was already imported the logging in `pydevd` is no longer changed.

So, the idea is providing a `pydevd.log_to` api.

Some additional work may also needed to ensure that important logging messages that are currently only logged during import time aren't lost in this case and that new subprocesses are also properly logged (if `pydevd.log_to` is called after a subprocess is created that subprocess logging won't be changed, but new subprocesses need to inherit the new configuration).

Discussion:
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=microsoft_debugpy&pullRequest=1040)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=microsoft_debugpy&pullRequest=1040)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=duplicated_lines_density&view=list) No Duplication information


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=microsoft_debugpy&pullRequest=1040)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=microsoft_debugpy&pullRequest=1040&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=microsoft_debugpy&pullRequest=1040&metric=duplicated_lines_density&view=list) No Duplication information



Patch:
diff --git a/src/debugpy/server/api.py b/src/debugpy/server/api.py
index 515ea060..e2a21e59 100644
--- a/src/debugpy/server/api.py
+++ b/src/debugpy/server/api.py
@@ -58,6 +58,7 @@ def ensure_logging():
     ensure_logging.ensured = True
     log.to_file(prefix="debugpy.server")
     log.describe_environment("Initial environment:")
+    pydevd.log_to(log.log_dir + "/debugpy.pydevd.log")
 
 
 ensure_logging.ensured = False


Test patch:
diff --git a/tests/debugpy/test_log.py b/tests/debugpy/test_log.py
index afe07597..6100474e 100644
--- a/tests/debugpy/test_log.py
+++ b/tests/debugpy/test_log.py
@@ -10,7 +10,7 @@ from tests.debug import runners, targets
 
 
 @contextlib.contextmanager
-def check_logs(tmpdir, run, pydevd_log):
+def check_logs(tmpdir, run):
     # For attach_pid, there's ptvsd.server process that performs the injection,
     # and then there's the debug server that is injected into the debuggee.
     server_count = 2 if type(run).__name__ == "attach_pid" else 1
@@ -18,7 +18,7 @@ def check_logs(tmpdir, run, pydevd_log):
     expected_logs = {
         "debugpy.adapter-*.log": 1,
         "debugpy.launcher-*.log": 1 if run.request == "launch" else 0,
-        "debugpy.pydevd.*.log": server_count if pydevd_log else 0,
+        "debugpy.pydevd.*.log": server_count,
         "debugpy.server-*.log": server_count,
     }
 
@@ -43,7 +43,7 @@ def test_log_dir(pyfile, tmpdir, run, target):
     # Depending on the method, the runner will use either `debugpy --log-dir ...`
     # or `debugpy.log_to() ...`.
     run = run.with_options(log_dir=tmpdir.strpath)
-    with check_logs(tmpdir, run, pydevd_log=False):
+    with check_logs(tmpdir, run):
         with debug.Session() as session:
             session.log_dir = None
 
@@ -62,7 +62,7 @@ def test_log_dir_env(pyfile, tmpdir, run, target):
         debuggee.setup()
         assert backchannel.receive() == "proceed"
 
-    with check_logs(tmpdir, run, pydevd_log=True):
+    with check_logs(tmpdir, run):
         with debug.Session() as session:
             session.log_dir = None
             session.spawn_adapter.env["DEBUGPY_LOG_DIR"] = tmpdir.strpath