Skip to content

Commit f2c4805

Browse files
committed
Expose node logging settings in CLI profiles
1 parent 1eb587f commit f2c4805

3 files changed

Lines changed: 267 additions & 0 deletions

File tree

src/xian_cli/cli.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
resolve_solution_pack_path,
2323
)
2424
from xian_cli.models import (
25+
SUPPORTED_APP_LOG_LEVELS,
2526
SUPPORTED_BLOCK_POLICY_MODES,
2627
SUPPORTED_RUNTIME_BACKENDS,
2728
SUPPORTED_TRACER_MODES,
@@ -554,6 +555,43 @@ def _handle_network_create(args: argparse.Namespace) -> int:
554555
block_policy_mode=manifest.block_policy_mode,
555556
block_policy_interval=manifest.block_policy_interval,
556557
tracer_mode=manifest.tracer_mode,
558+
transaction_trace_logging=_pick_template_value(
559+
args.transaction_trace_logging,
560+
None
561+
if template is None
562+
else template.get("transaction_trace_logging"),
563+
False,
564+
),
565+
app_log_level=_pick_template_value(
566+
args.app_log_level,
567+
None if template is None else template.get("app_log_level"),
568+
"INFO",
569+
),
570+
app_log_json=_pick_template_value(
571+
args.app_log_json,
572+
None if template is None else template.get("app_log_json"),
573+
False,
574+
),
575+
app_log_rotation_hours=_validate_positive_int(
576+
"app_log_rotation_hours",
577+
_pick_template_value(
578+
args.app_log_rotation_hours,
579+
None
580+
if template is None
581+
else template.get("app_log_rotation_hours"),
582+
1,
583+
),
584+
),
585+
app_log_retention_days=_validate_positive_int(
586+
"app_log_retention_days",
587+
_pick_template_value(
588+
args.app_log_retention_days,
589+
None
590+
if template is None
591+
else template.get("app_log_retention_days"),
592+
7,
593+
),
594+
),
557595
simulation_enabled=_pick_template_value(
558596
args.simulation_enabled,
559597
None
@@ -813,6 +851,43 @@ def _handle_network_join(args: argparse.Namespace) -> int:
813851
None if template is None else template.get("tracer_mode"),
814852
network.get("tracer_mode", "python_line_v1"),
815853
),
854+
transaction_trace_logging=_pick_template_value(
855+
args.transaction_trace_logging,
856+
None
857+
if template is None
858+
else template.get("transaction_trace_logging"),
859+
False,
860+
),
861+
app_log_level=_pick_template_value(
862+
args.app_log_level,
863+
None if template is None else template.get("app_log_level"),
864+
"INFO",
865+
),
866+
app_log_json=_pick_template_value(
867+
args.app_log_json,
868+
None if template is None else template.get("app_log_json"),
869+
False,
870+
),
871+
app_log_rotation_hours=_validate_positive_int(
872+
"app_log_rotation_hours",
873+
_pick_template_value(
874+
args.app_log_rotation_hours,
875+
None
876+
if template is None
877+
else template.get("app_log_rotation_hours"),
878+
1,
879+
),
880+
),
881+
app_log_retention_days=_validate_positive_int(
882+
"app_log_retention_days",
883+
_pick_template_value(
884+
args.app_log_retention_days,
885+
None
886+
if template is None
887+
else template.get("app_log_retention_days"),
888+
7,
889+
),
890+
),
816891
simulation_enabled=_pick_template_value(
817892
args.simulation_enabled,
818893
None if template is None else template.get("simulation_enabled"),
@@ -1312,6 +1387,13 @@ def _initialize_node_from_args(args: argparse.Namespace) -> dict:
13121387
network.get("tracer_mode", "python_line_v1"),
13131388
)
13141389
),
1390+
transaction_trace_logging=bool(
1391+
profile.get("transaction_trace_logging", False)
1392+
),
1393+
app_log_level=str(profile.get("app_log_level", "INFO")),
1394+
app_log_json=bool(profile.get("app_log_json", False)),
1395+
app_log_rotation_hours=int(profile.get("app_log_rotation_hours", 1)),
1396+
app_log_retention_days=int(profile.get("app_log_retention_days", 7)),
13151397
simulation_enabled=bool(profile.get("simulation_enabled", True)),
13161398
simulation_max_concurrency=int(
13171399
profile.get("simulation_max_concurrency", 2)
@@ -2492,6 +2574,49 @@ def build_parser() -> argparse.ArgumentParser:
24922574
"template defaults"
24932575
),
24942576
)
2577+
create_parser.add_argument(
2578+
"--transaction-trace-logging",
2579+
action=argparse.BooleanOptionalAction,
2580+
default=None,
2581+
help=(
2582+
"emit per-transaction debug summaries in generated bootstrap "
2583+
"profiles; overrides template defaults"
2584+
),
2585+
)
2586+
create_parser.add_argument(
2587+
"--app-log-level",
2588+
choices=sorted(SUPPORTED_APP_LOG_LEVELS),
2589+
type=str,
2590+
help=(
2591+
"application log level for generated bootstrap profiles; "
2592+
"overrides template defaults"
2593+
),
2594+
)
2595+
create_parser.add_argument(
2596+
"--app-log-json",
2597+
action=argparse.BooleanOptionalAction,
2598+
default=None,
2599+
help=(
2600+
"emit structured JSON application logs in generated bootstrap "
2601+
"profiles; overrides template defaults"
2602+
),
2603+
)
2604+
create_parser.add_argument(
2605+
"--app-log-rotation-hours",
2606+
type=int,
2607+
help=(
2608+
"rotate application log files after this many hours in generated "
2609+
"bootstrap profiles; overrides template defaults"
2610+
),
2611+
)
2612+
create_parser.add_argument(
2613+
"--app-log-retention-days",
2614+
type=int,
2615+
help=(
2616+
"retain rotated application logs for this many days in generated "
2617+
"bootstrap profiles; overrides template defaults"
2618+
),
2619+
)
24952620
create_parser.add_argument(
24962621
"--simulation-enabled",
24972622
action=argparse.BooleanOptionalAction,
@@ -2741,6 +2866,49 @@ def build_parser() -> argparse.ArgumentParser:
27412866
"manifest value"
27422867
),
27432868
)
2869+
join_parser.add_argument(
2870+
"--transaction-trace-logging",
2871+
action=argparse.BooleanOptionalAction,
2872+
default=None,
2873+
help=(
2874+
"optional node-local per-transaction debug logging override; "
2875+
"defaults to the template value"
2876+
),
2877+
)
2878+
join_parser.add_argument(
2879+
"--app-log-level",
2880+
choices=sorted(SUPPORTED_APP_LOG_LEVELS),
2881+
type=str,
2882+
help=(
2883+
"optional node-local application log level override; defaults "
2884+
"to the template value"
2885+
),
2886+
)
2887+
join_parser.add_argument(
2888+
"--app-log-json",
2889+
action=argparse.BooleanOptionalAction,
2890+
default=None,
2891+
help=(
2892+
"optional node-local structured JSON logging override; defaults "
2893+
"to the template value"
2894+
),
2895+
)
2896+
join_parser.add_argument(
2897+
"--app-log-rotation-hours",
2898+
type=int,
2899+
help=(
2900+
"optional node-local log rotation interval override in hours; "
2901+
"defaults to the template value"
2902+
),
2903+
)
2904+
join_parser.add_argument(
2905+
"--app-log-retention-days",
2906+
type=int,
2907+
help=(
2908+
"optional node-local log retention override in days; defaults "
2909+
"to the template value"
2910+
),
2911+
)
27442912
join_parser.add_argument(
27452913
"--simulation-enabled",
27462914
action=argparse.BooleanOptionalAction,

src/xian_cli/models.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,15 @@
2525
"local_stack",
2626
"service_node",
2727
}
28+
SUPPORTED_APP_LOG_LEVELS = {
29+
"TRACE",
30+
"DEBUG",
31+
"INFO",
32+
"SUCCESS",
33+
"WARNING",
34+
"ERROR",
35+
"CRITICAL",
36+
}
2837

2938

3039
def _require_str(payload: dict, key: str) -> str:
@@ -140,6 +149,18 @@ def _require_tracer_mode(payload: dict, key: str) -> str:
140149
return value
141150

142151

152+
def _require_app_log_level(payload: dict, key: str) -> str:
153+
value = payload.get(key, "INFO")
154+
if not isinstance(value, str):
155+
raise ValueError(f"{key} must be a string")
156+
normalized = value.upper()
157+
if normalized not in SUPPORTED_APP_LOG_LEVELS:
158+
raise ValueError(
159+
f"{key} must be one of {sorted(SUPPORTED_APP_LOG_LEVELS)}"
160+
)
161+
return normalized
162+
163+
143164
def _require_mode(payload: dict) -> str:
144165
mode = _require_str(payload, "mode")
145166
if mode not in SUPPORTED_NETWORK_MODES:
@@ -205,6 +226,17 @@ def normalize_node_profile(payload: dict) -> dict:
205226
payload, "block_policy_interval"
206227
),
207228
"tracer_mode": _require_tracer_mode(payload, "tracer_mode"),
229+
"transaction_trace_logging": _require_bool(
230+
payload, "transaction_trace_logging", default=False
231+
),
232+
"app_log_level": _require_app_log_level(payload, "app_log_level"),
233+
"app_log_json": _require_bool(payload, "app_log_json", default=False),
234+
"app_log_rotation_hours": _require_positive_int(
235+
payload, "app_log_rotation_hours", default=1
236+
),
237+
"app_log_retention_days": _require_positive_int(
238+
payload, "app_log_retention_days", default=7
239+
),
208240
"simulation_enabled": _require_bool(
209241
payload, "simulation_enabled", default=True
210242
),
@@ -268,6 +300,17 @@ def normalize_network_template(payload: dict) -> dict:
268300
payload, "block_policy_interval"
269301
),
270302
"tracer_mode": _require_tracer_mode(payload, "tracer_mode"),
303+
"transaction_trace_logging": _require_bool(
304+
payload, "transaction_trace_logging", default=False
305+
),
306+
"app_log_level": _require_app_log_level(payload, "app_log_level"),
307+
"app_log_json": _require_bool(payload, "app_log_json", default=False),
308+
"app_log_rotation_hours": _require_positive_int(
309+
payload, "app_log_rotation_hours", default=1
310+
),
311+
"app_log_retention_days": _require_positive_int(
312+
payload, "app_log_retention_days", default=7
313+
),
271314
"simulation_enabled": _require_bool(
272315
payload, "simulation_enabled", default=True
273316
),
@@ -437,6 +480,11 @@ class NodeProfile:
437480
block_policy_mode: str = "on_demand"
438481
block_policy_interval: str = "0s"
439482
tracer_mode: str = "python_line_v1"
483+
transaction_trace_logging: bool = False
484+
app_log_level: str = "INFO"
485+
app_log_json: bool = False
486+
app_log_rotation_hours: int = 1
487+
app_log_retention_days: int = 7
440488
simulation_enabled: bool = True
441489
simulation_max_concurrency: int = 2
442490
simulation_timeout_ms: int = 3000
@@ -465,6 +513,11 @@ class NetworkTemplate:
465513
block_policy_mode: str = "on_demand"
466514
block_policy_interval: str = "0s"
467515
tracer_mode: str = "python_line_v1"
516+
transaction_trace_logging: bool = False
517+
app_log_level: str = "INFO"
518+
app_log_json: bool = False
519+
app_log_rotation_hours: int = 1
520+
app_log_retention_days: int = 7
468521
simulation_enabled: bool = True
469522
simulation_max_concurrency: int = 2
470523
simulation_timeout_ms: int = 3000

0 commit comments

Comments
 (0)