Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion humux/api/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,16 @@ def __init__(self, agent: AgentCore | None = None, status: str = "STOPPED"):
# Severity levels offered in the Logs tab filter, low → high.
_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR"]

# Timezone for log timestamps (updated after config loads, defaults to UTC).
_LOG_TIMEZONE: str = "UTC"


def set_log_timezone(tz_name: str | None) -> None:
"""Update the log timezone (called after config loads, #289)."""
global _LOG_TIMEZONE
if tz_name:
_LOG_TIMEZONE = tz_name


def _should_capture_log_record(record: logging.LogRecord) -> bool:
"""Return True when a record should be visible in the admin log viewer."""
Expand Down Expand Up @@ -453,7 +463,9 @@ def emit(self, record: logging.LogRecord) -> None:
_LOG_BUFFER.append(
{
"ts": record.created,
"time": datetime.fromtimestamp(record.created).strftime("%H:%M:%S"),
"time": datetime.fromtimestamp(
record.created, tz=ZoneInfo(_LOG_TIMEZONE)
).strftime("%H:%M:%S"),
"level": record.levelname,
"levelno": record.levelno,
"name": record.name,
Expand Down
5 changes: 4 additions & 1 deletion humux/core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from fastapi import Depends, Request
from fastapi.responses import HTMLResponse

from api.admin import AgentState, create_admin_app, install_log_buffer
from api.admin import AgentState, create_admin_app, install_log_buffer, set_log_timezone
from core.config_store import ConfigStore
from core.email_config import materialize_himalaya_config
from core.secret_store import SecretStore
Expand Down Expand Up @@ -54,6 +54,9 @@ async def _start_agent(config_store: ConfigStore):
await _secret_store.load_infra_cache()
config = await config_store.export_to_config(vault_resolve=_secret_store.infra_resolve)

# Apply the configured timezone to log timestamps (#289)
set_log_timezone(config.agent.timezone)

agent = AgentCore(config, secret_store=_secret_store)

# Ensure scheduler jobs can resolve the current agent instance
Expand Down
45 changes: 45 additions & 0 deletions humux/tests/test_log_streams.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,48 @@ def test_bad_regex_is_noop_not_crash() -> None:
def test_stream_hue_is_stable() -> None:
assert _stream_hue("default") == _stream_hue("default")
assert 0 <= _stream_hue("coding-helper") < 360


def test_log_timestamp_defaults_to_utc() -> None:
"""Without a timezone configured, timestamps are formatted in UTC."""
import api.admin as admin

saved = admin._LOG_TIMEZONE
admin._LOG_TIMEZONE = "UTC"
try:
handler = _BufferHandler()
rec = logging.LogRecord("core.agent", logging.INFO, __file__, 1, "hi", None, None)
rec.created = 1700006400.0 # 2023-11-15 00:00:00 UTC
captured: list[dict] = []
saved_buf = admin._LOG_BUFFER
admin._LOG_BUFFER = captured
try:
handler.emit(rec)
finally:
admin._LOG_BUFFER = saved_buf
assert captured[0]["time"] == "00:00:00"
finally:
admin._LOG_TIMEZONE = saved


def test_log_timestamp_respects_configured_timezone() -> None:
"""Log timestamps are formatted in the configured timezone."""
import api.admin as admin

saved = admin._LOG_TIMEZONE
admin._LOG_TIMEZONE = "US/Eastern"
try:
handler = _BufferHandler()
rec = logging.LogRecord("core.agent", logging.INFO, __file__, 1, "hi", None, None)
rec.created = 1700006400.0 # 2023-11-15 00:00:00 UTC
captured: list[dict] = []
saved_buf = admin._LOG_BUFFER
admin._LOG_BUFFER = captured
try:
handler.emit(rec)
finally:
admin._LOG_BUFFER = saved_buf
# US/Eastern in Nov is EST (UTC-5): 2023-11-14 19:00:00
assert captured[0]["time"] == "19:00:00"
finally:
admin._LOG_TIMEZONE = saved
Loading