debug: add logging at key hot_reload decision points

- FileWatcher.check: log initial mtime and file changes
- _check_signal_file: log signal file detection
- check_and_reload: log which files triggered reload
- _reload_config/_reload_i18n: log singleton clearing
- hot_reload_task: log dispatch vs skip decision
- hot_reload_handler: log HTTP endpoint trigger
This commit is contained in:
yumoqing 2026-06-01 22:53:04 +08:00
parent b51d3f28d6
commit 40bd5736b1

View File

@ -65,9 +65,11 @@ class FileWatcher:
old_mtime = self._mtimes.get(path) old_mtime = self._mtimes.get(path)
if old_mtime is None: if old_mtime is None:
self._mtimes[path] = mtime self._mtimes[path] = mtime
debug(f'[hot_reload] initial mtime for {path}: {mtime}')
elif mtime > old_mtime: elif mtime > old_mtime:
self._mtimes[path] = mtime self._mtimes[path] = mtime
changed.append(path) changed.append(path)
debug(f'[hot_reload] changed: {path} (mtime {old_mtime} -> {mtime})')
return changed return changed
@ -108,11 +110,13 @@ class HotReloader:
"""Check if cache invalidation signal file was updated.""" """Check if cache invalidation signal file was updated."""
try: try:
mtime = os.path.getmtime(SIGNAL_FILE) mtime = os.path.getmtime(SIGNAL_FILE)
debug(f'[hot_reload] signal file mtime: {mtime}, last: {self._last_signal_mtime}')
if mtime > self._last_signal_mtime: if mtime > self._last_signal_mtime:
self._last_signal_mtime = mtime self._last_signal_mtime = mtime
debug('[hot_reload] signal file changed, triggering reload')
return True return True
except OSError: except OSError:
pass debug(f'[hot_reload] signal file not found: {SIGNAL_FILE}')
return False return False
def check_and_reload(self): def check_and_reload(self):
@ -130,6 +134,7 @@ class HotReloader:
# Check config.json # Check config.json
config_changed = self._watcher.check([self._config_path]) config_changed = self._watcher.check([self._config_path])
if config_changed: if config_changed:
debug(f'[hot_reload] config changed: {config_changed}')
self._reload_config() self._reload_config()
reloaded['config'] = True reloaded['config'] = True
@ -137,6 +142,7 @@ class HotReloader:
if self._i18n_paths: if self._i18n_paths:
i18n_changed = self._watcher.check(self._i18n_paths) i18n_changed = self._watcher.check(self._i18n_paths)
if i18n_changed: if i18n_changed:
debug(f'[hot_reload] i18n changed: {i18n_changed}')
self._reload_i18n() self._reload_i18n()
reloaded['i18n'] = True reloaded['i18n'] = True
@ -144,12 +150,16 @@ class HotReloader:
if self._check_signal_file(): if self._check_signal_file():
reloaded['signal'] = True reloaded['signal'] = True
if reloaded:
debug(f'[hot_reload] check_and_reload result: {reloaded}')
return reloaded return reloaded
def _reload_config(self): def _reload_config(self):
"""Clear JsonConfig singleton so next getConfig() call reloads from disk.""" """Clear JsonConfig singleton so next getConfig() call reloads from disk."""
try: try:
from appPublic.jsonConfig import JsonConfig from appPublic.jsonConfig import JsonConfig
debug('[hot_reload] clearing JsonConfig singleton')
# SingletonDecorator stores instance as .instance # SingletonDecorator stores instance as .instance
JsonConfig.instance = None JsonConfig.instance = None
info('[hot_reload] config.json changed, cache cleared') info('[hot_reload] config.json changed, cache cleared')
@ -160,6 +170,7 @@ class HotReloader:
"""Clear MiniI18N singleton and ServerEnv cache.""" """Clear MiniI18N singleton and ServerEnv cache."""
try: try:
from appPublic.i18n import MiniI18N from appPublic.i18n import MiniI18N
debug('[hot_reload] clearing MiniI18N singleton')
MiniI18N.instance = None MiniI18N.instance = None
# Clear cached i18n on ServerEnv # Clear cached i18n on ServerEnv
try: try:
@ -167,6 +178,7 @@ class HotReloader:
g = ServerEnv() g = ServerEnv()
if hasattr(g, 'myi18n'): if hasattr(g, 'myi18n'):
del g.myi18n del g.myi18n
debug('[hot_reload] cleared ServerEnv.myi18n')
except Exception: except Exception:
pass pass
info('[hot_reload] i18n files changed, cache cleared') info('[hot_reload] i18n files changed, cache cleared')
@ -197,6 +209,8 @@ async def hot_reload_task(app, reloader):
from .serverenv import ServerEnv from .serverenv import ServerEnv
dispatcher = ServerEnv().event_dispatcher dispatcher = ServerEnv().event_dispatcher
info(f'[hot_reload] started, interval={reloader._interval}s') info(f'[hot_reload] started, interval={reloader._interval}s')
debug(f'[hot_reload] config_path={reloader._config_path}')
debug(f'[hot_reload] watching {len(reloader._i18n_paths)} i18n paths')
try: try:
while True: while True:
await asyncio.sleep(reloader._interval) await asyncio.sleep(reloader._interval)
@ -207,7 +221,10 @@ async def hot_reload_task(app, reloader):
# Config-only reload just refreshes JsonConfig singleton, no cache clearing needed # Config-only reload just refreshes JsonConfig singleton, no cache clearing needed
needs_cache_clear = any(k != 'config' for k in reloaded) needs_cache_clear = any(k != 'config' for k in reloaded)
if needs_cache_clear: if needs_cache_clear:
debug(f'[hot_reload] dispatching hot_reload event (non-config changes detected)')
await dispatcher.dispatch('hot_reload', reloaded) await dispatcher.dispatch('hot_reload', reloaded)
else:
debug(f'[hot_reload] config-only change, skipping cache clear dispatch')
except asyncio.CancelledError: except asyncio.CancelledError:
info('[hot_reload] stopped') info('[hot_reload] stopped')
raise raise
@ -224,12 +241,14 @@ async def hot_reload_handler(request):
from aiohttp import web from aiohttp import web
from .serverenv import ServerEnv from .serverenv import ServerEnv
debug(f'[hot_reload] HTTP endpoint triggered, writing signal to {SIGNAL_FILE}')
# Write signal file - other workers will detect this # Write signal file - other workers will detect this
with open(SIGNAL_FILE, 'w') as f: with open(SIGNAL_FILE, 'w') as f:
f.write(str(time.time())) f.write(str(time.time()))
# Dispatch immediately for current worker # Dispatch immediately for current worker
dispatcher = ServerEnv().event_dispatcher dispatcher = ServerEnv().event_dispatcher
debug('[hot_reload] HTTP endpoint: dispatching hot_reload event')
await dispatcher.dispatch('hot_reload', {'source': 'http_endpoint'}) await dispatcher.dispatch('hot_reload', {'source': 'http_endpoint'})
return web.json_response({ return web.json_response({