We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
allow to follow one thread in logs
Motivation
----------
We use a decorated logger to prepend the worker number to the log messages, but this number is not actually stable through the lifetime of the thread. 

When trying to debug an issue, it may be useful to easily filter all messages from the same thread in kibana. 

Modification
------------
Prepend some hash to the decorated logger for easy filtering 



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/rucio/daemons/common.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 # Copyright European Organization for Nuclear Research (CERN) since 2012
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #    http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 import datetime
17 import logging
18 import os
19 import socket
20 import threading
21 import time
22 
23 from rucio.common.logging import formatted_logger
24 from rucio.common.utils import PriorityQueue
25 from rucio.core import heartbeat
26 
27 
28 class HeartbeatHandler:
29     """
30     Simple contextmanager which sets a heartbeat and associated logger on entry and cleans up the heartbeat on exit.
31     """
32 
33     def __init__(self, executable, renewal_interval, logger_prefix=None):
34         """
35         :param executable: the executable name which will be set in heartbeats
36         :param renewal_interval: the interval at which the heartbeat will be renewed in the database.
37         Calls to live() in-between intervals will re-use the locally cached heartbeat.
38         :param logger_prefix: the prefix to be prepended to all log messages
39         """
40         self.executable = executable
41         self.renewal_interval = renewal_interval
42         self.older_than = renewal_interval * 10 if renewal_interval and renewal_interval > 0 else None  # 10 was chosen without any particular reason
43         self.logger_prefix = logger_prefix or executable
44 
45         self.hostname = socket.getfqdn()
46         self.pid = os.getpid()
47         self.hb_thread = threading.current_thread()
48 
49         self.logger = None
50         self.last_heart_beat = None
51         self.last_time = None
52         self.last_payload = None
53 
54     def __enter__(self):
55         heartbeat.sanity_check(executable=self.executable, hostname=self.hostname)
56         self.live()
57         return self
58 
59     def __exit__(self, exc_type, exc_val, exc_tb):
60         if self.last_heart_beat:
61             heartbeat.die(self.executable, self.hostname, self.pid, self.hb_thread)
62             if self.logger:
63                 self.logger(logging.INFO, 'Heartbeat cleaned up')
64 
65     def live(self, force_renew=False, payload=None):
66         """
67         :return: a tuple: <the number of the current worker>, <total number of workers>, <decorated logger>
68         """
69         if force_renew \
70                 or not self.last_time \
71                 or self.last_time < datetime.datetime.now() - datetime.timedelta(seconds=self.renewal_interval) \
72                 or self.last_payload != payload:
73             if self.older_than:
74                 self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload, older_than=self.older_than)
75             else:
76                 self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)
77 
78             prefix = '%s[%i/%i]: ' % (self.logger_prefix, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])
79             self.logger = formatted_logger(logging.log, prefix + '%s')
80 
81             if not self.last_time:
82                 self.logger(logging.DEBUG, 'First heartbeat set')
83             else:
84                 self.logger(logging.DEBUG, 'Heartbeat renewed')
85             self.last_time = datetime.datetime.now()
86             self.last_payload = payload
87 
88         return self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'], self.logger
89 
90 
91 def run_daemon(once, graceful_stop, executable, logger_prefix, partition_wait_time, sleep_time, run_once_fnc, activities=None):
92     """
93     Run the daemon loop and call the function run_once_fnc at each iteration
94     :param once: Whether to stop after one iteration
95     :param graceful_stop: the threading.Event() object used for graceful stop of the daemon
96     :param executable: the name of the executable used for hearbeats
97     :param logger_prefix: the prefix to be prepended to all log messages
98     :param partition_wait_time: time to wait for database partition rebalancing before starting the actual daemon loop
99     :param sleep_time: time to sleep between the iterations of the daemon
100     :param run_once_fnc: the function which will do the actual work
101     :param activities: optional list of activities on which to work. The run_once_fnc will be called on activities one by one.
102     """
103 
104     with HeartbeatHandler(executable=executable, renewal_interval=sleep_time - 1, logger_prefix=logger_prefix) as heartbeat_handler:
105         logger = heartbeat_handler.logger
106         logger(logging.INFO, 'started')
107 
108         if partition_wait_time:
109             graceful_stop.wait(partition_wait_time)
110             _, _, logger = heartbeat_handler.live(force_renew=True)
111 
112         activity_next_exe_time = PriorityQueue()
113         for activity in activities or [None]:
114             activity_next_exe_time[activity] = time.time()
115 
116         while not graceful_stop.is_set() and activity_next_exe_time:
117             if once:
118                 activity = activity_next_exe_time.pop()
119                 time_to_sleep = 0
120             else:
121                 activity = activity_next_exe_time.top()
122                 time_to_sleep = activity_next_exe_time[activity] - time.time()
123 
124             if time_to_sleep > 0:
125                 if activity:
126                     logger(logging.DEBUG, 'Switching to activity %s and sleeping %s seconds', activity, time_to_sleep)
127                 else:
128                     logger(logging.DEBUG, 'Sleeping %s seconds', time_to_sleep)
129                 graceful_stop.wait(time_to_sleep)
130             else:
131                 if activity:
132                     logger(logging.DEBUG, 'Switching to activity %s', activity)
133                 else:
134                     logger(logging.DEBUG, 'Starting next iteration')
135 
136             _, _, logger = heartbeat_handler.live()
137 
138             must_sleep = True
139             start_time = time.time()
140             try:
141                 must_sleep = run_once_fnc(activity=activity, heartbeat_handler=heartbeat_handler)
142                 if must_sleep is None:
143                     # The run_once_fnc doesn't explicitly return whether we must sleep,
144                     # so sleep by default
145                     must_sleep = True
146             except Exception:
147                 logger(logging.CRITICAL, "Exception", exc_info=True)
148                 if once:
149                     raise
150 
151             if not once:
152                 if must_sleep:
153                     time_diff = time.time() - start_time
154                     time_to_sleep = max(1, sleep_time - time_diff)
155                     activity_next_exe_time[activity] = time.time() + time_to_sleep
156                 else:
157                     activity_next_exe_time[activity] = time.time() + 1
158 
159         if not once:
160             logger(logging.INFO, 'Graceful stop requested')
161
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/rucio/daemons/common.py b/lib/rucio/daemons/common.py
--- a/lib/rucio/daemons/common.py
+++ b/lib/rucio/daemons/common.py
@@ -14,6 +14,7 @@
 # limitations under the License.
 
 import datetime
