diff --git a/.gitignore b/.gitignore index 59c028f..e74b46e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ tests/test/* # mkdocs site +docs/API_usage_files/ # development environment .env diff --git a/cloudwatcher/argparser.py b/cloudwatcher/argparser.py index f89a601..31bb7a9 100644 --- a/cloudwatcher/argparser.py +++ b/cloudwatcher/argparser.py @@ -1,10 +1,9 @@ """ Computing configuration representation """ import argparse -from random import choices from ._version import __version__ -from .const import LOG_CMD, METRIC_CMD, SUBPARSER_MESSAGES +from .const import CLI_DEFAULTS, LOG_CMD, METRIC_CMD, SUBPARSER_MESSAGES class _VersionInHelpParser(argparse.ArgumentParser): @@ -19,18 +18,6 @@ def format_help(self): def build_argparser(): """Build argument parser""" - # args defaults - metric_name = "mem_used" - id = "memory_usage" - days = 1 - hours = 0 - minutes = 0 - unit = "Bytes" - stat = "Maximum" - period = 60 - dir = "./" - region = "us-east-1" - # add argument parser parser = _VersionInHelpParser( description="CloudWatch logs and metrics explorer.", @@ -70,7 +57,7 @@ def add_subparser(cmd, msg, subparsers): help="Region to monitor the metrics within. (default: %(default)s)", type=str, required=False, - default=region, + default=CLI_DEFAULTS["region"], metavar="R", ) aws_creds_group.add_argument( @@ -103,7 +90,7 @@ def add_subparser(cmd, msg, subparsers): "-d", "--dir", help="Directory to store the results in. Used with `--save` (default: %(default)s)", - default=dir, + default=CLI_DEFAULTS["dir"], ) sps[METRIC_CMD].add_argument( @@ -118,7 +105,7 @@ def add_subparser(cmd, msg, subparsers): "-i", "--id", help="The unique identifier to assign to the metric data. Must be of the form '^[a-z][a-zA-Z0-9_]*$'.", - default=id, + default=CLI_DEFAULTS["id"], required=False, metavar="ID", ) @@ -126,17 +113,26 @@ def add_subparser(cmd, msg, subparsers): "-m", "--metric", help="Name of the metric collected by CloudWatchAgent (default: %(default)s)", - default=metric_name, + default=CLI_DEFAULTS["metric_name"], required=False, metavar="N", ) sps[METRIC_CMD].add_argument( - "-iid", - "--instance-id", - help="Instance ID, needs to follow 'i-' format", + "-dn", + "--dimension-name", + help="The name of the dimension to query. (default: %(default)s)", + required=False, + type=str, + metavar="N", + default=CLI_DEFAULTS["dimension_name"], + ) + sps[METRIC_CMD].add_argument( + "-dv", + "--dimension-value", + help="The value of the dimension to filter on.", required=True, type=str, - metavar="ID", + metavar="V", ) sps[METRIC_CMD].add_argument( "--uptime", @@ -150,7 +146,7 @@ def add_subparser(cmd, msg, subparsers): metric_collection_start_time.add_argument( "--days", help="How many days to subtract from the current date to determine the metric collection start time (default: %(default)s).", - default=days, + default=CLI_DEFAULTS["days"], type=int, metavar="D", ) @@ -158,7 +154,7 @@ def add_subparser(cmd, msg, subparsers): "-hr", "--hours", help="How many hours to subtract from the current time to determine the metric collection start time (default: %(default)s).", - default=hours, + default=CLI_DEFAULTS["hours"], type=int, metavar="H", ) @@ -166,7 +162,7 @@ def add_subparser(cmd, msg, subparsers): "-mi", "--minutes", help="How many minutes to subtract from the current time to determine the metric collection start time (default: %(default)s).", - default=minutes, + default=CLI_DEFAULTS["minutes"], type=int, metavar="M", ) @@ -178,7 +174,6 @@ def add_subparser(cmd, msg, subparsers): If you specify a unit, it acts as a filter and returns only data that was collected with that unit specified. Use 'Bytes' for memory (default: %(default)s) """, - default=unit, type=str, metavar="U", ) @@ -186,7 +181,7 @@ def add_subparser(cmd, msg, subparsers): "-s", "--stat", help="The statistic to apply over the time intervals, e.g. 'Maximum' (default: %(default)s)", - default=stat, + default=CLI_DEFAULTS["stat"], type=str, metavar="S", ) @@ -197,7 +192,7 @@ def add_subparser(cmd, msg, subparsers): The granularity, in seconds, of the returned data points. Choices: 1, 5, 10, 30, 60, or any multiple of 60 (default: %(default)s). It affects the data availability. See the docs 'Usage' section for more details. """, - default=period, + default=CLI_DEFAULTS["period"], type=int, metavar="P", ) diff --git a/cloudwatcher/cli.py b/cloudwatcher/cli.py index aaae4b8..2ed4420 100644 --- a/cloudwatcher/cli.py +++ b/cloudwatcher/cli.py @@ -36,11 +36,6 @@ def main(): if args.query_json is not None: raise NotImplementedError("Querying via JSON is not yet implemented") - if not args.instance_id.startswith("i-"): - raise ValueError( - f"Instance id needs to start with 'i-'. Got: {args.instance_id}" - ) - if not os.path.exists(args.dir): _LOGGER.info(f"Creating directory: {args.dir}") os.makedirs(args.dir, exist_ok=True) @@ -50,7 +45,8 @@ def main(): metric_name=args.metric, metric_id=args.id, metric_unit=args.unit, - ec2_instance_id=args.instance_id, + dimension_value=args.dimension_value, + dimension_name=args.dimension_name, aws_access_key_id=args.aws_access_key_id, aws_secret_access_key=args.aws_secret_access_key, aws_session_token=args.aws_session_token, @@ -67,30 +63,26 @@ def main(): metric_watcher.log_response(response=response) metric_watcher.log_metric(response=response) + metric_watcher.log_metric_summary(response=response) + name_prefix = f"{args.dimension_name}_{args.dimension_value}_{args.metric}" if args.save: metric_watcher.save_metric_json( - file_path=os.path.join( - args.dir, f"{args.instance_id}_{args.metric}.json" - ), + file_path=os.path.join(args.dir, f"{name_prefix}.json"), response=response, ) metric_watcher.save_metric_csv( - file_path=os.path.join( - args.dir, f"{args.instance_id}_{args.metric}.csv" - ), + file_path=os.path.join(args.dir, f"{name_prefix}.csv"), response=response, ) metric_watcher.save_response_json( - file_path=os.path.join(args.dir, f"{args.instance_id}_response.json"), + file_path=os.path.join(args.dir, f"{name_prefix}_response.json"), response=response, ) if args.plot: metric_watcher.save_metric_plot( - file_path=os.path.join( - args.dir, f"{args.instance_id}_{args.metric}.png" - ), + file_path=os.path.join(args.dir, f"{name_prefix}.png"), response=response, ) diff --git a/cloudwatcher/const.py b/cloudwatcher/const.py index f366a78..437317e 100644 --- a/cloudwatcher/const.py +++ b/cloudwatcher/const.py @@ -17,3 +17,17 @@ "hour": {"days": 0, "hours": 1, "minutes": 0, "stat": "Maximum", "period": 1}, "minute": {"days": 0, "hours": 0, "minutes": 1, "stat": "Maximum", "period": 1}, } + +CLI_DEFAULTS = { + "metric_name": "mem_used", + "id": "memory_usage", + "dimension_name": "InstanceId", + "days": 1, + "hours": 0, + "minutes": 0, + "unit": "Bytes", + "stat": "Maximum", + "period": 60, + "dir": "./", + "region": "us-east-1", +} diff --git a/cloudwatcher/metric_handlers.py b/cloudwatcher/metric_handlers.py index 852aa20..c22070c 100644 --- a/cloudwatcher/metric_handlers.py +++ b/cloudwatcher/metric_handlers.py @@ -102,7 +102,7 @@ def __call__(self, target: str) -> None: raise NotImplementedError( "Logging responses to a file is not yet implemented." ) - _LOGGER.info(json.dumps(self.response, indent=4, default=str)) + _LOGGER.debug(json.dumps(self.response, indent=4, default=str)) class TimedMetricPlotter(TimedMetricHandler): diff --git a/cloudwatcher/metricwatcher.py b/cloudwatcher/metricwatcher.py index c25c62f..45c1196 100644 --- a/cloudwatcher/metricwatcher.py +++ b/cloudwatcher/metricwatcher.py @@ -6,6 +6,7 @@ import pytz from .cloudwatcher import CloudWatcher +from .const import DEFAULT_QUERY_KWARGS, QUERY_KWARGS_PRESETS from .metric_handlers import ( ResponseHandler, ResponseLogger, @@ -18,7 +19,6 @@ TimedMetricPlotter, TimedMetricSummarizer, ) -from .const import DEFAULT_QUERY_KWARGS, QUERY_KWARGS_PRESETS _LOGGER = logging.getLogger(__name__) @@ -31,10 +31,11 @@ class MetricWatcher(CloudWatcher): def __init__( self, namespace: str, - ec2_instance_id: str, + dimension_name: str, + dimension_value: str, metric_name: str, metric_id: str, - metric_unit: str, + metric_unit: Optional[str] = None, aws_access_key_id: Optional[str] = None, aws_secret_access_key: Optional[str] = None, aws_session_token: Optional[str] = None, @@ -55,7 +56,8 @@ def __init__( aws_region_name=aws_region_name, ) self.namespace = namespace - self.ec2_instance_id = ec2_instance_id + self.dimension_name = dimension_name + self.dimension_value = dimension_value self.metric_name = metric_name self.metric_id = metric_id self.metric_unit = metric_unit @@ -91,8 +93,9 @@ def query_ec2_metrics( start_time = now - datetime.timedelta(days=days, hours=hours, minutes=minutes) _LOGGER.info( - f"Querying '{self.metric_name}' for EC2 instance '{self.ec2_instance_id}'" - f" from {start_time.strftime('%H:%M:%S')} to {now.strftime('%H:%M:%S')}" + f"Querying '{self.metric_name}' for dimension " + f"('{self.dimension_name}'='{self.dimension_value}') from " + f"{start_time.strftime('%H:%M:%S')} to {now.strftime('%H:%M:%S')}" ) response = self.client.get_metric_data( @@ -104,11 +107,16 @@ def query_ec2_metrics( "Namespace": self.namespace, "MetricName": self.metric_name, "Dimensions": [ - {"Name": "InstanceId", "Value": self.ec2_instance_id} + { + "Name": self.dimension_name, + "Value": self.dimension_value, + } ], }, "Stat": stat, - "Unit": self.metric_unit, + "Unit": str( + self.metric_unit + ), # str(None) is desired, if no unit is specified "Period": period, }, }, @@ -125,6 +133,7 @@ def query_ec2_metrics( def get_ec2_uptime( self, + ec2_instance_id: str, days: int, hours: int, minutes: int, @@ -132,23 +141,20 @@ def get_ec2_uptime( """ Get the runtime of an EC2 instance - :param logging.logger logger: logger to use. Any object that has 'info', 'warning' and 'error' methods :param int days: how many days to subtract from the current date to determine the metric collection start time :param int hours: how many hours to subtract from the current time to determine the metric collection start time :param int minute: how many minutes to subtract from the current time to determine the metric collection start time - :param str namespace: namespace of the metric, e.g. 'NepheleNamespace' - :param boto3.resource ec2_resource: boto3 resource object to use, optional - + :param str ec2_instance_id: the ID of the EC2 instance to query Returns: int: runtime of the instance in seconds """ - if not self.is_ec2_running(): + if not self.is_ec2_running(ec2_instance_id): _LOGGER.info( - f"Instance '{self.ec2_instance_id}' is not running anymore. " + f"Instance '{self.dimension_value}' is not running anymore. " f"Uptime will be estimated based on reported metrics in the last {days} days" ) instances = self.ec2_resource.instances.filter( - Filters=[{"Name": "instance-id", "Values": [self.ec2_instance_id]}] + Filters=[{"Name": "instance-id", "Values": [self.dimension_value]}] ) # get the latest reported metric metrics_response = self.query_ec2_metrics( @@ -167,33 +173,32 @@ def get_ec2_uptime( earliest_metric_report_time - latest_metric_report_time ).total_seconds() except IndexError: - _LOGGER.warning(f"No metric data found for EC2: {self.ec2_instance_id}") + _LOGGER.warning(f"No metric data found for EC2: {self.dimension_value}") return instances = self.ec2_resource.instances.filter( - Filters=[{"Name": "instance-id", "Values": [self.ec2_instance_id]}] + Filters=[{"Name": "instance-id", "Values": [self.dimension_value]}] ) for instance in instances: _LOGGER.info( - f"Instance '{self.ec2_instance_id}' is still running. " + f"Instance '{self.dimension_value}' is still running. " f"Launch time: {instance.launch_time}" ) return (datetime.now(pytz.utc) - instance.launch_time).total_seconds() - def is_ec2_running(self) -> bool: + def is_ec2_running(self, ec2_instance_id: str) -> bool: """ Check if EC2 instance is running + :param str ec2_instance_id: the ID of the EC2 instance to query :returns bool: True if instance is running, False otherwise """ instances = self.ec2_resource.instances.filter( - Filters=[{"Name": "instance-id", "Values": [self.ec2_instance_id]}] + Filters=[{"Name": "instance-id", "Values": [ec2_instance_id]}] ) if len(list(instances)) == 0: return None if len(list(instances)) > 1: - raise Exception( - f"Multiple EC2 instances matched by ID: {self.ec2_instance_id}" - ) + raise Exception(f"Multiple EC2 instances matched by ID: {ec2_instance_id}") for instance in instances: # check the status codes and their meanings: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_InstanceState.html if instance.state["Code"] <= 16: @@ -375,7 +380,7 @@ def save_metric_plot( query_preset=query_preset, ) - def summarize_metric_json( + def log_metric_summary( self, response: Optional[Dict] = None, query_preset: Optional[str] = None ): """ diff --git a/docs/API_documentation.md b/docs/API_documentation.md index 53a8116..31191e7 100644 --- a/docs/API_documentation.md +++ b/docs/API_documentation.md @@ -54,7 +54,7 @@ A class for AWS CloudWatch metric retrieval and parsing ```python -def __init__(self, namespace: str, ec2_instance_id: str, metric_name: str, metric_id: str, metric_unit: str, aws_access_key_id: Union[str, NoneType]=None, aws_secret_access_key: Union[str, NoneType]=None, aws_session_token: Union[str, NoneType]=None, aws_region_name: Union[str, NoneType]=None) -> None +def __init__(self, namespace: str, dimension_name: str, dimension_value: str, metric_name: str, metric_id: str, metric_unit: Union[str, NoneType]=None, aws_access_key_id: Union[str, NoneType]=None, aws_secret_access_key: Union[str, NoneType]=None, aws_session_token: Union[str, NoneType]=None, aws_region_name: Union[str, NoneType]=None) -> None ``` Initialize MetricWatcher @@ -68,27 +68,30 @@ Initialize MetricWatcher ```python -def get_ec2_uptime(self, days: int, hours: int, minutes: int) -> int +def get_ec2_uptime(self, ec2_instance_id: str, days: int, hours: int, minutes: int) -> int ``` Get the runtime of an EC2 instance #### Parameters: -- `logger` (`logging.logger`): logger to use. Any object that has 'info', 'warning' and 'error' methods - `days` (`int`): how many days to subtract from the current date to determine the metric collection start time - `hours` (`int`): how many hours to subtract from the current time to determine the metric collection start time - `minute` (`int`): how many minutes to subtract from the current time to determine the metric collection start time -- `namespace` (`str`): namespace of the metric, e.g. 'NepheleNamespace' -- `ec2_resource` (`boto3.resource`): boto3 resource object to use, optional +- `ec2_instance_id` (`str`): the ID of the EC2 instance to queryReturns: int: runtime of the instance in seconds ```python -def is_ec2_running(self) -> bool +def is_ec2_running(self, ec2_instance_id: str) -> bool ``` Check if EC2 instance is running +#### Parameters: + +- `ec2_instance_id` (`str`): the ID of the EC2 instance to query + + #### Returns: - `bool`: True if instance is running, False otherwise @@ -112,6 +115,22 @@ Query and log the metric data +```python +def log_metric_summary(self, response: Union[Dict, NoneType]=None, query_preset: Union[str, NoneType]=None) +``` + +Query and summarize the metric data to a JSON file +#### Parameters: + +- `file_path` (`str`): path to the file to save the metric data to +- `response` (`dict`): response retrieved with `query_ec2_metrics`.A query is performed if not provided. +- `response` (`dict`): response from `query_ec2_metrics` method +- `query_kwargs` (`dict`): kwargs to pass to the EC2 query +- `query_preset` (`str`): period preset to use for the EC2 query + + + + ```python def log_response(self, response: Union[Dict, NoneType]=None, query_preset: Union[str, NoneType]=None) ``` @@ -209,22 +228,6 @@ Query and save the response data to a JSON file -```python -def summarize_metric_json(self, response: Union[Dict, NoneType]=None, query_preset: Union[str, NoneType]=None) -``` - -Query and summarize the metric data to a JSON file -#### Parameters: - -- `file_path` (`str`): path to the file to save the metric data to -- `response` (`dict`): response retrieved with `query_ec2_metrics`.A query is performed if not provided. -- `response` (`dict`): response from `query_ec2_metrics` method -- `query_kwargs` (`dict`): kwargs to pass to the EC2 query -- `query_preset` (`str`): period preset to use for the EC2 query - - - - ```python def timed_metric_factory(response: dict) -> List[cloudwatcher.metric_handlers.TimedMetric] ``` @@ -404,4 +407,4 @@ Class to establish the interface for a timed metric handling -*Version Information: `cloudwatcher` v0.0.4, generated by `lucidoc` v0.4.3* \ No newline at end of file +*Version Information: `cloudwatcher` v0.0.5, generated by `lucidoc` v0.4.3* \ No newline at end of file diff --git a/docs/API_usage.ipynb b/docs/API_usage.ipynb index 5f5054b..fa89bc3 100644 --- a/docs/API_usage.ipynb +++ b/docs/API_usage.ipynb @@ -30,20 +30,21 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from cloudwatcher import MetricWatcher\n", - "from cloudwatcher.const import DEFAULT_QUERY_KWARGS\n", + "from cloudwatcher.const import QUERY_KWARGS_PRESETS\n", "\n", - "instance_id = \"i-048529e65b26aaded\"\n", + "instance_id = \"i-0a424d0a6694039ec\"\n", "mw = MetricWatcher(\n", " namespace=\"michal-NepheleNamespace\",\n", " metric_name=\"mem_used\",\n", " metric_id=\"mem_used\",\n", " metric_unit=\"Bytes\",\n", - " ec2_instance_id=instance_id,\n", + " dimension_value=instance_id,\n", + " dimension_name=\"InstanceId\",\n", ")" ] }, @@ -53,12 +54,12 @@ "source": [ "### `MetricWatcher` EC2 query period selection\n", "\n", - "In order to specify the EC2 instace query settings (period, granularity, etc.), the user would need to provide multiple parameters. To make it easier, there are a few sensible presets that can be used to select the query settings, which are passed to `query_ec2_metrics` method. These presets are defined to query the data reported by the EC2 instance within the last day, hour or minute." + "In order to specify the EC2 instace query settings (period, granularity, etc.), the user would need to provide multiple parameters. To make it easier, there are a few sensible presets that can be used to select the query settings, which are passed to `query_ec2_metrics` method. These presets are defined to query the data reported by `CloudWatchAgent` within the last day, hour or minute." ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -67,14 +68,12 @@ "['day', 'hour', 'minute']" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "from cloudwatcher.const import QUERY_KWARGS_PRESETS\n", - "\n", "list(QUERY_KWARGS_PRESETS.keys())" ] }, @@ -91,111 +90,103 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
┏━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n",
-       "┃ Time (UTC)  Value      ┃\n",
-       "┡━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n",
-       "│  12:17:40  │ 641.938 MB │\n",
-       "│  12:17:30  │ 641.938 MB │\n",
-       "│  12:17:20  │ 641.906 MB │\n",
-       "│  12:17:10  │ 641.945 MB │\n",
-       "│  12:17:00  │ 641.438 MB │\n",
-       "│  12:16:50  │ 640.918 MB │\n",
-       "│  12:16:40  │ 640.918 MB │\n",
-       "│  12:16:30  │ 640.887 MB │\n",
-       "│  12:16:20  │ 641.262 MB │\n",
-       "│  12:16:10  │ 641.441 MB │\n",
-       "│  12:16:00  │ 641.441 MB │\n",
-       "│  12:15:50  │ 767.469 MB │\n",
-       "│  12:15:40  │ 2.078 GB   │\n",
-       "│  12:15:30  │ 2.079 GB   │\n",
-       "│  12:15:20  │ 2.304 GB   │\n",
-       "│  12:15:10  │ 2.064 GB   │\n",
-       "│  12:15:00  │ 7.922 GB   │\n",
-       "│  12:14:50  │ 7.789 GB   │\n",
-       "│  12:14:40  │ 2.307 GB   │\n",
-       "│  12:14:30  │ 2.128 GB   │\n",
-       "│  12:14:20  │ 2.122 GB   │\n",
-       "│  12:14:10  │ 2.095 GB   │\n",
-       "│  12:14:00  │ 1.995 GB   │\n",
-       "│  12:13:50  │ 1.886 GB   │\n",
-       "│  12:13:40  │ 1.926 GB   │\n",
-       "│  12:13:30  │ 1.730 GB   │\n",
-       "│  12:13:20  │ 1.730 GB   │\n",
-       "│  12:13:10  │ 1.555 GB   │\n",
-       "│  12:13:00  │ 1.492 GB   │\n",
-       "│  12:12:50  │ 1.462 GB   │\n",
-       "│  12:12:40  │ 1.540 GB   │\n",
-       "│  12:12:30  │ 1.313 GB   │\n",
-       "│  12:12:20  │ 967.289 MB │\n",
-       "│  12:12:10  │ 832.797 MB │\n",
-       "│  12:12:00  │ 1.134 GB   │\n",
-       "│  12:11:50  │ 1.031 GB   │\n",
-       "│  12:11:40  │ 958.074 MB │\n",
-       "│  12:11:30  │ 860.031 MB │\n",
-       "│  12:11:20  │ 799.848 MB │\n",
-       "│  12:11:10  │ 740.176 MB │\n",
-       "│  12:11:00  │ 679.586 MB │\n",
-       "│  12:10:50  │ 698.457 MB │\n",
-       "│  12:10:40  │ 689.941 MB │\n",
-       "│  12:10:30  │ 624.820 MB │\n",
-       "└────────────┴────────────┘\n",
+       "
┏━━━━━━━━━━━━┳━━━━━━━━━━━━━┓\n",
+       "┃ Time (UTC)  Value       ┃\n",
+       "┡━━━━━━━━━━━━╇━━━━━━━━━━━━━┩\n",
+       "│  20:06:00  │ 762.426 MB  │\n",
+       "│  20:05:50  │ 762.641 MB  │\n",
+       "│  20:05:40  │ 762.480 MB  │\n",
+       "│  20:05:30  │ 762.254 MB  │\n",
+       "│  20:05:20  │ 762.715 MB  │\n",
+       "│  20:05:10  │ 762.723 MB  │\n",
+       "│  20:05:00  │ 762.324 MB  │\n",
+       "│  20:04:50  │ 763.070 MB  │\n",
+       "│  20:04:40  │ 763.223 MB  │\n",
+       "│  20:04:30  │ 763.422 MB  │\n",
+       "│  20:04:20  │ 761.316 MB  │\n",
+       "│  20:04:10  │ 762.250 MB  │\n",
+       "│  20:04:00  │ 2.107 GB    │\n",
+       "│  20:03:50  │ 2.333 GB    │\n",
+       "│  20:03:40  │ 2.331 GB    │\n",
+       "│  20:03:30  │ 2.088 GB    │\n",
+       "│  20:03:20  │ 7.882 GB    │\n",
+       "│  20:03:10  │ 7.881 GB    │\n",
+       "│  20:03:00  │ 2.226 GB    │\n",
+       "│  20:02:50  │ 2.130 GB    │\n",
+       "│  20:02:40  │ 2.002 GB    │\n",
+       "│  20:02:30  │ 1.984 GB    │\n",
+       "│  20:02:20  │ 1.888 GB    │\n",
+       "│  20:02:10  │ 1.780 GB    │\n",
+       "│  20:02:00  │ 1.836 GB    │\n",
+       "│  20:01:50  │ 1.726 GB    │\n",
+       "│  20:01:40  │ 2.015 GB    │\n",
+       "│  20:01:30  │ 2.011 GB    │\n",
+       "│  20:01:20  │ 1.464 GB    │\n",
+       "│  20:01:10  │ 1.628 GB    │\n",
+       "│  20:01:00  │ 1.635 GB    │\n",
+       "│  20:00:50  │ 1.554 GB    │\n",
+       "│  20:00:40  │ 1.322 GB    │\n",
+       "│  20:00:30  │ 1.098 GB    │\n",
+       "│  20:00:20  │ 1023.930 MB │\n",
+       "│  20:00:10  │ 865.840 MB  │\n",
+       "│  20:00:00  │ 759.039 MB  │\n",
+       "│  19:59:50  │ 727.934 MB  │\n",
+       "│  19:59:40  │ 704.547 MB  │\n",
+       "│  19:59:30  │ 658.090 MB  │\n",
+       "└────────────┴─────────────┘\n",
        "
\n" ], "text/plain": [ - "┏━━━━━━━━━━━━┳━━━━━━━━━━━━┓\n", - "┃\u001b[1;35m \u001b[0m\u001b[1;35mTime (UTC)\u001b[0m\u001b[1;35m \u001b[0m┃\u001b[1;35m \u001b[0m\u001b[1;35mValue \u001b[0m\u001b[1;35m \u001b[0m┃\n", - "┡━━━━━━━━━━━━╇━━━━━━━━━━━━┩\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:17:40 \u001b[0m\u001b[2m \u001b[0m│ 641.938 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:17:30 \u001b[0m\u001b[2m \u001b[0m│ 641.938 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:17:20 \u001b[0m\u001b[2m \u001b[0m│ 641.906 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:17:10 \u001b[0m\u001b[2m \u001b[0m│ 641.945 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:17:00 \u001b[0m\u001b[2m \u001b[0m│ 641.438 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:50 \u001b[0m\u001b[2m \u001b[0m│ 640.918 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:40 \u001b[0m\u001b[2m \u001b[0m│ 640.918 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:30 \u001b[0m\u001b[2m \u001b[0m│ 640.887 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:20 \u001b[0m\u001b[2m \u001b[0m│ 641.262 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:10 \u001b[0m\u001b[2m \u001b[0m│ 641.441 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:16:00 \u001b[0m\u001b[2m \u001b[0m│ 641.441 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:50 \u001b[0m\u001b[2m \u001b[0m│ 767.469 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:40 \u001b[0m\u001b[2m \u001b[0m│ 2.078 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:30 \u001b[0m\u001b[2m \u001b[0m│ 2.079 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:20 \u001b[0m\u001b[2m \u001b[0m│ 2.304 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:10 \u001b[0m\u001b[2m \u001b[0m│ 2.064 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:15:00 \u001b[0m\u001b[2m \u001b[0m│ 7.922 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:50 \u001b[0m\u001b[2m \u001b[0m│ 7.789 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:40 \u001b[0m\u001b[2m \u001b[0m│ 2.307 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:30 \u001b[0m\u001b[2m \u001b[0m│ 2.128 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:20 \u001b[0m\u001b[2m \u001b[0m│ 2.122 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:10 \u001b[0m\u001b[2m \u001b[0m│ 2.095 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:14:00 \u001b[0m\u001b[2m \u001b[0m│ 1.995 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:50 \u001b[0m\u001b[2m \u001b[0m│ 1.886 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:40 \u001b[0m\u001b[2m \u001b[0m│ 1.926 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:30 \u001b[0m\u001b[2m \u001b[0m│ 1.730 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:20 \u001b[0m\u001b[2m \u001b[0m│ 1.730 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:10 \u001b[0m\u001b[2m \u001b[0m│ 1.555 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:13:00 \u001b[0m\u001b[2m \u001b[0m│ 1.492 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:50 \u001b[0m\u001b[2m \u001b[0m│ 1.462 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:40 \u001b[0m\u001b[2m \u001b[0m│ 1.540 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:30 \u001b[0m\u001b[2m \u001b[0m│ 1.313 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:20 \u001b[0m\u001b[2m \u001b[0m│ 967.289 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:10 \u001b[0m\u001b[2m \u001b[0m│ 832.797 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:12:00 \u001b[0m\u001b[2m \u001b[0m│ 1.134 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:50 \u001b[0m\u001b[2m \u001b[0m│ 1.031 GB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:40 \u001b[0m\u001b[2m \u001b[0m│ 958.074 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:30 \u001b[0m\u001b[2m \u001b[0m│ 860.031 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:20 \u001b[0m\u001b[2m \u001b[0m│ 799.848 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:10 \u001b[0m\u001b[2m \u001b[0m│ 740.176 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:11:00 \u001b[0m\u001b[2m \u001b[0m│ 679.586 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:10:50 \u001b[0m\u001b[2m \u001b[0m│ 698.457 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:10:40 \u001b[0m\u001b[2m \u001b[0m│ 689.941 MB │\n", - "│\u001b[2m \u001b[0m\u001b[2m 12:10:30 \u001b[0m\u001b[2m \u001b[0m│ 624.820 MB │\n", - "└────────────┴────────────┘\n" + "┏━━━━━━━━━━━━┳━━━━━━━━━━━━━┓\n", + "┃\u001b[1;35m \u001b[0m\u001b[1;35mTime (UTC)\u001b[0m\u001b[1;35m \u001b[0m┃\u001b[1;35m \u001b[0m\u001b[1;35mValue \u001b[0m\u001b[1;35m \u001b[0m┃\n", + "┡━━━━━━━━━━━━╇━━━━━━━━━━━━━┩\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:06:00 \u001b[0m\u001b[2m \u001b[0m│ 762.426 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:50 \u001b[0m\u001b[2m \u001b[0m│ 762.641 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:40 \u001b[0m\u001b[2m \u001b[0m│ 762.480 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:30 \u001b[0m\u001b[2m \u001b[0m│ 762.254 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:20 \u001b[0m\u001b[2m \u001b[0m│ 762.715 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:10 \u001b[0m\u001b[2m \u001b[0m│ 762.723 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:05:00 \u001b[0m\u001b[2m \u001b[0m│ 762.324 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:50 \u001b[0m\u001b[2m \u001b[0m│ 763.070 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:40 \u001b[0m\u001b[2m \u001b[0m│ 763.223 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:30 \u001b[0m\u001b[2m \u001b[0m│ 763.422 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:20 \u001b[0m\u001b[2m \u001b[0m│ 761.316 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:10 \u001b[0m\u001b[2m \u001b[0m│ 762.250 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:04:00 \u001b[0m\u001b[2m \u001b[0m│ 2.107 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:50 \u001b[0m\u001b[2m \u001b[0m│ 2.333 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:40 \u001b[0m\u001b[2m \u001b[0m│ 2.331 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:30 \u001b[0m\u001b[2m \u001b[0m│ 2.088 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:20 \u001b[0m\u001b[2m \u001b[0m│ 7.882 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:10 \u001b[0m\u001b[2m \u001b[0m│ 7.881 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:03:00 \u001b[0m\u001b[2m \u001b[0m│ 2.226 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:50 \u001b[0m\u001b[2m \u001b[0m│ 2.130 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:40 \u001b[0m\u001b[2m \u001b[0m│ 2.002 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:30 \u001b[0m\u001b[2m \u001b[0m│ 1.984 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:20 \u001b[0m\u001b[2m \u001b[0m│ 1.888 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:10 \u001b[0m\u001b[2m \u001b[0m│ 1.780 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:02:00 \u001b[0m\u001b[2m \u001b[0m│ 1.836 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:50 \u001b[0m\u001b[2m \u001b[0m│ 1.726 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:40 \u001b[0m\u001b[2m \u001b[0m│ 2.015 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:30 \u001b[0m\u001b[2m \u001b[0m│ 2.011 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:20 \u001b[0m\u001b[2m \u001b[0m│ 1.464 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:10 \u001b[0m\u001b[2m \u001b[0m│ 1.628 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:01:00 \u001b[0m\u001b[2m \u001b[0m│ 1.635 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:50 \u001b[0m\u001b[2m \u001b[0m│ 1.554 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:40 \u001b[0m\u001b[2m \u001b[0m│ 1.322 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:30 \u001b[0m\u001b[2m \u001b[0m│ 1.098 GB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:20 \u001b[0m\u001b[2m \u001b[0m│ 1023.930 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:10 \u001b[0m\u001b[2m \u001b[0m│ 865.840 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 20:00:00 \u001b[0m\u001b[2m \u001b[0m│ 759.039 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 19:59:50 \u001b[0m\u001b[2m \u001b[0m│ 727.934 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 19:59:40 \u001b[0m\u001b[2m \u001b[0m│ 704.547 MB │\n", + "│\u001b[2m \u001b[0m\u001b[2m 19:59:30 \u001b[0m\u001b[2m \u001b[0m│ 658.090 MB │\n", + "└────────────┴─────────────┘\n" ] }, "metadata": {}, @@ -217,12 +208,12 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -251,22 +242,22 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'RequestId': '20f5d4a7-6baa-4da7-ab28-b77ca796b3bb',\n", + "{'RequestId': '3e2aba48-c524-40fb-86af-309506615244',\n", " 'HTTPStatusCode': 200,\n", - " 'HTTPHeaders': {'x-amzn-requestid': '20f5d4a7-6baa-4da7-ab28-b77ca796b3bb',\n", + " 'HTTPHeaders': {'x-amzn-requestid': '3e2aba48-c524-40fb-86af-309506615244',\n", " 'content-type': 'text/xml',\n", - " 'content-length': '8116',\n", - " 'date': 'Thu, 28 Apr 2022 14:06:33 GMT'},\n", + " 'content-length': '1241',\n", + " 'date': 'Thu, 28 Apr 2022 20:09:22 GMT'},\n", " 'RetryAttempts': 0}" ] }, - "execution_count": 6, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -277,7 +268,7 @@ " \"hours\": 0,\n", " \"minutes\": 0,\n", " \"stat\": \"Maximum\",\n", - " \"period\": 10,\n", + " \"period\": 60,\n", "}\n", "response = mw.query_ec2_metrics(**FINE_TUNED_SETTINGS)\n", "\n", @@ -295,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -308,18 +299,16 @@ { "data": { "text/plain": [ - "[673120256.0,\n", - " 673087488.0,\n", - " 673087488.0,\n", - " 673087488.0,\n", - " 673120256.0,\n", - " 673128448.0,\n", - " 672595968.0,\n", - " 672595968.0,\n", - " 672051200.0]" + "[799772672.0,\n", + " 2262454272.0,\n", + " 8463368192.0,\n", + " 2286895104.0,\n", + " 2163302400.0,\n", + " 1668444160.0,\n", + " 763293696.0]" ] }, - "execution_count": 8, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } diff --git a/docs/API_usage.md b/docs/API_usage.md index 8a7bdfe..17f7f11 100644 --- a/docs/API_usage.md +++ b/docs/API_usage.md @@ -19,26 +19,25 @@ Alternatively, you can pass the values as arguments to the `MetricWatcher` const ```python from cloudwatcher import MetricWatcher -from cloudwatcher.const import DEFAULT_QUERY_KWARGS +from cloudwatcher.const import QUERY_KWARGS_PRESETS -instance_id = "i-048529e65b26aaded" +instance_id = "i-0a424d0a6694039ec" mw = MetricWatcher( namespace="michal-NepheleNamespace", metric_name="mem_used", metric_id="mem_used", metric_unit="Bytes", - ec2_instance_id=instance_id, + dimension_value=instance_id, + dimension_name="InstanceId", ) ``` ### `MetricWatcher` EC2 query period selection -In order to specify the EC2 instace query settings (period, granularity, etc.), the user would need to provide multiple parameters. To make it easier, there are a few sensible presets that can be used to select the query settings, which are passed to `query_ec2_metrics` method. These presets are defined to query the data reported by the EC2 instance within the last day, hour or minute. +In order to specify the EC2 instace query settings (period, granularity, etc.), the user would need to provide multiple parameters. To make it easier, there are a few sensible presets that can be used to select the query settings, which are passed to `query_ec2_metrics` method. These presets are defined to query the data reported by `CloudWatchAgent` within the last day, hour or minute. ```python -from cloudwatcher.const import QUERY_KWARGS_PRESETS - list(QUERY_KWARGS_PRESETS.keys()) ``` @@ -61,54 +60,50 @@ mw.log_metric(query_preset="day") ``` -
┏━━━━━━━━━━━━┳━━━━━━━━━━━━┓
-┃ Time (UTC)  Value      ┃
-┡━━━━━━━━━━━━╇━━━━━━━━━━━━┩
-│  12:17:40  │ 641.938 MB │
-│  12:17:30  │ 641.938 MB │
-│  12:17:20  │ 641.906 MB │
-│  12:17:10  │ 641.945 MB │
-│  12:17:00  │ 641.438 MB │
-│  12:16:50  │ 640.918 MB │
-│  12:16:40  │ 640.918 MB │
-│  12:16:30  │ 640.887 MB │
-│  12:16:20  │ 641.262 MB │
-│  12:16:10  │ 641.441 MB │
-│  12:16:00  │ 641.441 MB │
-│  12:15:50  │ 767.469 MB │
-│  12:15:40  │ 2.078 GB   │
-│  12:15:30  │ 2.079 GB   │
-│  12:15:20  │ 2.304 GB   │
-│  12:15:10  │ 2.064 GB   │
-│  12:15:00  │ 7.922 GB   │
-│  12:14:50  │ 7.789 GB   │
-│  12:14:40  │ 2.307 GB   │
-│  12:14:30  │ 2.128 GB   │
-│  12:14:20  │ 2.122 GB   │
-│  12:14:10  │ 2.095 GB   │
-│  12:14:00  │ 1.995 GB   │
-│  12:13:50  │ 1.886 GB   │
-│  12:13:40  │ 1.926 GB   │
-│  12:13:30  │ 1.730 GB   │
-│  12:13:20  │ 1.730 GB   │
-│  12:13:10  │ 1.555 GB   │
-│  12:13:00  │ 1.492 GB   │
-│  12:12:50  │ 1.462 GB   │
-│  12:12:40  │ 1.540 GB   │
-│  12:12:30  │ 1.313 GB   │
-│  12:12:20  │ 967.289 MB │
-│  12:12:10  │ 832.797 MB │
-│  12:12:00  │ 1.134 GB   │
-│  12:11:50  │ 1.031 GB   │
-│  12:11:40  │ 958.074 MB │
-│  12:11:30  │ 860.031 MB │
-│  12:11:20  │ 799.848 MB │
-│  12:11:10  │ 740.176 MB │
-│  12:11:00  │ 679.586 MB │
-│  12:10:50  │ 698.457 MB │
-│  12:10:40  │ 689.941 MB │
-│  12:10:30  │ 624.820 MB │
-└────────────┴────────────┘
+
┏━━━━━━━━━━━━┳━━━━━━━━━━━━━┓
+┃ Time (UTC)  Value       ┃
+┡━━━━━━━━━━━━╇━━━━━━━━━━━━━┩
+│  20:06:00  │ 762.426 MB  │
+│  20:05:50  │ 762.641 MB  │
+│  20:05:40  │ 762.480 MB  │
+│  20:05:30  │ 762.254 MB  │
+│  20:05:20  │ 762.715 MB  │
+│  20:05:10  │ 762.723 MB  │
+│  20:05:00  │ 762.324 MB  │
+│  20:04:50  │ 763.070 MB  │
+│  20:04:40  │ 763.223 MB  │
+│  20:04:30  │ 763.422 MB  │
+│  20:04:20  │ 761.316 MB  │
+│  20:04:10  │ 762.250 MB  │
+│  20:04:00  │ 2.107 GB    │
+│  20:03:50  │ 2.333 GB    │
+│  20:03:40  │ 2.331 GB    │
+│  20:03:30  │ 2.088 GB    │
+│  20:03:20  │ 7.882 GB    │
+│  20:03:10  │ 7.881 GB    │
+│  20:03:00  │ 2.226 GB    │
+│  20:02:50  │ 2.130 GB    │
+│  20:02:40  │ 2.002 GB    │
+│  20:02:30  │ 1.984 GB    │
+│  20:02:20  │ 1.888 GB    │
+│  20:02:10  │ 1.780 GB    │
+│  20:02:00  │ 1.836 GB    │
+│  20:01:50  │ 1.726 GB    │
+│  20:01:40  │ 2.015 GB    │
+│  20:01:30  │ 2.011 GB    │
+│  20:01:20  │ 1.464 GB    │
+│  20:01:10  │ 1.628 GB    │
+│  20:01:00  │ 1.635 GB    │
+│  20:00:50  │ 1.554 GB    │
+│  20:00:40  │ 1.322 GB    │
+│  20:00:30  │ 1.098 GB    │
+│  20:00:20  │ 1023.930 MB │
+│  20:00:10  │ 865.840 MB  │
+│  20:00:00  │ 759.039 MB  │
+│  19:59:50  │ 727.934 MB  │
+│  19:59:40  │ 704.547 MB  │
+│  19:59:30  │ 658.090 MB  │
+└────────────┴─────────────┘
 
@@ -143,7 +138,7 @@ FINE_TUNED_SETTINGS = { "hours": 0, "minutes": 0, "stat": "Maximum", - "period": 10, + "period": 60, } response = mw.query_ec2_metrics(**FINE_TUNED_SETTINGS) @@ -153,12 +148,12 @@ response["ResponseMetadata"] - {'RequestId': '20f5d4a7-6baa-4da7-ab28-b77ca796b3bb', + {'RequestId': '3e2aba48-c524-40fb-86af-309506615244', 'HTTPStatusCode': 200, - 'HTTPHeaders': {'x-amzn-requestid': '20f5d4a7-6baa-4da7-ab28-b77ca796b3bb', + 'HTTPHeaders': {'x-amzn-requestid': '3e2aba48-c524-40fb-86af-309506615244', 'content-type': 'text/xml', - 'content-length': '8116', - 'date': 'Thu, 28 Apr 2022 14:06:33 GMT'}, + 'content-length': '1241', + 'date': 'Thu, 28 Apr 2022 20:09:22 GMT'}, 'RetryAttempts': 0} @@ -181,15 +176,13 @@ timed_metric.values[1:10] - [673120256.0, - 673087488.0, - 673087488.0, - 673087488.0, - 673120256.0, - 673128448.0, - 672595968.0, - 672595968.0, - 672051200.0] + [799772672.0, + 2262454272.0, + 8463368192.0, + 2286895104.0, + 2163302400.0, + 1668444160.0, + 763293696.0] diff --git a/docs/EC2_instance_setup.md b/docs/EC2_instance_setup.md index da97055..d12fd9f 100644 --- a/docs/EC2_instance_setup.md +++ b/docs/EC2_instance_setup.md @@ -1,5 +1,9 @@ # EC2 instance setup +!!! info "Using ECS ContainerInsights?" + + **This section may not be required for your setup**. For instance, if you plan to monitor ECS containers that report the metrics with [ECS ContainerInsights](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/deploy-container-insights-ECS-cluster.html). + In order to use the tool a `CloudWatchAgent` process must be running on the EC2 instance to be monitored. Please refer to [this page](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/install-CloudWatch-Agent-commandline-fleet.html) to learn how to install and start the `CloudWatchAgent` on an EC2 instance. diff --git a/docs/README.md b/docs/README.md index 9cba85b..daac31e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,11 @@ # cloudwatcher -`cloudwatcher` is a tool for monitoring [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) metrics and logs in EC2 instances. It can be used both as a command line tool and as a Python library. +`cloudwatcher` is a tool for monitoring [AWS CloudWatch](https://aws.amazon.com/cloudwatch/) metrics and logs. It can be used both as a command line tool and as a Python library. ## Quick start Here are the steps to use `cloudwatcher` as a command line tool: 1. [Install `cloudwatcher` with `pip`](installation.md) -2. [Configure target EC2 instance](EC2_instance_setup.md) -3. [Run `cloudwatcher`](usage.md) +2. [Configure target EC2 instance](EC2_instance_setup.md) (optional if used with ECS ContainerInsights) +3. Run `cloudwatcher` [CLI](usage.md) or [Python API](API_usage.md) diff --git a/docs/changelog.md b/docs/changelog.md index 1bc1afa..f23a669 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -2,6 +2,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) format. +## [0.0.5] - 2022-04-28 + +### Added + +- A possibility to query dimensions other than `InstanceId` via `--dimension-name` option. + +### Removed + +- `-iid/--instance-id` option. Use a combination of `--dimension-name` and `--dimension-value` from now. + ## [0.0.4] - 2022-04-28 ### Added diff --git a/docs/features.md b/docs/features.md index bbcf6fe..f90a798 100644 --- a/docs/features.md +++ b/docs/features.md @@ -50,7 +50,7 @@ Generated when `--save` option is used. A JSON file with the response from the AWS API, useful for debugging. -```json title="{instance_id}_response.json" +```json title="{dimension_name}_{dimension_value}_{metric}_response.json" { "MetricDataResults": [ { @@ -88,7 +88,7 @@ A JSON file with the response from the AWS API, useful for debugging. A JSON file with the raw data, which can be used for further analysis. -```json title="{instance_id}_{metric_label}.json" +```json title="{dimension_name}_{dimension_value}_{metric}.json" { "Label": "mem_used", "Values": [492003328.0, 492204032.0, 492040192.0, 450666496.0, 429965312.0], @@ -107,7 +107,7 @@ A JSON file with the raw data, which can be used for further analysis. A CSV file with the raw data, which can be used for further analysis. -``` title="{instance_id}_{metric_label}.csv" +``` title="{dimension_name}_{dimension_value}_{metric}.csv" time,value 2021-11-12 19:19:00+00:00,492003328.0 2021-11-12 19:18:30+00:00,492204032.0 @@ -122,5 +122,5 @@ Generated when `--plot` option used.
![Memory usage over time](./example_plot.png) -
{instance_id}_{metric_label}.png
+
{dimension_name}_{dimension_value}_{metric}.png
diff --git a/docs/usage.md b/docs/usage.md index 075d877..12a1997 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -8,7 +8,6 @@ cloudwatcher --help ``` ``` -version: 0.0.3 Documentation available at: https://niaid.github.io/cloudwatcher usage: cloudwatcher [-h] {metric,log} ... @@ -37,55 +36,57 @@ clouwatcher metric --help ``` ``` -version: 0.0.3 Documentation available at: https://niaid.github.io/cloudwatcher usage: cloudwatcher metric [-h] [--version] [--debug] [--aws-region R] [--aws-access-key-id K] [--aws-secret-access-key S] [--aws-session-token T] - [--save] [-d DIR] [-q Q] [-i ID] [-m N] -iid ID [--uptime] [--days D] [-hr H] [-mi M] [-u U] [-s S] [-p P] [--plot] + [--save] [-d DIR] [-q Q] [-i ID] [-m N] [-dn N] -dv V [--uptime] [--days D] [-hr H] [-mi M] [-u U] [-s S] [-p P] [--plot] --namespace N Interact with AWS CloudWatch metrics. optional arguments: - -h, --help show this help message and exit - --version Print version and exit - --debug Whether debug mode should be launched (default: False) - --save Whether to save the results to files in the selected directory (default: False) - -d DIR, --dir DIR Directory to store the results in. Used with `--save` (default: ./) - -q Q, --query-json Q Path to a query JSON file. This is not implemented yet. - -i ID, --id ID The unique identifier to assign to the metric data. Must be of the form '^[a-z][a-zA-Z0-9_]*$'. - -m N, --metric N Name of the metric collected by CloudWatchAgent (default: mem_used) - -iid ID, --instance-id ID Instance ID, needs to follow 'i-' format - --uptime Display the uptime of the instance in seconds. It's either calculated precisely if the instance is still running, or - estimated based on the reported metrics. - -u U, --unit U If you omit Unit then all data that was collected with any unit is returned. If you specify a unit, it acts as a filter - and returns only data that was collected with that unit specified. Use 'Bytes' for memory (default: Bytes) - -s S, --stat S The statistic to apply over the time intervals, e.g. 'Maximum' (default: Maximum) - -p P, --period P The granularity, in seconds, of the returned data points. Choices: 1, 5, 10, 30, 60, or any multiple of 60 (default: 60). - It affects the data availability. See the docs 'Usage' section for more details. - --plot Whether to plot the metric data (default: False) - --namespace N Namespace to monitor the metrics within. This value must match the 'Namespace' value in the CloudWatchAgent config. + -h, --help show this help message and exit + --version Print version and exit + --debug Whether debug mode should be launched (default: False) + --save Whether to save the results to files in the selected directory (default: False) + -d DIR, --dir DIR Directory to store the results in. Used with `--save` (default: ./) + -q Q, --query-json Q Path to a query JSON file. This is not implemented yet. + -i ID, --id ID The unique identifier to assign to the metric data. Must be of the form '^[a-z][a-zA-Z0-9_]*$'. + -m N, --metric N Name of the metric collected by CloudWatchAgent (default: mem_used) + -dn N, --dimension-name N The name of the dimension to query. (default: InstanceId) + -dv V, --dimension-value V The value of the dimension to filter on. + --uptime Display the uptime of the instance in seconds. It's either calculated precisely if the instance is still running, or + estimated based on the reported metrics. + -u U, --unit U If you omit Unit then all data that was collected with any unit is returned. If you specify a unit, it acts as a filter + and returns only data that was collected with that unit specified. Use 'Bytes' for memory (default: None) + -s S, --stat S The statistic to apply over the time intervals, e.g. 'Maximum' (default: Maximum) + -p P, --period P The granularity, in seconds, of the returned data points. Choices: 1, 5, 10, 30, 60, or any multiple of 60 (default: + 60). It affects the data availability. See the docs 'Usage' section for more details. + --plot Whether to plot the metric data (default: False) + --namespace N Namespace to monitor the metrics within. This value must match the 'Namespace' value in the CloudWatchAgent config. AWS CREDENTIALS: Can be ommited if set in environment variables - --aws-region R Region to monitor the metrics within. (default: us-east-1) - --aws-access-key-id K AWS Access Key ID to use for authentication - --aws-secret-access-key S AWS Secret Access Key to use for authentication - --aws-session-token T AWS Session Token to use for authentication + --aws-region R Region to monitor the metrics within. (default: us-east-1) + --aws-access-key-id K AWS Access Key ID to use for authentication + --aws-secret-access-key S AWS Secret Access Key to use for authentication + --aws-session-token T AWS Session Token to use for authentication METRIC COLLECTION TIME: The time range to collect metrics from. Uptime will be estimated in the timespan starting at least 15 ago. - --days D How many days to subtract from the current date to determine the metric collection start time (default: 1). - -hr H, --hours H How many hours to subtract from the current time to determine the metric collection start time (default: 0). - -mi M, --minutes M How many minutes to subtract from the current time to determine the metric collection start time (default: 0). + --days D How many days to subtract from the current date to determine the metric collection start time (default: 1). + -hr H, --hours H How many hours to subtract from the current time to determine the metric collection start time (default: 0). + -mi M, --minutes M How many minutes to subtract from the current time to determine the metric collection start time (default: 0). ``` ### Minimal command example +This minimal command will query dimesion `InstanceId` for `mem_user` in `Bytes` with period 60s over last 24h. + ```console -cloudwatcher metric --instance-id i-024a73d6738255cbd +cloudwatcher metric --dimension-value i-024a73d6738255cbd ``` ### Notes on metrics availabilty @@ -106,7 +107,6 @@ clouwatcher log --help ``` ``` -version: 0.0.3 Documentation available at: https://niaid.github.io/cloudwatcher usage: cloudwatcher log [-h] [--version] [--debug] [--aws-region R] [--aws-access-key-id K] [--aws-secret-access-key S] [--aws-session-token T] diff --git a/pyproject.toml b/pyproject.toml index fae28d5..492c076 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "cloudwatcher" -version = "0.0.4" +version = "0.0.5" description = "A tool for monitoring AWS CloudWatch metrics" authors = ["Michal Stolarczyk "] diff --git a/tests/test_version.py b/tests/test_version.py index fe259f1..2e1f556 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -1,8 +1,6 @@ -import pytest - # verify that the version is correct from cloudwatcher._version import __version__ def test_version(): - assert __version__ == "0.0.4" + assert __version__ == "0.0.5"