Skip to content

Commit 97d32b6

Browse files
authored
Merge pull request #922 from inclusionAI/fix/gateway-log-growth-no-script
Fix gateway launchd log growth
2 parents 98ad653 + de97c1b commit 97d32b6

5 files changed

Lines changed: 203 additions & 73 deletions

File tree

aworld-cli/src/aworld_cli/gateway_cli.py

Lines changed: 89 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
from aworld_gateway.config import GatewayConfig
2121
from aworld_gateway.http.artifact_service import ArtifactService
2222
from aworld_gateway.http.server import create_gateway_app
23-
from aworld_gateway.logging import configure_gateway_logging, get_gateway_logger
23+
from aworld_gateway.logging import (
24+
GATEWAY_CONSOLE_LOG_ENV,
25+
configure_gateway_logging,
26+
get_gateway_logger,
27+
)
2428
from aworld_gateway.registry import ChannelRegistry
2529
from aworld_gateway.router import GatewayRouter, LocalCliAgentBackend
2630
from aworld_gateway.runtime import GatewayRuntime
@@ -190,18 +194,27 @@ def _build_artifact_service(
190194
)
191195

192196

193-
def _enable_aworld_console_logging_for_gateway() -> None:
194-
os.environ["AWORLD_DISABLE_CONSOLE_LOG"] = "false"
197+
def _suppress_gateway_console_logging() -> None:
198+
os.environ["AWORLD_DISABLE_CONSOLE_LOG"] = "true"
199+
os.environ[GATEWAY_CONSOLE_LOG_ENV] = "false"
195200

196201
try:
197202
from aworld.logs import util as log_util
198203
except Exception:
199204
return
200205

201206
aworld_logger = getattr(log_util, "logger", None)
202-
if aworld_logger is None or not getattr(aworld_logger, "disable_console", False):
207+
if aworld_logger is None or getattr(aworld_logger, "disable_console", False):
203208
return
204209

210+
log_id = getattr(aworld_logger, "log_id", None)
211+
bound_logger = getattr(aworld_logger, "_logger", None)
212+
if log_id is not None and bound_logger is not None:
213+
try:
214+
bound_logger.remove(log_id)
215+
except Exception:
216+
pass
217+
205218
file_log_config = getattr(aworld_logger, "file_log_config", None)
206219
if isinstance(file_log_config, dict):
207220
file_log_config = dict(file_log_config)
@@ -211,11 +224,18 @@ def _enable_aworld_console_logging_for_gateway() -> None:
211224
name=getattr(aworld_logger, "name", "AWorld"),
212225
console_level=getattr(aworld_logger, "console_level", "INFO"),
213226
formatter=getattr(aworld_logger, "formater", None),
214-
disable_console=False,
227+
disable_console=True,
215228
file_log_config=file_log_config,
216229
)
217230

218231