+import hashlib
 import logging
 import os
 import socket
@@ -45,6 +46,7 @@
         self.hostname = socket.getfqdn()
         self.pid = os.getpid()
         self.hb_thread = threading.current_thread()
+        self.logger_id = hashlib.sha1(f'{self.hostname}:{self.pid}:{self.hb_thread}'.encode('utf-8')).hexdigest()[:7]
 
         self.logger = None
         self.last_heart_beat = None
@@ -75,7 +77,7 @@
             else:
                 self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)
 
-            prefix = '%s[%i/%i]: ' % (self.logger_prefix, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])
+            prefix = '%s[%s:%i/%i]: ' % (self.logger_prefix, self.logger_id, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])
             self.logger = formatted_logger(logging.log, prefix + '%s')
 
             if not self.last_time:

{"golden_diff": "diff --git a/lib/rucio/daemons/common.py b/lib/rucio/daemons/common.py\n--- a/lib/rucio/daemons/common.py\n+++ b/lib/rucio/daemons/common.py\n@@ -14,6 +14,7 @@\n # limitations under the License.\n \n import datetime\n+import hashlib\n import logging\n import os\n import socket\n@@ -45,6 +46,7 @@\n         self.hostname = socket.getfqdn()\n         self.pid = os.getpid()\n         self.hb_thread = threading.current_thread()\n+        self.logger_id = hashlib.sha1(f'{self.hostname}:{self.pid}:{self.hb_thread}'.encode('utf-8')).hexdigest()[:7]\n \n         self.logger = None\n         self.last_heart_beat = None\n@@ -75,7 +77,7 @@\n             else:\n                 self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)\n \n-            prefix = '%s[%i/%i]: ' % (self.logger_prefix, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])\n+            prefix = '%s[%s:%i/%i]: ' % (self.logger_prefix, self.logger_id, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])\n             self.logger = formatted_logger(logging.log, prefix + '%s')\n \n             if not self.last_time:\n", "issue": "allow to follow one thread in logs\nMotivation\r\n----------\r\nWe use a decorated logger to prepend the worker number to the log messages, but this number is not actually stable through the lifetime of the thread. \r\n\r\nWhen trying to debug an issue, it may be useful to easily filter all messages from the same thread in kibana. \r\n\r\nModification\r\n------------\r\nPrepend some hash to the decorated logger for easy filtering \r\n\r\n\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright European Organization for Nuclear Research (CERN) since 2012\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nimport logging\nimport os\nimport socket\nimport threading\nimport time\n\nfrom rucio.common.logging import formatted_logger\nfrom rucio.common.utils import PriorityQueue\nfrom rucio.core import heartbeat\n\n\nclass HeartbeatHandler:\n    \"\"\"\n    Simple contextmanager which sets a heartbeat and associated logger on entry and cleans up the heartbeat on exit.\n    \"\"\"\n\n    def __init__(self, executable, renewal_interval, logger_prefix=None):\n        \"\"\"\n        :param executable: the executable name which will be set in heartbeats\n        :param renewal_interval: the interval at which the heartbeat will be renewed in the database.\n        Calls to live() in-between intervals will re-use the locally cached heartbeat.\n        :param logger_prefix: the prefix to be prepended to all log messages\n        \"\"\"\n        self.executable = executable\n        self.renewal_interval = renewal_interval\n        self.older_than = renewal_interval * 10 if renewal_interval and renewal_interval > 0 else None  # 10 was chosen without any particular reason\n        self.logger_prefix = logger_prefix or executable\n\n        self.hostname = socket.getfqdn()\n        self.pid = os.getpid()\n        self.hb_thread = threading.current_thread()\n\n        self.logger = None\n        self.last_heart_beat = None\n        self.last_time = None\n        self.last_payload = None\n\n    def __enter__(self):\n        heartbeat.sanity_check(executable=self.executable, hostname=self.hostname)\n        self.live()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.last_heart_beat:\n            heartbeat.die(self.executable, self.hostname, self.pid, self.hb_thread)\n            if self.logger:\n                self.logger(logging.INFO, 'Heartbeat cleaned up')\n\n    def live(self, force_renew=False, payload=None):\n        \"\"\"\n        :return: a tuple: <the number of the current worker>, <total number of workers>, <decorated logger>\n        \"\"\"\n        if force_renew \\\n                or not self.last_time \\\n                or self.last_time < datetime.datetime.now() - datetime.timedelta(seconds=self.renewal_interval) \\\n                or self.last_payload != payload:\n            if self.older_than:\n                self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload, older_than=self.older_than)\n            else:\n                self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)\n\n            prefix = '%s[%i/%i]: ' % (self.logger_prefix, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])\n            self.logger = formatted_logger(logging.log, prefix + '%s')\n\n            if not self.last_time:\n                self.logger(logging.DEBUG, 'First heartbeat set')\n            else:\n                self.logger(logging.DEBUG, 'Heartbeat renewed')\n            self.last_time = datetime.datetime.now()\n            self.last_payload = payload\n\n        return self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'], self.logger\n\n\ndef run_daemon(once, graceful_stop, executable, logger_prefix, partition_wait_time, sleep_time, run_once_fnc, activities=None):\n    \"\"\"\n    Run the daemon loop and call the function run_once_fnc at each iteration\n    :param once: Whether to stop after one iteration\n    :param graceful_stop: the threading.Event() object used for graceful stop of the daemon\n    :param executable: the name of the executable used for hearbeats\n    :param logger_prefix: the prefix to be prepended to all log messages\n    :param partition_wait_time: time to wait for database partition rebalancing before starting the actual daemon loop\n    :param sleep_time: time to sleep between the iterations of the daemon\n    :param run_once_fnc: the function which will do the actual work\n    :param activities: optional list of activities on which to work. The run_once_fnc will be called on activities one by one.\n    \"\"\"\n\n    with HeartbeatHandler(executable=executable, renewal_interval=sleep_time - 1, logger_prefix=logger_prefix) as heartbeat_handler:\n        logger = heartbeat_handler.logger\n        logger(logging.INFO, 'started')\n\n        if partition_wait_time:\n            graceful_stop.wait(partition_wait_time)\n            _, _, logger = heartbeat_handler.live(force_renew=True)\n\n        activity_next_exe_time = PriorityQueue()\n        for activity in activities or [None]:\n            activity_next_exe_time[activity] = time.time()\n\n        while not graceful_stop.is_set() and activity_next_exe_time:\n            if once:\n                activity = activity_next_exe_time.pop()\n                time_to_sleep = 0\n            else:\n                activity = activity_next_exe_time.top()\n                time_to_sleep = activity_next_exe_time[activity] - time.time()\n\n            if time_to_sleep > 0:\n                if activity:\n                    logger(logging.DEBUG, 'Switching to activity %s and sleeping %s seconds', activity, time_to_sleep)\n                else:\n                    logger(logging.DEBUG, 'Sleeping %s seconds', time_to_sleep)\n                graceful_stop.wait(time_to_sleep)\n            else:\n                if activity:\n                    logger(logging.DEBUG, 'Switching to activity %s', activity)\n                else:\n                    logger(logging.DEBUG, 'Starting next iteration')\n\n            _, _, logger = heartbeat_handler.live()\n\n            must_sleep = True\n            start_time = time.time()\n            try:\n                must_sleep = run_once_fnc(activity=activity, heartbeat_handler=heartbeat_handler)\n                if must_sleep is None:\n                    # The run_once_fnc doesn't explicitly return whether we must sleep,\n                    # so sleep by default\n                    must_sleep = True\n            except Exception:\n                logger(logging.CRITICAL, \"Exception\", exc_info=True)\n                if once:\n                    raise\n\n            if not once:\n                if must_sleep:\n                    time_diff = time.time() - start_time\n                    time_to_sleep = max(1, sleep_time - time_diff)\n                    activity_next_exe_time[activity] = time.time() + time_to_sleep\n                else:\n                    activity_next_exe_time[activity] = time.time() + 1\n\n        if not once:\n            logger(logging.INFO, 'Graceful stop requested')\n", "path": "lib/rucio/daemons/common.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n# Copyright European Organization for Nuclear Research (CERN) since 2012\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport datetime\nimport hashlib\nimport logging\nimport os\nimport socket\nimport threading\nimport time\n\nfrom rucio.common.logging import formatted_logger\nfrom rucio.common.utils import PriorityQueue\nfrom rucio.core import heartbeat\n\n\nclass HeartbeatHandler:\n    \"\"\"\n    Simple contextmanager which sets a heartbeat and associated logger on entry and cleans up the heartbeat on exit.\n    \"\"\"\n\n    def __init__(self, executable, renewal_interval, logger_prefix=None):\n        \"\"\"\n        :param executable: the executable name which will be set in heartbeats\n        :param renewal_interval: the interval at which the heartbeat will be renewed in the database.\n        Calls to live() in-between intervals will re-use the locally cached heartbeat.\n        :param logger_prefix: the prefix to be prepended to all log messages\n        \"\"\"\n        self.executable = executable\n        self.renewal_interval = renewal_interval\n        self.older_than = renewal_interval * 10 if renewal_interval and renewal_interval > 0 else None  # 10 was chosen without any particular reason\n        self.logger_prefix = logger_prefix or executable\n\n        self.hostname = socket.getfqdn()\n        self.pid = os.getpid()\n        self.hb_thread = threading.current_thread()\n        self.logger_id = hashlib.sha1(f'{self.hostname}:{self.pid}:{self.hb_thread}'.encode('utf-8')).hexdigest()[:7]\n\n        self.logger = None\n        self.last_heart_beat = None\n        self.last_time = None\n        self.last_payload = None\n\n    def __enter__(self):\n        heartbeat.sanity_check(executable=self.executable, hostname=self.hostname)\n        self.live()\n        return self\n\n    def __exit__(self, exc_type, exc_val, exc_tb):\n        if self.last_heart_beat:\n            heartbeat.die(self.executable, self.hostname, self.pid, self.hb_thread)\n            if self.logger:\n                self.logger(logging.INFO, 'Heartbeat cleaned up')\n\n    def live(self, force_renew=False, payload=None):\n        \"\"\"\n        :return: a tuple: <the number of the current worker>, <total number of workers>, <decorated logger>\n        \"\"\"\n        if force_renew \\\n                or not self.last_time \\\n                or self.last_time < datetime.datetime.now() - datetime.timedelta(seconds=self.renewal_interval) \\\n                or self.last_payload != payload:\n            if self.older_than:\n                self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload, older_than=self.older_than)\n            else:\n                self.last_heart_beat = heartbeat.live(self.executable, self.hostname, self.pid, self.hb_thread, payload=payload)\n\n            prefix = '%s[%s:%i/%i]: ' % (self.logger_prefix, self.logger_id, self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'])\n            self.logger = formatted_logger(logging.log, prefix + '%s')\n\n            if not self.last_time:\n                self.logger(logging.DEBUG, 'First heartbeat set')\n            else:\n                self.logger(logging.DEBUG, 'Heartbeat renewed')\n            self.last_time = datetime.datetime.now()\n            self.last_payload = payload\n\n        return self.last_heart_beat['assign_thread'], self.last_heart_beat['nr_threads'], self.logger\n\n\ndef run_daemon(once, graceful_stop, executable, logger_prefix, partition_wait_time, sleep_time, run_once_fnc, activities=None):\n    \"\"\"\n    Run the daemon loop and call the function run_once_fnc at each iteration\n    :param once: Whether to stop after one iteration\n    :param graceful_stop: the threading.Event() object used for graceful stop of the daemon\n    :param executable: the name of the executable used for hearbeats\n    :param logger_prefix: the prefix to be prepended to all log messages\n    :param partition_wait_time: time to wait for database partition rebalancing before starting the actual daemon loop\n    :param sleep_time: time to sleep between the iterations of the daemon\n    :param run_once_fnc: the function which will do the actual work\n    :param activities: optional list of activities on which to work. The run_once_fnc will be called on activities one by one.\n    \"\"\"\n\n    with HeartbeatHandler(executable=executable, renewal_interval=sleep_time - 1, logger_prefix=logger_prefix) as heartbeat_handler:\n        logger = heartbeat_handler.logger\n        logger(logging.INFO, 'started')\n\n        if partition_wait_time:\n            graceful_stop.wait(partition_wait_time)\n            _, _, logger = heartbeat_handler.live(force_renew=True)\n\n        activity_next_exe_time = PriorityQueue()\n        for activity in activities or [None]:\n            activity_next_exe_time[activity] = time.time()\n\n        while not graceful_stop.is_set() and activity_next_exe_time:\n            if once:\n                activity = activity_next_exe_time.pop()\n                time_to_sleep = 0\n            else:\n                activity = activity_next_exe_time.top()\n                time_to_sleep = activity_next_exe_time[activity] - time.time()\n\n            if time_to_sleep > 0:\n                if activity:\n                    logger(logging.DEBUG, 'Switching to activity %s and sleeping %s seconds', activity, time_to_sleep)\n                else:\n                    logger(logging.DEBUG, 'Sleeping %s seconds', time_to_sleep)\n                graceful_stop.wait(time_to_sleep)\n            else:\n                if activity:\n                    logger(logging.DEBUG, 'Switching to activity %s', activity)\n                else:\n                    logger(logging.DEBUG, 'Starting next iteration')\n\n            _, _, logger = heartbeat_handler.live()\n\n            must_sleep = True\n            start_time = time.time()\n            try:\n                must_sleep = run_once_fnc(activity=activity, heartbeat_handler=heartbeat_handler)\n                if must_sleep is None:\n                    # The run_once_fnc doesn't explicitly return whether we must sleep,\n                    # so sleep by default\n                    must_sleep = True\n            except Exception:\n                logger(logging.CRITICAL, \"Exception\", exc_info=True)\n                if once:\n                    raise\n\n            if not once:\n                if must_sleep:\n                    time_diff = time.time() - start_time\n                    time_to_sleep = max(1, sleep_time - time_diff)\n                    activity_next_exe_time[activity] = time.time() + time_to_sleep\n                else:\n                    activity_next_exe_time[activity] = time.time() + 1\n\n        if not once:\n            logger(logging.INFO, 'Graceful stop requested')\n", "path": "lib/rucio/daemons/common.py"}]}