Skip to content

Commit 1ac69a9

Browse files
vadakattuxuanyang15
authored andcommitted
feat: Add log_level option for adk run CLI
Merge google#3674 Co-authored-by: Xuan Yang <xygoogle@google.com> PiperOrigin-RevId: 933452646
1 parent f022307 commit 1ac69a9

3 files changed

Lines changed: 111 additions & 24 deletions

File tree

src/google/adk/cli/cli_tools_click.py

Lines changed: 39 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,41 @@
4747
)
4848

4949

50+
def _logging_options():
51+
"""Decorator to add logging options to click commands."""
52+
53+
def decorator(func):
54+
@click.option(
55+
"-v",
56+
"--verbose",
57+
is_flag=True,
58+
show_default=True,
59+
default=False,
60+
help="Enable verbose (DEBUG) logging. Shortcut for --log_level DEBUG.",
61+
)
62+
@click.option(
63+
"--log_level",
64+
type=LOG_LEVELS,
65+
default="INFO",
66+
help="Optional. Set the logging level",
67+
)
68+
@functools.wraps(func)
69+
@click.pass_context
70+
def wrapper(ctx, *args, **kwargs):
71+
# If verbose flag is set and log level is not set, set log level to DEBUG.
72+
log_level_source = ctx.get_parameter_source("log_level")
73+
if (
74+
kwargs.pop("verbose", False)
75+
and log_level_source == ParameterSource.DEFAULT
76+
):
77+
kwargs["log_level"] = "DEBUG"
78+
return func(*args, **kwargs)
79+
80+
return wrapper
81+
82+
return decorator
83+
84+
5085
def _apply_feature_overrides(
5186
*,
5287
enable_features: tuple[str, ...] = (),
@@ -606,6 +641,7 @@ def wrapper(*args, **kwargs):
606641
@main.command("run", cls=HelpfulCommand)
607642
@feature_options()
608643
@adk_services_options(default_use_local_storage=True)
644+
@_logging_options()
609645
@click.option(
610646
"--save_session",
611647
type=bool,
@@ -700,6 +736,7 @@ def cli_run(
700736
memory_service_uri: Optional[str] = None,
701737
use_local_storage: bool = True,
702738
default_llm_model: Optional[str] = None,
739+
log_level: str = "INFO",
703740
):
704741
"""Runs an agent. If no query is provided, enters interactive mode.
705742
@@ -711,7 +748,7 @@ def cli_run(
711748
adk run path/to/my_agent
712749
adk run path/to/my_agent "hello"
713750
"""
714-
logs.log_to_tmp_folder()
751+
logs.log_to_tmp_folder(level=getattr(logging, log_level.upper()))
715752

716753
agent_parent_folder = os.path.dirname(agent)
717754
agent_folder_name = os.path.basename(agent)
@@ -1588,6 +1625,7 @@ def fast_api_common_options():
15881625
"""Decorator to add common fast api options to click commands."""
15891626

15901627
def decorator(func):
1628+
func = _logging_options()(func)
15911629

15921630
@click.option(
15931631
"--host",
@@ -1611,20 +1649,6 @@ def decorator(func):
16111649
),
16121650
multiple=True,
16131651
)
1614-
@click.option(
1615-
"-v",
1616-
"--verbose",
1617-
is_flag=True,
1618-
show_default=True,
1619-
default=False,
1620-
help="Enable verbose (DEBUG) logging. Shortcut for --log_level DEBUG.",
1621-
)
1622-
@click.option(
1623-
"--log_level",
1624-
type=LOG_LEVELS,
1625-
default="INFO",
1626-
help="Optional. Set the logging level",
1627-
)
16281652
@click.option(
16291653
"--trace_to_cloud",
16301654
is_flag=True,
@@ -1707,14 +1731,6 @@ def decorator(func):
17071731
@functools.wraps(func)
17081732
@click.pass_context
17091733
def wrapper(ctx, *args, **kwargs):
1710-
# If verbose flag is set and log level is not set, set log level to DEBUG.
1711-
log_level_source = ctx.get_parameter_source("log_level")
1712-
if (
1713-
kwargs.pop("verbose", False)
1714-
and log_level_source == ParameterSource.DEFAULT
1715-
):
1716-
kwargs["log_level"] = "DEBUG"
1717-
17181734
# Parse comma-separated trigger_sources into a list.
17191735
trigger_sources = kwargs.get("trigger_sources")
17201736
if trigger_sources is not None:

tests/unittests/cli/test_cli_tools_click_option_mismatch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ def test_adk_run():
9898
run_command,
9999
cli_run.callback,
100100
"run",
101-
ignore_params={"enable_features", "disable_features"},
101+
ignore_params={"verbose", "enable_features", "disable_features"},
102102
)
103103

104104

tests/unittests/cli/utils/test_cli_tools_click.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import builtins
2020
import json
21+
import logging
2122
from pathlib import Path
2223
from types import SimpleNamespace
2324
from typing import Any
@@ -1538,3 +1539,73 @@ def _mock_to_cloud_run(*_a, **kwargs):
15381539
" command."
15391540
)
15401541
assert expected_msg in result.output
1542+
1543+
1544+
@pytest.mark.parametrize(
1545+
"cli_args,expected_log_level",
1546+
[
1547+
pytest.param(
1548+
[],
1549+
"INFO",
1550+
id="default_info",
1551+
),
1552+
pytest.param(
1553+
["--log_level", "DEBUG"],
1554+
"DEBUG",
1555+
id="explicit_debug",
1556+
),
1557+
pytest.param(
1558+
["--log_level", "WARNING"],
1559+
"WARNING",
1560+
id="explicit_warning",
1561+
),
1562+
pytest.param(
1563+
["-v"],
1564+
"DEBUG",
1565+
id="verbose_flag",
1566+
),
1567+
pytest.param(
1568+
["--verbose"],
1569+
"DEBUG",
1570+
id="verbose_long_flag",
1571+
),
1572+
pytest.param(
1573+
["-v", "--log_level", "WARNING"],
1574+
"WARNING",
1575+
id="both_verbose_and_explicit_warning",
1576+
),
1577+
],
1578+
)
1579+
def test_cli_run_log_level(
1580+
tmp_path: Path,
1581+
monkeypatch: pytest.MonkeyPatch,
1582+
cli_args: list[str],
1583+
expected_log_level: str,
1584+
) -> None:
1585+
"""`adk run` should configure log level correctly based on flags."""
1586+
agent_dir = tmp_path / "agent"
1587+
agent_dir.mkdir()
1588+
(agent_dir / "__init__.py").touch()
1589+
(agent_dir / "agent.py").touch()
1590+
1591+
# Mock logs.log_to_tmp_folder
1592+
mock_log_to_tmp_folder = mock.Mock()
1593+
monkeypatch.setattr(
1594+
cli_tools_click.logs, "log_to_tmp_folder", mock_log_to_tmp_folder
1595+
)
1596+
1597+
# Mock asyncio.run to do nothing, preventing full run
1598+
monkeypatch.setattr(cli_tools_click.asyncio, "run", mock.Mock())
1599+
1600+
runner = CliRunner()
1601+
result = runner.invoke(
1602+
cli_tools_click.main,
1603+
["run", *cli_args, str(agent_dir)],
1604+
)
1605+
assert result.exit_code == 0, (result.output, repr(result.exception))
1606+
1607+
# Check if log_to_tmp_folder was called with the correct log level object from `logging` module
1608+
expected_logging_level = getattr(logging, expected_log_level)
1609+
mock_log_to_tmp_folder.assert_called_once()
1610+
kwargs = mock_log_to_tmp_folder.call_args[1]
1611+
assert kwargs.get("level") == expected_logging_level

0 commit comments

Comments
 (0)