232+
def _restore_env_var(name: str, previous_value: str | None) -> None:
233+
if previous_value is None:
234+
os.environ.pop(name, None)
235+
else:
236+
os.environ[name] = previous_value
237+
238+
219239
def _configure_gateway_file_logging(*, base_dir: Path) -> Path:
220240
log_path = (base_dir / "logs" / "gateway.log").resolve()
221241
os.environ["AWORLD_GATEWAY_LOG_PATH"] = str(log_path)
@@ -261,59 +281,69 @@ async def serve_gateway(
261281
agent_files: list[str] | None,
262282
) -> None:
263283
resolved_base_dir = Path.cwd() if base_dir is None else Path(base_dir)
264-
gateway_log_path = _configure_gateway_file_logging(base_dir=resolved_base_dir)
265-
gateway_logger = get_gateway_logger("cli")
266-
_enable_aworld_console_logging_for_gateway()
267-
enable_quiet_gateway_boot()
268-
gateway_logger.info(
269-
"Gateway server boot starting "
270-
f"base_dir={resolved_base_dir.resolve()} log_path={gateway_log_path}"
271-
)
272-
273-
from aworld_cli.main import load_all_agents
274-
275-
await load_all_agents(
276-
remote_backends=remote_backends,
277-
local_dirs=local_dirs,
278-
agent_files=agent_files,
279-
)
280-
281-
config = GatewayConfigLoader(base_dir=resolved_base_dir).load_or_init()
282-
artifact_service = _build_artifact_service(base_dir=resolved_base_dir, config=config)
283-
router = GatewayRouter(
284-
session_binding=SessionBinding(),
285-
agent_resolver=AgentResolver(default_agent_id=config.default_agent_id),
286-
agent_backend=LocalCliAgentBackend(),
287-
)
288-
runtime = GatewayRuntime(
289-
config=config,
290-
registry=ChannelRegistry(),
291-
router=router,
292-
artifact_service=artifact_service,
293-
)
294-
295-
await runtime.start()
296-
gateway_logger.info(
297-
"Gateway runtime started "
298-
f"host={config.gateway.host} port={config.gateway.port}"
299-
)
300-
telegram_adapter = runtime.get_started_channel("telegram")
301-
app = create_gateway_app(
302-
runtime_status=runtime.status(),
303-
artifact_service=artifact_service,
304-
telegram_adapter=telegram_adapter,
305-
telegram_webhook_path=config.channels.telegram.webhook_path,
306-
)
307-
uvicorn_config = uvicorn.Config(
308-
app=app,
309-
host=config.gateway.host,
310-
port=config.gateway.port,
311-
)
312-
server = uvicorn.Server(uvicorn_config)
313-
284+
previous_disable_console_log = os.environ.get("AWORLD_DISABLE_CONSOLE_LOG")
285+
previous_gateway_console_log = os.environ.get(GATEWAY_CONSOLE_LOG_ENV)
286+
gateway_log_path: Path | None = None
287+
_suppress_gateway_console_logging()
314288
try:
315-
await server.serve()
289+
gateway_log_path = _configure_gateway_file_logging(base_dir=resolved_base_dir)
290+
gateway_logger = get_gateway_logger("cli")
291+
enable_quiet_gateway_boot()
292+
gateway_logger.info(
293+
"Gateway server boot starting "
294+
f"base_dir={resolved_base_dir.resolve()} log_path={gateway_log_path}"
295+
)
296+
297+
from aworld_cli.main import load_all_agents
298+
299+
await load_all_agents(
300+
remote_backends=remote_backends,
301+
local_dirs=local_dirs,
302+
agent_files=agent_files,
303+
)
304+
305+
config = GatewayConfigLoader(base_dir=resolved_base_dir).load_or_init()
306+
artifact_service = _build_artifact_service(base_dir=resolved_base_dir, config=config)
307+
router = GatewayRouter(
308+
session_binding=SessionBinding(),
309+
agent_resolver=AgentResolver(default_agent_id=config.default_agent_id),
310+
agent_backend=LocalCliAgentBackend(),
311+
)
312+
runtime = GatewayRuntime(
313+
config=config,
314+
registry=ChannelRegistry(),
315+
router=router,
316+
artifact_service=artifact_service,
317+
)
318+
319+
await runtime.start()
320+
gateway_logger.info(
321+
"Gateway runtime started "
322+
f"host={config.gateway.host} port={config.gateway.port}"
323+
)
324+
telegram_adapter = runtime.get_started_channel("telegram")
325+
app = create_gateway_app(
326+
runtime_status=runtime.status(),
327+
artifact_service=artifact_service,
328+
telegram_adapter=telegram_adapter,
329+
telegram_webhook_path=config.channels.telegram.webhook_path,
330+
)
331+
uvicorn_config = uvicorn.Config(
332+
app=app,
333+
host=config.gateway.host,
334+
port=config.gateway.port,
335+
log_level=str(os.getenv("AWORLD_GATEWAY_UVICORN_LOG_LEVEL") or "warning"),
336+
)
337+
server = uvicorn.Server(uvicorn_config)
338+
339+
try:
340+
await server.serve()
341+
finally:
342+
gateway_logger.info("Gateway runtime stopping")
343+
await runtime.stop()
344+
gateway_logger.info("Gateway runtime stopped")
316345
finally:
317-
gateway_logger.info("Gateway runtime stopping")
318-
await runtime.stop()
319-
gateway_logger.info("Gateway runtime stopped")
346+
_restore_env_var("AWORLD_DISABLE_CONSOLE_LOG", previous_disable_console_log)
347+
_restore_env_var(GATEWAY_CONSOLE_LOG_ENV, previous_gateway_console_log)
348+
if gateway_log_path is not None:
349+
configure_gateway_logging(log_path=gateway_log_path)

