-
Notifications
You must be signed in to change notification settings - Fork 709
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
Deadlock when objects emit messages at garbage collection #712
Comments
Just noting that a solution which would work for me would be being able to skip emitting a message if the handler lock is not free (rather than waiting). I imagine that wouldn't cover everyone's use cases however! Yet another solution might be to allow pre-processing messages before the lock is acquired (a la #521), perhaps by extending the dynamic format functionality? However I don't think that solves the issue for more complicated output sinks, which may need to allocate some memory just for the purpose of sending the message as-is. |
I suppose another option would be to use a re-entrant lock, but I assume this was avoided for a reason? |
Hi @jacksmith15! This is a bug report of very great quality, thanks a lot for that! Your analysis is very interesting. I didn't realize the possibility of a deadlock if the logger was used in a I saw that you went further ahead and implemented a solution before opening a PR, thanks once again for your work. 👍 Regarding the chosen solution of making handlers re-entrant. Some time ago, I had to solve a bug causing a deadlock when a process was forked using the However, what I don't like about using a I thought for a long time that there was no way to cleanly prevent the user from facing a deadlock, but I finally realized: why not just check that the lock is not already in use by the current thread when calling the handler? The implementation could roughly look like that (exception handling not addressed): --- a/loguru/_handler.py
+++ b/loguru/_handler.py
@@ -2,7 +2,8 @@ import functools
import json
import multiprocessing
import os
-from threading import Thread
+from threading import Thread, local
from ._colorizer import Colorizer
from ._locks_machinery import create_handler_lock
@@ -57,6 +58,8 @@ class Handler:
self._exception_formatter = exception_formatter
self._id = id_
self._levels_ansi_codes = levels_ansi_codes # Warning, reference shared among handlers
+ self._local = local()
+ self._local.locked = False
self._decolorized_format = None
self._precolorized_formats = {}
@@ -168,6 +171,12 @@ class Handler:
str_record = Message(formatted)
str_record.record = record
+ if self._local.locked:
+ raise RuntimeError("Deadlock avoided")
+
+ self._local.locked = True
+
with self._lock:
if self._stopped:
return
@@ -176,6 +185,8 @@ class Handler:
else:
self._sink.write(str_record)
+ self._local.locked = False
+
except Exception:
if not self._error_interceptor.should_catch():
raise If I'm not mistaken, I don't think there is any possible data race or deadlock with this solution. Well, this implies that the message in This also has the advantage of avoiding deadlocks or infinite loops in case the logger is used inside a sink. This is the most common problem, and it would allow to warn the user with an exception explaining the problem explicitly. That's basically the first solution your proposed. What do you think of it? That's not ideal as some messages might be discarded, but I basically consider logging inside |
To verify I understand the And if the test case was modified to use: - loguru.logger.add(Sink(), level="INFO")
+ loguru.logger.add(Sink(), level="INFO", catch=False) Then the program would print a number of Assuming I've got that correct, I think this change would certainly be a vast improvement on the current behaviour ✔️ . The behaviour might still be surprising when they users encounter it, but its better than a deadlock. Possibly there is an argument for a custom exception type which is handled regardless of the As an aside, if you have the time I'd be interested in understanding the following more clearly:
|
Also I think its worth moving the |
Yeah, I will probably use a context manager to make sure Your interpretation of the proposed solution is perfectly correct. Although I'm not so sure about what will happen in practice, because the documentation of Regarding the possible problems using a Just imagine sinks like that for example: def sink_example_1(record):
with open("db.json") as file:
db = json.load(file)
# If garbage collector happens here -> lost messages.
db["messages"].append(record["message"])
with open("db.json", "w") as file:
json.dump(db, file)
def sink_example_2(record):
with threading.Lock:
# If garbage collector happens here -> deadlock.
db.write(record["message"])
def sink_example_3(record):
with open("file.log", "a") as file:
file.write("Message: ")
data = do_some_computation()
# If garbage collector happens here -> interleaved messages.
file.write(f"[data] record[message]\n") |
That is a very good point, I suppose it is likely better to always handle the error in the handler - because otherwise And thankyou for the explanation of the issues with re-entrancy, the examples make a lot of sense! |
I've raised a PR here: #720 Feel free to disregard if you already know how you want to go about implementing it 👍 |
[//]: # (dependabot-start)⚠️ **Dependabot is rebasing this PR**⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [loguru](https://github.com/Delgan/loguru) from 0.6.0 to 0.7.3. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/Delgan/loguru/releases">loguru's releases</a>.</em></p> <blockquote> <h2>0.7.3</h2> <ul> <li>Fix Cython incompatibility caused by the absence of underlying stack frames, which resulted in a <code>ValueError</code> during logging (<a href="https://redirect.github.com/Delgan/loguru/issues/88">#88</a>).</li> <li>Fix possible <code>RuntimeError</code> when removing all handlers with <code>logger.remove()</code> due to thread-safety issue (<a href="https://redirect.github.com/Delgan/loguru/issues/1183">#1183</a>, thanks <a href="https://github.com/jeremyk"><code>@jeremyk</code></a>).</li> <li>Fix <code>diagnose=True</code> option of exception formatting not working as expected with Python 3.13 (<a href="https://redirect.github.com/Delgan/loguru/issues/1235">#1235</a>, thanks <a href="https://github.com/etianen"><code>@etianen</code></a>).</li> <li>Fix non-standard level names not fully compatible with <code>logging.Formatter()</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/1231">#1231</a>, thanks <a href="https://github.com/yechielb2000"><code>@yechielb2000</code></a>).</li> <li>Fix inability to display a literal <code>"\"</code> immediately before color markups (<a href="https://redirect.github.com/Delgan/loguru/issues/988">#988</a>).</li> <li>Fix possible infinite recursion when an exception is raised from a <code>__repr__</code> method decorated with <code>logger.catch()</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/1044">#1044</a>).</li> <li>Improve performance of <code>datetime</code> formatting while logging messages (<a href="https://redirect.github.com/Delgan/loguru/issues/1201">#1201</a>, thanks <a href="https://github.com/trim21"><code>@trim21</code></a>).</li> <li>Reduce startup time in the presence of installed but unused <code>IPython</code> third-party library (<a href="https://redirect.github.com/Delgan/loguru/issues/1001">#1001</a>, thanks <a href="https://github.com/zakstucke"><code>@zakstucke</code></a>).</li> </ul> <h2>0.7.2</h2> <ul> <li>Add support for formatting of <code>ExceptionGroup</code> errors (<a href="https://redirect.github.com/Delgan/loguru/issues/805">#805</a>).</li> <li>Fix possible <code>RuntimeError</code> when using <code>multiprocessing.set_start_method()</code> after importing the <code>logger</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/974">#974</a>).</li> <li>Fix formatting of possible <code>__notes__</code> attached to an <code>Exception</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/980">#980</a>).</li> </ul> <h2>0.7.1</h2> <ul> <li>Add a new <code>context</code> optional argument to <code>logger.add()</code> specifying <code>multiprocessing</code> context (like <code>"spawn"</code> or <code>"fork"</code>) to be used internally instead of the default one (<a href="https://redirect.github.com/Delgan/loguru/issues/851">#851</a>).</li> <li>Add support for true colors on Windows using ANSI/VT console when available (<a href="https://redirect.github.com/Delgan/loguru/issues/934">#934</a>, thanks <a href="https://github.com/tunaflsh"><code>@tunaflsh</code></a>).</li> <li>Fix possible deadlock when calling <code>logger.complete()</code> with concurrent logging of an asynchronous sink (<a href="https://redirect.github.com/Delgan/loguru/issues/906">#906</a>).</li> <li>Fix file possibly rotating too early or too late when re-starting an application around midnight (<a href="https://redirect.github.com/Delgan/loguru/issues/894">#894</a>).</li> <li>Fix inverted <code>"<hide>"</code> and <code>"<strike>"</code> color tags (<a href="https://redirect.github.com/Delgan/loguru/issues/943">#943</a>, thanks <a href="https://github.com/tunaflsh"><code>@tunaflsh</code></a>).</li> <li>Fix possible untraceable errors raised when logging non-unpicklable <code>Exception</code> instances while using <code>enqueue=True</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/329">#329</a>).</li> <li>Fix possible errors raised when logging non-picklable <code>Exception</code> instances while using <code>enqueue=True</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/342">#342</a>, thanks <a href="https://github.com/ncoudene"><code>@ncoudene</code></a>).</li> <li>Fix missing seconds and microseconds when formatting timezone offset that requires such accuracy (<a href="https://redirect.github.com/Delgan/loguru/issues/961">#961</a>).</li> <li>Raise <code>ValueError</code> if an attempt to use nanosecond precision for time formatting is detected (<a href="https://redirect.github.com/Delgan/loguru/issues/855">#855</a>).</li> </ul> <h2>0.7.0</h2> <ul> <li>Update <code>InterceptHandler</code> recipe to make it compatible with Python 3.11 (<a href="https://redirect.github.com/Delgan/loguru/issues/654">#654</a>).</li> <li>Add a new <code>watch</code> optional argument to file sinks in order to automatically re-create possibly deleted or changed file (<a href="https://redirect.github.com/Delgan/loguru/issues/471">#471</a>).</li> <li>Make <code>patch()</code> calls cumulative instead of overriding the possibly existing patching function (<a href="https://redirect.github.com/Delgan/loguru/issues/462">#462</a>).</li> <li>Make sinks added with <code>enqueue=True</code> and <code>catch=False</code> still process logged messages in case of internal exception (<a href="https://redirect.github.com/Delgan/loguru/issues/833">#833</a>).</li> <li>Avoid possible deadlocks caused by re-using the logger inside a sink, a signal handler or a <code>__del__</code> method. Since the logger is not re-entrant, such misuse will be detected and will now generate a <code>RuntimeError</code> (<a href="https://redirect.github.com/Delgan/loguru/issues/712">#712</a>, thanks <a href="https://github.com/jacksmith15"><code>@jacksmith15</code></a>).</li> <li>Fix file sink rotation using an aware <code>datetime.time</code> for which the timezone was ignored (<a href="https://redirect.github.com/Delgan/loguru/issues/697">#697</a>).</li> <li>Fix logs colorization not automatically enabled for Jupyter Notebook and Google Colab (<a href="https://redirect.github.com/Delgan/loguru/issues/494">#494</a>).</li> <li>Fix logs colorization not automatically enabled for Github Actions and others CI platforms (<a href="https://redirect.github.com/Delgan/loguru/issues/604">#604</a>).</li> <li>Fix <code>logger.complete()</code> possibly hanging forever when <code>enqueue=True</code> and <code>catch=False</code> if internal thread killed due to <code>Exception</code> raised by sink (<a href="https://redirect.github.com/Delgan/loguru/issues/647">#647</a>).</li> <li>Fix incompatibility with <code>freezegun</code> library used to simulate time (<a href="https://redirect.github.com/Delgan/loguru/issues/600">#600</a>).</li> <li>Raise exception if <code>logger.catch()</code> is used to wrap a class instead of a function to avoid unexpected behavior (<a href="https://redirect.github.com/Delgan/loguru/issues/623">#623</a>).</li> </ul> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/Delgan/loguru/blob/master/CHANGELOG.rst">loguru's changelog</a>.</em></p> <blockquote> <h1><code>0.7.3</code>_ (2024-12-06)</h1> <ul> <li>Fix Cython incompatibility caused by the absence of underlying stack frames, which resulted in a <code>ValueError</code> during logging (<code>[#88](Delgan/loguru#88) <https://github.com/Delgan/loguru/issues/88></code>_).</li> <li>Fix possible <code>RuntimeError</code> when removing all handlers with <code>logger.remove()</code> due to thread-safety issue (<code>[#1183](Delgan/loguru#1183) <https://github.com/Delgan/loguru/issues/1183></code><em>, thanks <code>@jeremyk <https://github.com/jeremyk></code></em>).</li> <li>Fix <code>diagnose=True</code> option of exception formatting not working as expected with Python 3.13 (<code>[#1235](Delgan/loguru#1235) <https://github.com/Delgan/loguru/issues/1235></code><em>, thanks <code>@etianen <https://github.com/etianen></code></em>).</li> <li>Fix non-standard level names not fully compatible with <code>logging.Formatter()</code> (<code>[#1231](Delgan/loguru#1231) <https://github.com/Delgan/loguru/issues/1231></code><em>, thanks <code>@yechielb2000 <https://github.com/yechielb2000></code></em>).</li> <li>Fix inability to display a literal <code>"\"</code> immediately before color markups (<code>[#988](Delgan/loguru#988) <https://github.com/Delgan/loguru/issues/988></code>_).</li> <li>Fix possible infinite recursion when an exception is raised from a <code>__repr__</code> method decorated with <code>logger.catch()</code> (<code>[#1044](Delgan/loguru#1044) <https://github.com/Delgan/loguru/issues/1044></code>_).</li> <li>Improve performance of <code>datetime</code> formatting while logging messages (<code>[#1201](Delgan/loguru#1201) <https://github.com/Delgan/loguru/issues/1201></code><em>, thanks <code>@trim21 <https://github.com/trim21></code></em>).</li> <li>Reduce startup time in the presence of installed but unused <code>IPython</code> third-party library (<code>[#1001](Delgan/loguru#1001) <https://github.com/Delgan/loguru/issues/1001></code><em>, thanks <code>@zakstucke <https://github.com/zakstucke></code></em>).</li> </ul> <h1><code>0.7.2</code>_ (2023-09-11)</h1> <ul> <li>Add support for formatting of <code>ExceptionGroup</code> errors (<code>[#805](Delgan/loguru#805) <https://github.com/Delgan/loguru/issues/805></code>_).</li> <li>Fix possible <code>RuntimeError</code> when using <code>multiprocessing.set_start_method()</code> after importing the <code>logger</code> (<code>[#974](Delgan/loguru#974) <https://github.com/Delgan/loguru/issues/974></code>_).</li> <li>Fix formatting of possible <code>__notes__</code> attached to an <code>Exception</code> (<code>[#980](Delgan/loguru#980) <https://github.com/Delgan/loguru/issues/980></code>_).</li> </ul> <h1><code>0.7.1</code>_ (2023-09-04)</h1> <ul> <li>Add a new <code>context</code> optional argument to <code>logger.add()</code> specifying <code>multiprocessing</code> context (like <code>"spawn"</code> or <code>"fork"</code>) to be used internally instead of the default one (<code>[#851](Delgan/loguru#851) <https://github.com/Delgan/loguru/issues/851></code>_).</li> <li>Add support for true colors on Windows using ANSI/VT console when available (<code>[#934](Delgan/loguru#934) <https://github.com/Delgan/loguru/issues/934></code><em>, thanks <code>@tunaflsh <https://github.com/tunaflsh></code></em>).</li> <li>Fix possible deadlock when calling <code>logger.complete()</code> with concurrent logging of an asynchronous sink (<code>[#906](Delgan/loguru#906) <https://github.com/Delgan/loguru/issues/906></code>_).</li> <li>Fix file possibly rotating too early or too late when re-starting an application around midnight (<code>[#894](Delgan/loguru#894) <https://github.com/Delgan/loguru/issues/894></code>_).</li> <li>Fix inverted <code>"<hide>"</code> and <code>"<strike>"</code> color tags (<code>[#943](Delgan/loguru#943) <https://github.com/Delgan/loguru/pull/943></code><em>, thanks <code>@tunaflsh <https://github.com/tunaflsh></code></em>).</li> <li>Fix possible untraceable errors raised when logging non-unpicklable <code>Exception</code> instances while using <code>enqueue=True</code> (<code>[#329](Delgan/loguru#329) <https://github.com/Delgan/loguru/issues/329></code>_).</li> <li>Fix possible errors raised when logging non-picklable <code>Exception</code> instances while using <code>enqueue=True</code> (<code>[#342](Delgan/loguru#342) <https://github.com/Delgan/loguru/issues/342></code><em>, thanks <code>@ncoudene <https://github.com/ncoudene></code></em>).</li> <li>Fix missing seconds and microseconds when formatting timezone offset that requires such accuracy (<code>[#961](Delgan/loguru#961) <https://github.com/Delgan/loguru/issues/961></code>_).</li> <li>Raise <code>ValueError</code> if an attempt to use nanosecond precision for time formatting is detected (<code>[#855](Delgan/loguru#855) <https://github.com/Delgan/loguru/issues/855></code>_).</li> </ul> <h1><code>0.7.0</code>_ (2023-04-10)</h1> <ul> <li>Update <code>InterceptHandler</code> recipe to make it compatible with Python 3.11 (<code>[#654](Delgan/loguru#654) <https://github.com/Delgan/loguru/issues/654></code>_).</li> <li>Add a new <code>watch</code> optional argument to file sinks in order to automatically re-create possibly deleted or changed file (<code>[#471](Delgan/loguru#471) <https://github.com/Delgan/loguru/issues/471></code>_).</li> <li>Make <code>patch()</code> calls cumulative instead of overriding the possibly existing patching function (<code>[#462](Delgan/loguru#462) <https://github.com/Delgan/loguru/issues/462></code>_).</li> <li>Make sinks added with <code>enqueue=True</code> and <code>catch=False</code> still process logged messages in case of internal exception (<code>[#833](Delgan/loguru#833) <https://github.com/Delgan/loguru/issues/833></code>_).</li> <li>Avoid possible deadlocks caused by re-using the logger inside a sink, a signal handler or a <code>__del__</code> method. Since the logger is not re-entrant, such misuse will be detected and will now generate a <code>RuntimeError</code> (<code>[#712](Delgan/loguru#712) <https://github.com/Delgan/loguru/issues/712></code><em>, thanks <code>@jacksmith15 <https://github.com/jacksmith15></code></em>).</li> <li>Fix file sink rotation using an aware <code>datetime.time</code> for which the timezone was ignored (<code>[#697](Delgan/loguru#697) <https://github.com/Delgan/loguru/issues/697></code>_).</li> <li>Fix logs colorization not automatically enabled for Jupyter Notebook and Google Colab (<code>[#494](Delgan/loguru#494) <https://github.com/Delgan/loguru/issues/494></code>_).</li> <li>Fix logs colorization not automatically enabled for Github Actions and others CI platforms (<code>[#604](Delgan/loguru#604) <https://github.com/Delgan/loguru/issues/604></code>_).</li> <li>Fix <code>logger.complete()</code> possibly hanging forever when <code>enqueue=True</code> and <code>catch=False</code> if internal thread killed due to <code>Exception</code> raised by sink (<code>[#647](Delgan/loguru#647) <https://github.com/Delgan/loguru/issues/647></code>_).</li> <li>Fix incompatibility with <code>freezegun</code> library used to simulate time (<code>[#600](Delgan/loguru#600) <https://github.com/Delgan/loguru/issues/600></code>_).</li> <li>Raise exception if <code>logger.catch()</code> is used to wrap a class instead of a function to avoid unexpected behavior (<code>[#623](Delgan/loguru#623) <https://github.com/Delgan/loguru/issues/623></code>_).</li> </ul> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/Delgan/loguru/commit/ae3bfd1b85b6b4a3db535f69b975687c79498be4"><code>ae3bfd1</code></a> Bump version to 0.7.3</li> <li><a href="https://github.com/Delgan/loguru/commit/ccca3566cc00c22eed8659705e45386fa2101b5d"><code>ccca356</code></a> Replace "notifiers" (seems unmaintained) with "apprise" in docs (<a href="https://redirect.github.com/Delgan/loguru/issues/1251">#1251</a>)</li> <li><a href="https://github.com/Delgan/loguru/commit/a372814bf79d47628e66ca9a91072f53fba032f8"><code>a372814</code></a> Configure "trusted publishing" in Github workflow</li> <li><a href="https://github.com/Delgan/loguru/commit/633016db07e5dff63bc05dd3c4d5aa81b6190700"><code>633016d</code></a> Use tox to define the "release" command</li> <li><a href="https://github.com/Delgan/loguru/commit/ef12cbbaf54fb2e64ca66b0a90675cdf9e8a522f"><code>ef12cbb</code></a> Convert README from RST to MD (fix PyPI packaging)</li> <li><a href="https://github.com/Delgan/loguru/commit/cb3314a5af107ad175a9bafa11b1b79670e3067a"><code>cb3314a</code></a> Add Github action to verify packaging</li> <li><a href="https://github.com/Delgan/loguru/commit/6161a13b79e1a3a29e922926b44f26edbcc3c06a"><code>6161a13</code></a> Complete the "Troubleshooting" docs with more examples</li> <li><a href="https://github.com/Delgan/loguru/commit/7f5001fe46393627ced287bba2c4064442c3fd25"><code>7f5001f</code></a> Simplify the example of dynamic handler level</li> <li><a href="https://github.com/Delgan/loguru/commit/2e0cd7bb5105461057c56aefb225da569882ad29"><code>2e0cd7b</code></a> Move "Introduction to logging" docs to a new section</li> <li><a href="https://github.com/Delgan/loguru/commit/72b93d1a24d1491ce7ba752fe36c14f9570a5d63"><code>72b93d1</code></a> Correct outdated instructions for reporting a vulnerability</li> <li>Additional commits viewable in <a href="https://github.com/Delgan/loguru/compare/0.6.0...0.7.3">compare view</a></li> </ul> </details> <br /> [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=loguru&package-manager=pip&previous-version=0.6.0&new-version=0.7.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Hello 👋
I have found a issue whereby loguru can hit a deadlock due to garbage collection. Specifically, if there is an object in memory that has the following properties:
__del__
finalizer which explicitly or implicitly triggers a log statementThen any memory assignment during log handling has a chance to trigger a deadlock.
Reproducible test case
Python version: 3.8.12
Loguru version: 0.6.0
The following is a script which demonstrates the deadlock:
Expected behaviour
A series of
Hello
logs are emitted, interspersed withGoodbye!
logs.Actual behaviour
The program reaches a deadlock, requiring a SIGTERM to exit.
Explanation of the issue
The
emit
method of loguru's handler uses a lock to avoid contention for the log handler. It is for this reason, that emitting additional log messages in custom sinks is not supported and will result in deadlocks (see #390).This seems to be a relatively sensible limitation, however the nature of the generational garbage collector in CPython can cause this to happen inadvertently and unpredictably in code which has cyclic isolates and performs some level of processing during log handling.
Real world example
The reproducible example above seems contrived, and one might suggest that the answer is simply to avoid reference cycles, so I will give an actual example of this that I've seen. The Python Elasticsearch client contains reference cycles as a means for sharing connections between interfaces.
As a transport it uses aiohttp, which implements a
__del__
garbage collection finalizer. This in turn invokes the asyncio exception handler, which defaults to emitting a log statement. Since this is a stdliblogging
call, it doesn't cause a problem on its own, but it is likely that users of loguru will set up some kind of intercept handler to forward third party stdlib logs tologuru
.Additional notes
It took me some time to figure out a reproducible example for this, as I was only familiar with the reference counting part of the garbage collector. I found the this article useful to understand the generational collector, which is what led me to isolate the circumstances to reproduce.
The text was updated successfully, but these errors were encountered: