""" Command-line interface for the metrics module. """ import argparse from datetime import datetime, timezone # Import logger_utils from app.logcli.utils.logger_utils import log_info, log_error # Import client functions from .client import get_metrics, export_metrics_to_file from .models import DEFAULT_METRICS, DEFAULT_START_TIME, DEFAULT_END_TIME def setup_metrics_parser(subparsers) -> None: """ Set up the metrics subcommand parser. Args: subparsers: The subparsers object from the main parser """ metrics_parser = subparsers.add_parser( 'metrics', help='Query and export cluster metrics' ) # Add required arguments for metrics metrics_parser.add_argument( '--region', type=str, required=True, help='The region where the cluster is located (e.g., us-west-2)' ) metrics_parser.add_argument( '--o11y-id', type=str, required=True, help='The observability ID for the cluster' ) # Create subparsers for metrics actions metrics_subparsers = metrics_parser.add_subparsers( dest='metrics_action', required=True, help='Action to perform on metrics data (query, export)' ) # Common arguments for metrics commands common_metrics_args = argparse.ArgumentParser(add_help=False) common_metrics_args.add_argument( '--cluster', type=str, required=True, help='The TiDB Cluster ID.' ) common_metrics_args.add_argument( '--start', type=validate_datetime_format, default=DEFAULT_START_TIME, help=f'Start time in YYYYMMDDHH or YYYYMMDDHHMM format (default: {DEFAULT_START_TIME.strftime("%Y%m%d%H%M")})' ) common_metrics_args.add_argument( '--end', type=validate_datetime_format, default=DEFAULT_END_TIME, help=f'End time in YYYYMMDDHH or YYYYMMDDHHMM format (default: {DEFAULT_END_TIME.strftime("%Y%m%d%H%M")})' ) common_metrics_args.add_argument( '--metrics', nargs='+', default=DEFAULT_METRICS, help=f'List of metrics to query (default: {DEFAULT_METRICS})' ) common_metrics_args.add_argument( '--instance', type=str, help='Filter metrics by instance (e.g., db-tidb-1)' ) common_metrics_args.add_argument( '--step', type=str, default='15s', help='Query resolution step width in duration format (e.g., 15s, 1m, 1h)' ) # Query subcommand query_parser = metrics_subparsers.add_parser( 'query', parents=[common_metrics_args], help='Query metrics and display results in the console' ) query_parser.set_defaults(func=handle_query_metrics) # Export subcommand export_parser = metrics_subparsers.add_parser( 'export', parents=[common_metrics_args], help='Export metrics to a file' ) export_parser.add_argument( '--output', type=str, required=True, help='Output file path' ) export_parser.add_argument( '--format', type=str, choices=['json', 'csv'], default='json', help='Output format (default: json)' ) export_parser.set_defaults(func=handle_export_metrics) def validate_datetime_format(date_string: str) -> datetime: """ Validate and parse a datetime string in YYYYMMDDHH or YYYYMMDDHHMM format. Args: date_string: The datetime string to validate and parse Returns: A datetime object Raises: argparse.ArgumentTypeError: If the datetime string is invalid """ try: if len(date_string) == 10: # YYYYMMDDHH return datetime.strptime(date_string, '%Y%m%d%H').replace(tzinfo=timezone.utc) elif len(date_string) == 12: # YYYYMMDDHHMM return datetime.strptime(date_string, '%Y%m%d%H%M').replace(tzinfo=timezone.utc) else: raise ValueError("Invalid datetime format. Use YYYYMMDDHH or YYYYMMDDHHMM.") except ValueError as e: raise argparse.ArgumentTypeError(f"Invalid datetime format: {e}") def handle_query_metrics(args) -> int: """ Handle the 'metrics query' command. Args: args: Parsed command-line arguments Returns: Exit code (0 for success, non-zero for failure) """ try: log_info(f"Querying metrics for cluster {args.cluster} from {args.start} to {args.end}") # Query metrics metrics_data = get_metrics( cluster_id=args.cluster, start_time=args.start, end_time=args.end, metrics=args.metrics, instance=args.instance, step=args.step, region=args.region, o11y_id=args.o11y_id ) # Print results in a readable format print("\n=== Metrics Query Results ===") print(f"Cluster: {args.cluster}") print(f"Time Range: {args.start} to {args.end}") print(f"Metrics: {', '.join(args.metrics)}") if args.instance: print(f"Instance: {args.instance}") # Print a summary of the results results = metrics_data.get('data', {}).get('result', []) print(f"\nFound {len(results)} metrics:") for result in results: metric_name = result.get('metric', {}).get('__name__', 'unknown') instance = result.get('metric', {}).get('instance', 'all') num_values = len(result.get('values', [])) print(f"- {metric_name} (instance: {instance}): {num_values} data points") return 0 except Exception as e: log_error(f"Failed to query metrics: {e}") return 1 def handle_export_metrics(args) -> int: """ Handle the 'metrics export' command. Args: args: Parsed command-line arguments Returns: Exit code (0 for success, non-zero for failure) """ try: log_info(f"Exporting metrics for cluster {args.cluster} from {args.start} to {args.end}") # Query metrics metrics_data = get_metrics( cluster_id=args.cluster, start_time=args.start, end_time=args.end, metrics=args.metrics, instance=args.instance, step=args.step, region=args.region, o11y_id=args.o11y_id ) # Export to file export_metrics_to_file(metrics_data, args.format, args.output) return 0 except Exception as e: log_error(f"Failed to export metrics: {e}") return 1