aworld_gateway/channels/wechat/connector.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ async def _default_post_json(
127127
timeout_ms: int,
128128
) -> dict[str, object]:
129129
request_summary = _summarize_wechat_request_payload(payload)
130-
logger.info(
130+
log_success = logger.debug if endpoint == EP_GET_UPDATES else logger.info
131+
log_success(
131132
"WeChat API request "
132133
f"endpoint={endpoint} timeout_ms={timeout_ms}"
133134
f"{f' {request_summary}' if request_summary else ''}"
@@ -149,7 +150,7 @@ async def _default_post_json(
149150
)
150151
raise RuntimeError(f"iLink POST {endpoint} HTTP {response.status}: {raw[:200]}")
151152
data = json.loads(raw)
152-
logger.info(
153+
log_success(
153154
"WeChat API response "
154155
f"endpoint={endpoint} http_status={getattr(response, 'status', 'unknown')} "
155156
f"ret={data.get('ret', 'missing')} "

aworld_gateway/logging.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@
66
from pathlib import Path
77

88
GATEWAY_LOGGER_NAME = "aworld.gateway"
9+
GATEWAY_CONSOLE_LOG_ENV = "AWORLD_GATEWAY_CONSOLE_LOG"
910
_GATEWAY_HANDLER_MARKER = "_aworld_gateway_file_handler"
11+
_FALSEY_VALUES = {"0", "false", "no", "off"}
12+
13+
14+
def _is_gateway_console_log_enabled() -> bool:
15+
configured = str(os.getenv(GATEWAY_CONSOLE_LOG_ENV) or "").strip().lower()
16+
if configured in _FALSEY_VALUES:
17+
return False
18+
return True
1019

1120

1221
def resolve_gateway_log_path(log_path: Path | str | None = None) -> Path:
@@ -30,7 +39,7 @@ def configure_gateway_logging(*, log_path: Path | str | None = None) -> Path:
3039

3140
gateway_root_logger = logging.getLogger(GATEWAY_LOGGER_NAME)
3241
gateway_root_logger.setLevel(logging.INFO)
33-
gateway_root_logger.propagate = True
42+
gateway_root_logger.propagate = _is_gateway_console_log_enabled()
3443

3544
for handler in list(gateway_root_logger.handlers):
3645
if not getattr(handler, _GATEWAY_HANDLER_MARKER, False):

tests/gateway/test_gateway_status_command.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import asyncio
44
import importlib
5+
import logging
56
import os
67
import sys
78
from types import ModuleType
@@ -51,7 +52,7 @@ def test_gateway_channels_list_contains_placeholder_channels(
5152
assert rows["web"]["implemented"] is False
5253

5354

54-
def test_enable_aworld_console_logging_for_gateway_reconfigures_disabled_logger(
55+
def test_suppress_gateway_console_logging_reconfigures_enabled_logger(
5556
monkeypatch: pytest.MonkeyPatch,
5657
) -> None:
5758
class FakeLogger:
@@ -83,15 +84,31 @@ def __init__(
8384

8485
import aworld.logs.util as log_util
8586

86-
fake_logger = FakeLogger(disable_console=True)
87+
fake_logger = FakeLogger(disable_console=False)
8788
monkeypatch.setattr(log_util, "logger", fake_logger)
88-
monkeypatch.setenv("AWORLD_DISABLE_CONSOLE_LOG", "true")
89+
monkeypatch.setenv("AWORLD_DISABLE_CONSOLE_LOG", "false")
90+
monkeypatch.delenv("AWORLD_GATEWAY_CONSOLE_LOG", raising=False)
91+
92+
gateway_cli._suppress_gateway_console_logging()
93+
94+
assert os.environ["AWORLD_DISABLE_CONSOLE_LOG"] == "true"
95+
assert os.environ["AWORLD_GATEWAY_CONSOLE_LOG"] == "false"
96+
assert fake_logger.disable_console is True
97+
assert fake_logger.calls[-1]["disable_console"] is True
98+
99+
100+
def test_configure_gateway_file_logging_can_suppress_console_propagation(
101+
monkeypatch: pytest.MonkeyPatch,
102+
tmp_path: Path,
103+
) -> None:
104+
monkeypatch.setenv("AWORLD_GATEWAY_CONSOLE_LOG", "false")
105+
106+
gateway_cli._configure_gateway_file_logging(base_dir=tmp_path)
89107

90-
gateway_cli._enable_aworld_console_logging_for_gateway()
108+
assert logging.getLogger("aworld.gateway").propagate is False
91109

92-
assert os.environ["AWORLD_DISABLE_CONSOLE_LOG"] == "false"
93-
assert fake_logger.disable_console is False
94-
assert fake_logger.calls[-1]["disable_console"] is False
110+
monkeypatch.setenv("AWORLD_GATEWAY_CONSOLE_LOG", "true")
111+
gateway_cli._configure_gateway_file_logging(base_dir=tmp_path)
95112

96113

97114
def test_configure_gateway_file_logging_writes_to_dedicated_gateway_log(
@@ -136,7 +153,7 @@ def test_get_gateway_logger_returns_named_child_logger(
136153
assert logger.name == f"{GATEWAY_LOGGER_NAME}.child_component"
137154

138155

139-
def test_serve_gateway_enables_console_logging_before_loading_agents(
156+
def test_serve_gateway_suppresses_console_logging_before_loading_agents(
140157
monkeypatch: pytest.MonkeyPatch,
141158
tmp_path: Path,
142159
) -> None:
@@ -145,6 +162,7 @@ def test_serve_gateway_enables_console_logging_before_loading_agents(
145162

146163
async def fake_load_all_agents(*, remote_backends, local_dirs, agent_files):
147164
observed["disable_console_env"] = os.environ.get("AWORLD_DISABLE_CONSOLE_LOG", "")
165+
observed["gateway_console_env"] = os.environ.get("AWORLD_GATEWAY_CONSOLE_LOG", "")
148166
observed["quiet_boot_env"] = os.environ.get("AWORLD_GATEWAY_QUIET_BOOT", "")
149167
raise RuntimeError("stop after env check")
150168

@@ -163,7 +181,8 @@ async def fake_load_all_agents(*, remote_backends, local_dirs, agent_files):
163181
)
164182
)
165183

166-
assert observed["disable_console_env"] == "false"
184+
assert observed["disable_console_env"] == "true"
185+
assert observed["gateway_console_env"] == "false"
167186
assert observed["quiet_boot_env"] == "true"
168187

169188

@@ -311,11 +330,12 @@ def get_started_channel(self, channel_name: str):
311330
return telegram_adapter
312331

313332
class FakeUvicornConfig:
314-
def __init__(self, *, app, host, port):
333+
def __init__(self, *, app, host, port, log_level):
315334
calls["uvicorn_config"] = {
316335
"app": app,
317336
"host": host,
318337
"port": port,
338+
"log_level": log_level,
319339
}
320340

321341
class FakeUvicornServer:
@@ -434,8 +454,9 @@ def get_started_channel(self, channel_name: str):
434454
return None
435455

436456
class FakeUvicornConfig:
437-
def __init__(self, *, app, host, port):
457+
def __init__(self, *, app, host, port, log_level):
438458
calls["uvicorn_app"] = app
459+
calls["uvicorn_log_level"] = log_level
439460

440461
class FakeUvicornServer:
441462
def __init__(self, config):

0 commit comments

Comments
 (0)