├── .codecov.yml ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── autoapprove.yml │ ├── automerge.yml │ ├── build_and_test.yml │ ├── build_and_test_release_latest.yml │ └── release_latest.repos ├── .rosinstall.master ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cloudwatch_metrics_collector ├── CMakeLists.txt ├── LICENSE.txt ├── NOTICE.txt ├── config │ └── sample_configuration.yaml ├── include │ └── cloudwatch_metrics_collector │ │ ├── metrics_collector.hpp │ │ └── metrics_collector_parameter_helper.hpp ├── launch │ ├── cloudwatch_metrics_collector.launch │ └── sample_application.launch ├── package.xml ├── src │ ├── main.cpp │ ├── metrics_collector.cpp │ └── metrics_collector_parameter_helper.cpp └── test │ ├── cloudwatch_metrics_collector_test.cpp │ └── test_cloudwatch_metrics_collector.test └── wiki └── images ├── cpu.svg └── memory.svg /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | ignore: 3 | - "**/test/*" 4 | status: 5 | # doc: https://docs.codecov.io/docs/commit-status 6 | project: 7 | default: 8 | # will use the coverage from the base commit (pull request base or parent commit) coverage to compare against. 9 | target: auto 10 | threshold: null 11 | # will use the pull request base if the commit is on a pull request. If not, the parent commit will be used. 12 | base: auto 13 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "16:00" 8 | open-pull-requests-limit: 10 9 | 10 | -------------------------------------------------------------------------------- /.github/workflows/autoapprove.yml: -------------------------------------------------------------------------------- 1 | name: Auto approve 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | # Auto-approve dependabot PRs since this repo requires at least one approving review. 7 | # Dependabot will automatically merge minor version upgrades 8 | # (see .dependabot/config.yml for more info). 9 | auto-approve-dependabot: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: hmarr/auto-approve-action@v2.0.0 13 | if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' 14 | with: 15 | github-token: "${{ secrets.GITHUB_TOKEN }}" -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Auto merge 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | master 7 | pull_request_review: 8 | types: 9 | - submitted 10 | check_suite: 11 | types: 12 | - completed 13 | status: {} 14 | jobs: 15 | # Automatically merge approved and green dependabot PRs. 16 | auto-merge-dependabot: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: pascalgn/automerge-action@v0.13.1 20 | if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' 21 | env: 22 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 23 | MERGE_LABELS: "dependencies" 24 | MERGE_METHOD: "squash" # Sqush and merge 25 | MERGE_COMMIT_MESSAGE: "pull-request-title-and-description" 26 | MERGE_RETRY_SLEEP: "1200000" # Retry after 20m, enough time for check suites to run 27 | UPDATE_RETRIES: "6" 28 | UPDATE_METHOD: "rebase" # Rebase PR on base branch 29 | UPDATE_RETRY_SLEEP: "300000" -------------------------------------------------------------------------------- /.github/workflows/build_and_test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | # Run every hour. This helps detect flakiness, 9 | # and broken external dependencies. 10 | - cron: '0 * * * *' 11 | 12 | jobs: 13 | build_and_test_master: 14 | name: Build and Test Master ROS ${{ matrix.ros_version }} ${{ matrix.ros_distro }} 15 | runs-on: ubuntu-latest 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | ros_distro: [kinetic, melodic] 20 | include: 21 | - ros_distro: kinetic 22 | ubuntu_distro: xenial 23 | - ros_distro: melodic 24 | ubuntu_distro: bionic 25 | container: 26 | image: rostooling/setup-ros-docker:ubuntu-${{ matrix.ubuntu_distro }}-ros-${{ matrix.ros_distro }}-ros-base-latest 27 | env: 28 | # Needed for the CMakeLists.txt setup 29 | ROS_DISTRO: ${{ matrix.ros_distro }} 30 | ROS_VERSION: 1 31 | steps: 32 | # Needed to access the vcs repos file from the workspace 33 | - name: Checkout source 34 | uses: actions/checkout@v2 35 | - name: Run action-ros-ci to build and test 36 | uses: ros-tooling/action-ros-ci@0.1.2 37 | with: 38 | target-ros1-distro: ${{ env.ROS_VERSION == '1' && matrix.ros_distro || '' }} 39 | target-ros2-distro: ${{ env.ROS_VERSION == '2' && matrix.ros_distro || '' }} 40 | package-name: cloudwatch_metrics_collector 41 | vcs-repo-file-url: '' 42 | - name: Upload resulting colcon logs 43 | uses: actions/upload-artifact@v2.2.2 44 | with: 45 | name: colcon-logs-${{ matrix.ubuntu_distro }}-ros-${{ matrix.ros_distro }} 46 | path: ros_ws/log 47 | log_workflow_status_to_cloudwatch: 48 | runs-on: ubuntu-latest 49 | container: 50 | image: ubuntu:bionic 51 | needs: 52 | - build_and_test_master 53 | # Don't skip if prior steps failed, but don't run on a fork because it won't have access to AWS secrets 54 | if: ${{ always() && ! github.event.repository.fork && ! github.event.pull_request.head.repo.fork }} 55 | steps: 56 | - name: Configure AWS Credentials 57 | uses: aws-actions/configure-aws-credentials@v1 58 | with: 59 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 60 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 61 | aws-region: ${{ secrets.AWS_REGION }} 62 | - uses: ros-tooling/action-cloudwatch-metrics@0.0.5 63 | with: 64 | # Checks if any of the jobs have failed. 65 | # 66 | # needs.*.result is returns the list of all success statuses as an 67 | # array, i.e. ['success', 'failure, 'success'] 68 | # join() converts the array to a string 'successfailuresuccess' 69 | # contains() checks whether the string contains failure 70 | metric-value: ${{ ! contains(join(needs.*.result, ''), 'failure') && ! contains(join(needs.*.result, ''), 'cancelled') }} 71 | -------------------------------------------------------------------------------- /.github/workflows/build_and_test_release_latest.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test release-latest 2 | on: 3 | schedule: 4 | # Run every hour. This helps detect flakiness, 5 | # and broken external dependencies. 6 | - cron: '0 * * * *' 7 | 8 | jobs: 9 | build_and_test_release_latest: 10 | name: Build and Test Release Latest ROS ${{ matrix.ros_version }} ${{ matrix.ros_distro }} 11 | runs-on: ubuntu-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | ros_distro: [kinetic, melodic] 16 | include: 17 | - ros_distro: kinetic 18 | ubuntu_distro: xenial 19 | - ros_distro: melodic 20 | ubuntu_distro: bionic 21 | container: 22 | image: rostooling/setup-ros-docker:ubuntu-${{ matrix.ubuntu_distro }}-ros-${{ matrix.ros_distro }}-ros-base-latest 23 | env: 24 | # Needed for the CMakeLists.txt setup 25 | ROS_DISTRO: ${{ matrix.ros_distro }} 26 | ROS_VERSION: 1 27 | steps: 28 | # Needed to access the vcs repos file from the workspace 29 | - name: Checkout source 30 | uses: actions/checkout@v2 31 | - name: Run action-ros-ci to build and test 32 | uses: ros-tooling/action-ros-ci@0.1.2 33 | with: 34 | target-ros1-distro: ${{ env.ROS_VERSION == '1' && matrix.ros_distro || '' }} 35 | target-ros2-distro: ${{ env.ROS_VERSION == '2' && matrix.ros_distro || '' }} 36 | package-name: cloudwatch_metrics_collector 37 | # schedule runs against the default branch (master), so specify release-latest via repos file 38 | vcs-repo-file-url: "${{ github.workspace }}/.github/workflows/release_latest.repos" 39 | - name: Upload resulting colcon logs 40 | uses: actions/upload-artifact@v2.2.2 41 | with: 42 | name: colcon-logs-${{ matrix.ubuntu_distro }}-ros-${{ matrix.ros_distro }} 43 | path: ros_ws/log 44 | log_workflow_status_to_cloudwatch: 45 | runs-on: ubuntu-latest 46 | container: 47 | image: ubuntu:bionic 48 | needs: 49 | - build_and_test_release_latest 50 | # Don't skip if prior steps failed, but don't run on a fork because it won't have access to AWS secrets 51 | if: ${{ always() && ! github.event.repository.fork && ! github.event.pull_request.head.repo.fork }} 52 | steps: 53 | - name: Configure AWS Credentials 54 | uses: aws-actions/configure-aws-credentials@v1 55 | with: 56 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 57 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 58 | aws-region: ${{ secrets.AWS_REGION }} 59 | - uses: ros-tooling/action-cloudwatch-metrics@0.0.5 60 | with: 61 | metric-dimensions: >- 62 | [ 63 | { "Name": "github.event_name", "Value": "${{ github.event_name }}" }, 64 | { "Name": "github.ref", "Value": "release-latest" }, 65 | { "Name": "github.repository", "Value": "${{ github.repository }}" } 66 | ] 67 | # Checks if any of the jobs have failed. 68 | # 69 | # needs.*.result is returns the list of all success statuses as an 70 | # array, i.e. ['success', 'failure, 'success'] 71 | # join() converts the array to a string 'successfailuresuccess' 72 | # contains() checks whether the string contains failure 73 | metric-value: ${{ ! contains(join(needs.*.result, ''), 'failure') && ! contains(join(needs.*.result, ''), 'cancelled') }} 74 | -------------------------------------------------------------------------------- /.github/workflows/release_latest.repos: -------------------------------------------------------------------------------- 1 | repositories: 2 | cloudwatchmetrics-ros1: 3 | type: git 4 | url: https://github.com/aws-robotics/cloudwatchmetrics-ros1 5 | version: release-latest 6 | -------------------------------------------------------------------------------- /.rosinstall.master: -------------------------------------------------------------------------------- 1 | - git: 2 | uri: https://github.com/aws-robotics/utils-common.git 3 | local-name: utils-common 4 | version: master 5 | - git: 6 | uri: https://github.com/aws-robotics/utils-ros1.git 7 | local-name: utils-ros1 8 | version: master 9 | - git: 10 | uri: https://github.com/aws-robotics/cloudwatch-common.git 11 | local-name: cloudwatch-common 12 | version: master 13 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-robotics/cloudwatchmetrics-ros1/issues), or [recently closed](https://github.com/aws-robotics/cloudwatchmetrics-ros1/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-robotics/cloudwatchmetrics-ros1/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-robotics/cloudwatchmetrics-ros1/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cloudwatch_metrics_collector 2 | 3 | 4 | ## Overview 5 | The `cloudwatch_metrics_collector` ROS Node publishes your robot's metrics to the cloud to enable you to easily track the health of a fleet with the use of automated monitoring and actions for when a robot's metrics show abnormalities. You can easily track historic trends and profile behavior such as resource usage. Out of the box it provides a ROS interface to take a ROS message defining a metric and publish it to Amazon CloudWatch Metrics. The only configuration required to get started is setting up AWS Credentials and Permissions for your robot.The CloudWatch Metrics Node can be used with existing ROS Nodes that publish metric messages or with your own nodes if you instrument them to publish your own custom metrics. 6 | 7 | **Amazon CloudWatch Metrics Summary**: Amazon CloudWatch is a monitoring and management service built for developers, system operators, site reliability engineers (SRE), and IT managers. CloudWatch provides you with data and actionable insights to monitor your applications, understand and respond to system-wide performance changes, optimize resource utilization, and get a unified view of operational health. CloudWatch collects monitoring and operational data in the form of logs, metrics, and events, providing you with a unified view of AWS resources, applications and services that run on AWS, and on-premises servers. You can use CloudWatch to set high resolution alarms, visualize logs and metrics side by side, take automated actions, troubleshoot issues, and discover insights to optimize your applications, and ensure they are running smoothly. 8 | 9 | **Keywords**: ROS, AWS, CloudWatch, Metrics 10 | 11 | ### License 12 | The source code is released under an [Apache 2.0]. 13 | 14 | **Author**: AWS RoboMaker
15 | **Affiliation**: [Amazon Web Services (AWS)]
16 | 17 | RoboMaker cloud extensions rely on third-party software licensed under open-source licenses and are provided for demonstration purposes only. Incorporation or use of RoboMaker cloud extensions in connection with your production workloads or commercial product(s) or devices may affect your legal rights or obligations under the applicable open-source licenses. License information for this repository can be found [here](https://github.com/aws-robotics/cloudwatchmetrics-ros1/blob/master/LICENSE). AWS does not provide support for this cloud extension. You are solely responsible for how you configure, deploy, and maintain this cloud extension in your workloads or commercial product(s) or devices. 18 | 19 | ### Supported ROS Distributions 20 | - Kinetic 21 | - Melodic 22 | 23 | ## Installation 24 | 25 | ### AWS Credentials 26 | You will need to create an AWS Account and configure the credentials to be able to communicate with AWS services. You may find [AWS Configuration and Credential Files] helpful. Specifying AWS [credentials by setting environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-environment.html) is not supported. 27 | 28 | This node will require the following AWS account IAM role permissions: 29 | - `cloudwatch:PutMetricData` 30 | 31 | ### Building from Source 32 | 33 | To build from source you'll need to create a new workspace, clone and checkout the latest release branch of this repository, install all the dependencies, and compile. If you need the latest development features you can clone from the `master` branch instead of the latest release branch. While we guarantee the release branches are stable, __the `master` should be considered to have an unstable build__ due to ongoing development. 34 | 35 | - Install build tool: please refer to `colcon` [installation guide](https://colcon.readthedocs.io/en/released/user/installation.html) 36 | 37 | - Create a ROS workspace and a source directory 38 | 39 | mkdir -p ~/ros-workspace/src 40 | 41 | - Clone the package into the source directory . 42 | 43 | cd ~/ros-workspace/src 44 | git clone https://github.com/aws-robotics/cloudwatchmetrics-ros1.git -b release-latest 45 | 46 | - Install dependencies 47 | 48 | cd ~/ros-workspace 49 | sudo apt-get update && rosdep update 50 | rosdep install --from-paths src --ignore-src -r -y 51 | 52 | _Note: If building the master branch instead of a release branch you may need to also checkout and build the master branches of the packages this package depends on._ 53 | 54 | - Build the packages 55 | 56 | cd ~/ros-workspace && colcon build 57 | 58 | - Configure ROS library Path 59 | 60 | source ~/ros-workspace/install/setup.bash 61 | 62 | - Build and run the unit tests 63 | 64 | colcon build --packages-select cloudwatch_metrics_collector --cmake-target tests 65 | colcon test --packages-select cloudwatch_metrics_collector cloudwatch_metrics_common && colcon test-result --all 66 | 67 | 68 | ## Launch Files 69 | In order to include a `cloudwatch_metrics_collector` in your launch file, you should add `` to your launch file. The launch file uses the following arguments: 70 | 71 | | Arg Name | Description | 72 | | --------- | ------------ | 73 | | node_name | (optional) The name the metrics node should be launched with. If not provided the node will default to "cloudwatch_metrics_collector" | 74 | | config_file | (optional) A path to a rosparam config file. If provided the launch file will use rosparam to load the configuration into the private namespace of the node. | 75 | 76 | An example launch file called `sample_application.launch` is included in this project that gives an example of how you can include this node in your project and provide it with arguments. 77 | 78 | 79 | ## Usage 80 | 81 | ### Run the node 82 | - **With** launch file using parameters in .yaml format (example provided) 83 | - ROS: `roslaunch cloudwatch_metrics_collector sample_application.launch --screen` 84 | 85 | ### Send a test metric 86 | - `rostopic pub /metrics ros_monitoring_msgs/MetricList '[{header: auto, metric_name: "ExampleMetric", unit: "sec", value: 42, time_stamp: now, dimensions: []}, {header: auto, metric_name: "ExampleMetric2", unit: "count", value: 22, time_stamp: now, dimensions: [{name: "ExampleDimension", value: "1"}]}]'` 87 | 88 | 89 | ## Configuration File and Parameters 90 | An example configuration file named `sample_configuration.yaml` is provided that contains a detailed example configuration for the Node. 91 | 92 | All parameters to the Node are provided via the parameter server when the node is started. When loading configuration the Node will start looking for each setting inside of its private namespace and then search up the namespace heirarchy to the global namespace for the parameters. 93 | 94 | | Parameter Name | Description | Type | Default | 95 | | ------------- | -----------------------------------------------------------| ------------- | ------------ | 96 | | aws_metrics_namespace | If provided it will set the namespace for all metrics provided by this node to the provided value. If the node is running on AWS RoboMaker then the provided launch file will ignore this parameter in favor of the namespace specified by the AWS RoboMaker ecosystem | *string* | ROS | 97 | | aws_monitored_metric_topics | An optional list of topics to listen to. If not provided or is empty the node will just listen on the global "metrics" topic. If this list is not empty then the node will not subscribe to the "metrics" topic and will only subscribe to the topics in the list. | *array of strings* | metrics | 98 | | storage_directory | The location where all offline metrics will be stored | *string* | ~/.ros/cwmetrics/ | 99 | | storage_limit | The maximum size of all offline storage files in KB. Once this limit is reached offline logs will start to be deleted oldest first. | *int* | 1048576 | 100 | | aws_client_configuration | If given the node will load the provided configuration when initializing the client. If a specific configuration setting is not included in the map then it will search up the namespace hierarchy for an 'aws_client_configuration' map that contains the field. In this way, a global configuration can be provided for all AWS nodes with only specific values overridden for a specific Node instance if needed | *map* | | 101 | | storage_resolution | The storage resolution level for presenting metrics in CloudWatch. For more information, see [high-resolution-metrics](http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html). | *int* | 60 | 102 | 103 | 104 | ### Advanced Configuration Parameters 105 | Most users won't need to touch these parameters, they are useful if you want fine grained control over how your metrics are stored offline and uploaded to CloudWatch. 106 | 107 | | Parameter Name | Description | Type | Default | 108 | | ------------- | -----------------------------------------------------------| ------------- | ------------ | 109 | | batch_max_queue_size | The maximum number metrics items to add to the CloudWatch upload queue before they start to be written to disk | *int* | 1024 | 110 | | batch_trigger_publish_size | Only publish metrics to CloudWatch when there are this many items in the queue. When this is set the publishing of metrics on a constant timer is disabled. This must be smaller than batch_max_queue_size. Metrics uploaded from offline storage are not affected by this. | *int* | *unset* | 111 | | file_max_queue_size | The max number of batches in the queue, each of size file_upload_batch_size, when reading and uploading from offline storage files | *int* | 5 | 112 | | file_upload_batch_size | The size of each batch of metrics in the queue, when reading and uploading from offline storage files | *int* | 50 | 113 | | file_prefix | A prefix to add to each offline storage file so they're easier to identify later | *string* | cwmetric | 114 | | file_extension | The extension for all offline storage files | *string* | .log | 115 | | maximum_file_size | The maximum size each offline storage file in KB | *int* | 1024 | 116 | | stream_max_queue_size | The maximum number of batches in the queue to stream to CloudWatch. If this queue is full subsequent batches of metrics will be written to disk. | *int* | 3 | 117 | 118 | 119 | ## Node 120 | 121 | ### cloudwatch_metrics_collector 122 | Sends metrics in a ROS system to AWS CloudWatch Metrics service. 123 | 124 | #### Subscribed Topics 125 | - **`Configurable (default="/metrics")`** 126 | 127 | The node can subscribe to a configurable list of topic names. If no list in provided then it will default to subscribing to a global topic names `/metrics`.
128 | Message Type: `ros_monitoring_msgs/MetricList` 129 | 130 | #### Published Topics 131 | None 132 | 133 | #### Services 134 | None 135 | 136 | [Amazon Web Services (AWS)]: https://aws.amazon.com/ 137 | [Apache 2.0]: https://aws.amazon.com/apache-2-0/ 138 | [AWS Configuration and Credential Files]: https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html 139 | [high-resolution-metrics]: https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics 140 | [Issue Tracker]: https://github.com/aws-robotics/cloudwatchmetrics-ros1/issues 141 | [ROS]: http://www.ros.org 142 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(cloudwatch_metrics_collector) 3 | 4 | ## Compile as C++11, supported in ROS Kinetic and newer 5 | set(CMAKE_CXX_STANDARD 14) 6 | 7 | # Enable strict compiler flags if possible. 8 | include(CheckCXXCompilerFlag) 9 | # Removed -Wmissing-declarations until gmock is ignored 10 | # removed -Werror=pedantic until ros.h is fixed 11 | set(FLAGS -Wno-long-long -Wall -Wextra -Wcast-align -Wcast-qual -Wformat -Wwrite-strings) 12 | foreach(FLAG ${FLAGS}) 13 | check_cxx_compiler_flag(${FLAG} R${FLAG}) 14 | if(${R${FLAG}}) 15 | set(WARNING_CXX_FLAGS "${WARNING_CXX_FLAGS} ${FLAG}") 16 | endif() 17 | endforeach() 18 | 19 | if(NOT DEFINED CXX_DISABLE_WERROR) 20 | set(WARNING_CXX_FLAGS "-Werror ${WARNING_CXX_FLAGS}") 21 | endif() 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_CXX_FLAGS}") 23 | 24 | ## Compile as C++11, supported in ROS Kinetic and newer 25 | add_compile_options(-std=c++11) 26 | 27 | find_package(dataflow_lite REQUIRED) 28 | find_package(file_management REQUIRED) 29 | find_package(cloudwatch_metrics_common REQUIRED) 30 | 31 | ## Find catkin macros and libraries 32 | find_package(catkin REQUIRED COMPONENTS 33 | cloudwatch_metrics_common 34 | roscpp 35 | aws_common 36 | aws_ros1_common 37 | rosgraph_msgs 38 | ros_monitoring_msgs 39 | std_srvs 40 | std_msgs) 41 | 42 | ################################### 43 | ## catkin specific configuration ## 44 | ################################### 45 | catkin_package() 46 | 47 | ########### 48 | ## Build ## 49 | ########### 50 | 51 | ## Specify additional locations of header files 52 | include_directories(${catkin_INCLUDE_DIRS}) 53 | 54 | ## Declare a C++ executable 55 | add_library(${PROJECT_NAME}_lib SHARED src/metrics_collector.cpp src/metrics_collector_parameter_helper.cpp) 56 | 57 | target_include_directories(${PROJECT_NAME}_lib PUBLIC include ${catkin_INCLUDE_DIRS} ${dataflow_lite_INCLUDE_DIRS}) 58 | target_link_libraries(${PROJECT_NAME}_lib ${catkin_LIBRARIES} ${dataflow_lite_common_LIBRARIES}) 59 | 60 | target_include_directories(${PROJECT_NAME}_lib PUBLIC include ${catkin_INCLUDE_DIRS} ${file_management_INCLUDE_DIRS}) 61 | target_link_libraries(${PROJECT_NAME}_lib ${catkin_LIBRARIES} ${file_management_LIBRARIES}) 62 | 63 | target_include_directories(${PROJECT_NAME}_lib PUBLIC include ${catkin_INCLUDE_DIRS} ${cloudwatch_metrics_common_INCLUDE_DIRS}) 64 | target_link_libraries(${PROJECT_NAME}_lib ${catkin_LIBRARIES} ${cloudwatch_metrics_common_LIBRARIES}) 65 | 66 | add_executable(${PROJECT_NAME} src/main.cpp) 67 | target_include_directories(${PROJECT_NAME} PRIVATE include ${catkin_INCLUDE_DIRS} ${cloudwatch_metrics_common_INCLUDE_DIRS}) 68 | target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}_lib ${cloudwatch_metrics_common_LIBRARIES}) 69 | 70 | ############# 71 | ## Install ## 72 | ############# 73 | 74 | ## Mark executables and/or libraries for installation 75 | install(TARGETS ${PROJECT_NAME} 76 | ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 77 | LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 78 | RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 79 | ) 80 | 81 | install(TARGETS ${PROJECT_NAME}_lib 82 | ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 83 | LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION} 84 | RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 85 | ) 86 | 87 | install(DIRECTORY 88 | config 89 | launch 90 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 91 | ) 92 | 93 | ############# 94 | ## Testing ## 95 | ############# 96 | 97 | ## Add gtest based cpp test target and link libraries 98 | if(CATKIN_ENABLE_TESTING) 99 | set(CW_METRICS_COLLECTOR_TEST test_cloudwatch_metrics_collector) 100 | find_package(rostest REQUIRED) 101 | find_package(GMock QUIET) 102 | if(GMOCK_FOUND) 103 | add_rostest_gmock(${CW_METRICS_COLLECTOR_TEST} 104 | test/test_cloudwatch_metrics_collector.test 105 | test/cloudwatch_metrics_collector_test.cpp 106 | ) 107 | target_include_directories(${CW_METRICS_COLLECTOR_TEST} PRIVATE 108 | include 109 | ${catkin_INCLUDE_DIRS} 110 | ${cloudwatch_metrics_collector_INCLUDE_DIRS} 111 | ${GMOCK_INCLUDE_DIRS} 112 | ) 113 | target_link_libraries(${CW_METRICS_COLLECTOR_TEST} 114 | ${PROJECT_NAME}_lib 115 | ${catkin_LIBRARIES} 116 | ${GMOCK_BOTH_LIBRARIES} 117 | ) 118 | else() 119 | include_directories(/usr/include/gmock /usr/src/gmock) 120 | add_library(${PROJECT_NAME}_libgmock SHARED /usr/src/gmock/src/gmock-all.cc) 121 | 122 | add_rostest_gtest(${CW_METRICS_COLLECTOR_TEST} 123 | test/test_cloudwatch_metrics_collector.test 124 | test/cloudwatch_metrics_collector_test.cpp 125 | ) 126 | target_include_directories(${CW_METRICS_COLLECTOR_TEST} PRIVATE 127 | include 128 | ${catkin_INCLUDE_DIRS} 129 | ${cloudwatch_metrics_collector_INCLUDE_DIRS} 130 | ) 131 | target_link_libraries(${CW_METRICS_COLLECTOR_TEST} 132 | ${PROJECT_NAME}_lib 133 | ${catkin_LIBRARIES} 134 | ${PROJECT_NAME}_libgmock 135 | ) 136 | endif() 137 | endif() 138 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2018 Amazon.com, Inc. or its affiliates 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/NOTICE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | This product includes software developed by 4 | Amazon Technologies, Inc (http://www.amazon.com/). 5 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/config/sample_configuration.yaml: -------------------------------------------------------------------------------- 1 | # An optional metric namespace parameter. If provided it will set the namespace for all metrics provided by this node to 2 | # the provided value. If the node is running on AWS RoboMaker, then the provided launch file will ignore this parameter in 3 | # favor of the namespace specified by the AWS RoboMaker ecosystem 4 | aws_metrics_namespace: "ros_metrics_collector" 5 | 6 | # An optional list of topics to listen to. If not provided or is empty the node will just listen on the global "metrics" 7 | # topic. If this list is not empty then the node will not subscribe to the "metrics" topic and will only subscribe to the 8 | # topics in the list. 9 | #aws_monitored_metric_topics: ["metrics"] 10 | 11 | # The absolute path to a folder that all offline metric files will be stored in 12 | storage_directory: "~/.ros/cwmetrics/" 13 | 14 | # The maximum size of all files in offline storage in KB 15 | storage_limit: 1048576 16 | 17 | # This is the AWS Client Configuration used by the AWS service client in the Node. If given the node will load the 18 | # provided configuration when initializing the client. 19 | aws_client_configuration: 20 | # Specifies where you want the client to communicate. Examples include us-east-1 or us-west-1. You must ensure that 21 | # the service you want to use has an endpoint in the region you configure. 22 | region: "us-west-2" 23 | 24 | # Built in the constructor and pulls information from your operating system. Do not alter the user agent. 25 | #user_agent: "" 26 | 27 | # Use this to override the http endpoint used to talk to a service. If you set this, you must also set authenticationRegion. 28 | #endpoint_override: "" 29 | 30 | # These settings allow you to configure a proxy for all communication with AWS. Examples of when this functionality 31 | # might be useful include debugging in conjunction with the Burp suite, or using a proxy to connect to the Internet. 32 | #proxy_host: "" 33 | #proxy_port: 0 34 | #proxy_user_name: "" 35 | #proxy_password: "" 36 | 37 | # Enables you to tell the HTTP client where to find your SSL certificate trust store (for example, a directory 38 | # prepared with OpenSSL's c_rehash utility). You shouldn't need to do this unless you are using symlinks in your 39 | # environment. This has no effect on Windows or OS X. 40 | #ca_path: "" 41 | #ca_file: "" 42 | 43 | # Values that determine the length of time, in milliseconds, to wait before timing out a request. You can increase 44 | # this value if you need to transfer large files, such as in Amazon S3 or Amazon CloudFront. 45 | request_timeout_ms: 2000 46 | connect_timeout_ms: 2000 47 | max_retries: 1 48 | no_retry_strategy: true 49 | 50 | # The maximum number of allowed connections to a single server for your HTTP communications. The default value is 25. 51 | # You can set this value as high as you can support the bandwidth. We recommend a value around 25. 52 | #max_connections: 25 53 | 54 | # Use dual stack endpoint in the endpoint calculation. You must ensure that the service you want to use supports ipv6 in the region you select. 55 | #use_dual_stack: false 56 | 57 | # Enable adjustment for clock skew 58 | #enable_clock_skew_adjustment: true 59 | 60 | # If set to true the http stack will follow redirect codes 61 | #follow_redirects: false 62 | 63 | # Specifies whether to enable SSL certificate verification. If necessary, you can disable SSL certificate verification by setting verifySSL to false. 64 | #verify_SSL: true 65 | 66 | # This is the storage resolution level for presenting metrics in CloudWatch. 67 | # Valid values are 1 and 60. Setting this to 1 specifies this metric as a 68 | # high-resolution metric, so that CloudWatch stores the metric with sub-minute 69 | # resolution down to one second. Setting this to 60 specifies this metric as a 70 | # regular-resolution metric, which CloudWatch stores at 1-minute resolution. 71 | # Currently, high resolution is available only for custom metrics. For more 72 | # information about high-resolution metrics, see http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html 73 | storage_resolution: 60 74 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/include/cloudwatch_metrics_collector/metrics_collector.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | namespace Aws { 32 | namespace CloudWatchMetrics { 33 | namespace Utils { 34 | 35 | class MetricsCollector : public Service 36 | { 37 | public: 38 | 39 | MetricsCollector() = default; 40 | ~MetricsCollector() override = default; 41 | 42 | /** 43 | * Accept input metric message to be batched for publishing. 44 | * 45 | * @param metric_list_msg 46 | * @return the number of metrics successfully batched 47 | */ 48 | int RecordMetrics(const ros_monitoring_msgs::MetricList::ConstPtr & metric_list_msg); 49 | 50 | /** 51 | * Force all batched data to be published to CloudWatch. 52 | */ 53 | void TriggerPublish(const ros::TimerEvent &); 54 | 55 | /** 56 | * Initialize the MetricsCollector with parameters read from the config file. 57 | * 58 | * @param metric_namespace 59 | * @param default_dimensions 60 | * @param storage_resolution 61 | * @param config 62 | * @param sdk_options 63 | * @param metric_service_factory 64 | */ 65 | void Initialize(std::string metric_namespace, 66 | std::map & default_dimensions, 67 | int storage_resolution, 68 | const ros::NodeHandle& node_handle, 69 | const Aws::Client::ClientConfiguration & config, 70 | const Aws::SDKOptions & sdk_options, 71 | const Aws::CloudWatchMetrics::CloudWatchOptions & cloudwatch_options, 72 | const std::shared_ptr& metric_service_factory = std::make_shared()); 73 | 74 | void SubscribeAllTopics(); 75 | 76 | bool start() override; 77 | bool shutdown() override; 78 | 79 | /** 80 | * Return a Trigger response detailing the MetricService online status. 81 | * 82 | * @param request input request 83 | * @param response output response 84 | * @return true if the request was handled successfully, false otherwise 85 | */ 86 | bool checkIfOnline(std_srvs::Trigger::Request& request, std_srvs::Trigger::Response& response); 87 | 88 | /** 89 | * Gets the timestamp for the input metric message as milliseconds since epoch 90 | */ 91 | static int64_t GetMetricDataEpochMillis(const ros_monitoring_msgs::MetricData & metric_msg); 92 | 93 | private: 94 | 95 | std::string metric_namespace_; 96 | std::map default_dimensions_; 97 | std::atomic storage_resolution_{}; 98 | std::shared_ptr metric_service_; 99 | std::vector subscriptions_; 100 | ros::NodeHandle node_handle_; 101 | std::vector topics_; 102 | }; 103 | 104 | } // namespace Utils 105 | } // namespace CloudWatchMetrics 106 | } // namespace Aws 107 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/include/cloudwatch_metrics_collector/metrics_collector_parameter_helper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | #pragma once 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | 26 | namespace Aws { 27 | namespace CloudWatchMetrics { 28 | namespace Utils { 29 | 30 | const std::string kNodeParamMonitorTopicsListKey("aws_monitored_metric_topics"); 31 | const std::string kNodeParamMetricNamespaceKey = "aws_metrics_namespace"; 32 | const std::string kNodeParamDefaultMetricDimensionsKey = "aws_default_metric_dimensions"; 33 | const std::string kNodeName = "cloudwatch_metrics_collector"; 34 | const char kNodeParamPublishFrequencyKey[] = "publish_frequency"; 35 | 36 | /** Configuration params for Aws::DataFlow::UploaderOptions **/ 37 | constexpr char kNodeParamBatchMaxQueueSize[] = "batch_max_queue_size"; 38 | constexpr char kNodeParamBatchTriggerPublishSize[] = "batch_trigger_publish_size"; 39 | constexpr char kNodeParamFileMaxQueueSize[] = "file_max_queue_size"; 40 | constexpr char kNodeParamFileUploadBatchSize[] = "file_upload_batch_size"; 41 | constexpr char kNodeParamStreamMaxQueueSize[] = "stream_max_queue_size"; 42 | 43 | /** Configuration params for Aws::FileManagement::FileManagerStrategyOptions **/ 44 | constexpr char kNodeParamFileExtension[] = "file_extension"; 45 | constexpr char kNodeParamFilePrefix[] = "file_prefix"; 46 | constexpr char kNodeParamMaximumFileSize[] = "maximum_file_size"; 47 | constexpr char kNodeParamStorageDirectory[] = "storage_directory"; 48 | constexpr char kNodeParamStorageLimit[] = "storage_limit"; 49 | 50 | constexpr int kNodeSubQueueSize = 100; 51 | constexpr int kNodePublishFrequencyDefaultValue = 10; 52 | const int kNodeMetricServiceTimeSec = 1; 53 | const std::string kNodeDefaultMetricNamespace = "ROS"; 54 | const std::string kNodeDefaulMetricsTopic = "metrics"; 55 | constexpr int kNodeDefaultMetricDatumStorageResolution = 60; 56 | const std::string kNodeParamMetricDatumStorageResolutionKey = "storage_resolution"; 57 | const std::set kNodeParamMetricDatumStorageResolutionValidValues = {1, 60}; 58 | 59 | 60 | /** 61 | * Fetch the parameter for the log publishing frequency. 62 | * 63 | * @param parameter_reader to retrieve the parameters from. 64 | * @param publish_frequency the parameter is stored here when it is read successfully. 65 | * @return an error code that indicates whether the parameter was read successfully or not, 66 | * as returned by \p parameter_reader 67 | */ 68 | void ReadPublishFrequency( 69 | const std::shared_ptr& parameter_reader, 70 | double & publish_frequency); 71 | 72 | /** 73 | * 74 | * @param parameter_reader 75 | * @param metric_namespace 76 | * @return 77 | */ 78 | void ReadMetricNamespace( 79 | const std::shared_ptr& parameter_reader, 80 | std::string & metric_namespace); 81 | 82 | /** 83 | * 84 | * @param parameter_reader 85 | * @param dimensions_param 86 | * @param metric_dims 87 | * @return 88 | */ 89 | void ReadMetricDimensions( 90 | const std::shared_ptr& parameter_reader, 91 | Aws::String & dimensions_param, 92 | std::map & metric_dims); 93 | /** 94 | * 95 | * @param parameter_reader 96 | * @param storage_resolution 97 | * @return 98 | */ 99 | void ReadStorageResolution( 100 | const std::shared_ptr& parameter_reader, 101 | int & storage_resolution); 102 | 103 | void ReadTopics(std::vector & topics); 104 | 105 | /** 106 | * Fetch the options related to cloudwatch uploading and offline file mangement 107 | * 108 | * @param parameter_reader to retrieve the parameters from 109 | * @param cloudwatch_options a struct of uploader and file_manager options 110 | * @return an error code that indicates whether the parameter was read successfully or not, 111 | * as returned by \p parameter_reader 112 | */ 113 | void ReadCloudWatchOptions( 114 | const std::shared_ptr& parameter_reader, 115 | Aws::CloudWatchMetrics::CloudWatchOptions & cloudwatch_options); 116 | 117 | /** 118 | * Fetch the options related to cloudwatch log uploading 119 | * 120 | * @param parameter_reader to retrieve the parameters from 121 | * @param uploader_options a struct of uploader options 122 | * @return an error code that indicates whether the parameter was read successfully or not, 123 | * as returned by \p parameter_reader 124 | */ 125 | void ReadUploaderOptions( 126 | const std::shared_ptr& parameter_reader, 127 | Aws::DataFlow::UploaderOptions & uploader_options); 128 | 129 | /** 130 | * Fetch the options related to cloudwatch offline file management 131 | * 132 | * @param parameter_reader to retrieve the parameters from 133 | * @param file_manager_strategy_options a struct of file management options 134 | * @return an error code that indicates whether the parameter was read successfully or not, 135 | * as returned by \p parameter_reader 136 | */ 137 | void ReadFileManagerStrategyOptions( 138 | const std::shared_ptr& parameter_reader, 139 | Aws::FileManagement::FileManagerStrategyOptions & file_manager_strategy_options); 140 | 141 | /** 142 | * Fetch a single string option 143 | * 144 | * @param parameter_reader to retrieve the parameters from 145 | * @param option_key the parameter key to read 146 | * @param default_value a default value if the parameter doesn't exist or is unreadble 147 | * @param option_value the string value for this option 148 | * @return an error code that indicates whether the parameter was read successfully or not, 149 | * as returned by \p parameter_reader 150 | */ 151 | void ReadOption( 152 | const std::shared_ptr& parameter_reader, 153 | const std::string & option_key, 154 | const std::string & default_value, 155 | std::string & option_value); 156 | 157 | /** 158 | * Fetch a single size_t option 159 | * 160 | * @param parameter_reader to retrieve the parameters from 161 | * @param option_key the parameter key to read 162 | * @param default_value a default value if the parameter doesn't exist or is unreadble 163 | * @param option_value the size_t value for this option 164 | * @return an error code that indicates whether the parameter was read successfully or not, 165 | * as returned by \p parameter_reader 166 | */ 167 | void ReadOption( 168 | const std::shared_ptr& parameter_reader, 169 | const std::string & option_key, 170 | const size_t & default_value, 171 | size_t & option_value); 172 | 173 | } // namespace Utils 174 | } // namespace CloudWatchMetrics 175 | } // namespace Aws 176 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/launch/cloudwatch_metrics_collector.launch: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/launch/sample_application.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cloudwatch_metrics_collector 4 | 2.2.1 5 | Subscriber node for the aws/monitoring topic to publish metrics to AWS Cloudwatch 6 | http://wiki.ros.org/cloudwatch_metrics_collector 7 | 8 | AWS RoboMaker 9 | AWS RoboMaker 10 | 11 | Apache 2.0 12 | 13 | catkin 14 | 15 | ros_monitoring_msgs 16 | cloudwatch_metrics_common 17 | roscpp 18 | aws_common 19 | aws_ros1_common 20 | std_srvs 21 | std_msgs 22 | 23 | cloudwatch_metrics_common 24 | aws_common 25 | aws_ros1_common 26 | roscpp 27 | std_srvs 28 | std_msgs 29 | ros_monitoring_msgs 30 | 31 | cloudwatch_metrics_common 32 | aws_common 33 | aws_ros1_common 34 | roscpp 35 | std_srvs 36 | std_msgs 37 | ros_monitoring_msgs 38 | 39 | gtest 40 | google-mock 41 | rostest 42 | 43 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | 42 | #include 43 | #include 44 | 45 | using Aws::CloudWatchMetrics::Utils::kNodeName; 46 | using Aws::CloudWatchMetrics::Utils::kNodeDefaultMetricDatumStorageResolution; 47 | using Aws::CloudWatchMetrics::Utils::MetricsCollector; 48 | 49 | int main(int argc, char * argv[]) 50 | { 51 | int status = 0; 52 | ros::init(argc, argv, kNodeName); 53 | ros::NodeHandle node_handle; 54 | 55 | std::vector subscriptions; 56 | 57 | // initialize SDK logging 58 | Aws::Utils::Logging::InitializeAWSLogging(Aws::MakeShared(kNodeName.c_str())); 59 | 60 | //-----------Start Read Configuration Parameters--------------------- 61 | 62 | std::shared_ptr parameter_reader = 63 | std::make_shared(); 64 | 65 | // SDK client config 66 | Aws::Client::ClientConfigurationProvider client_config_provider(parameter_reader); 67 | Aws::Client::ClientConfiguration client_config = client_config_provider.GetClientConfiguration(); 68 | 69 | Aws::SDKOptions sdk_options; 70 | 71 | double publish_frequency; 72 | 73 | std::string metric_namespace; 74 | Aws::String dimensions_param; 75 | std::map default_metric_dims; 76 | 77 | // Load the storage resolution 78 | int storage_resolution = kNodeDefaultMetricDatumStorageResolution; 79 | Aws::CloudWatchMetrics::CloudWatchOptions cloudwatch_options; 80 | 81 | Aws::CloudWatchMetrics::Utils::ReadPublishFrequency(parameter_reader, publish_frequency); 82 | Aws::CloudWatchMetrics::Utils::ReadMetricNamespace(parameter_reader, metric_namespace); 83 | Aws::CloudWatchMetrics::Utils::ReadMetricDimensions(parameter_reader, dimensions_param, default_metric_dims); 84 | Aws::CloudWatchMetrics::Utils::ReadStorageResolution(parameter_reader, storage_resolution); 85 | 86 | Aws::CloudWatchMetrics::Utils::ReadCloudWatchOptions(parameter_reader, cloudwatch_options); 87 | //-----------------End read configuration parameters----------------------- 88 | 89 | // create the metric collector 90 | Aws::CloudWatchMetrics::Utils::MetricsCollector metrics_collector; 91 | 92 | // initialize with options read from the config file 93 | metrics_collector.Initialize( 94 | metric_namespace, 95 | default_metric_dims, 96 | storage_resolution, 97 | node_handle, 98 | client_config, 99 | sdk_options, 100 | cloudwatch_options); 101 | 102 | ros::ServiceServer service = node_handle.advertiseService(kNodeName, 103 | &Aws::CloudWatchMetrics::Utils::MetricsCollector::checkIfOnline, 104 | &metrics_collector); 105 | 106 | // start the collection process 107 | metrics_collector.start(); 108 | 109 | bool publish_when_size_reached = cloudwatch_options.uploader_options.batch_trigger_publish_size 110 | != Aws::DataFlow::kDefaultUploaderOptions.batch_trigger_publish_size; 111 | 112 | ros::Timer timer; 113 | // Publish on a timer if we are not publishing on a size limit. 114 | if (!publish_when_size_reached) { 115 | timer = 116 | node_handle.createTimer(ros::Duration(publish_frequency), 117 | &Aws::CloudWatchMetrics::Utils::MetricsCollector::TriggerPublish, 118 | &metrics_collector); 119 | } 120 | 121 | ros::spin(); 122 | 123 | AWS_LOG_INFO(__func__, "Shutting down Metrics Collector ..."); 124 | metrics_collector.shutdown(); 125 | Aws::Utils::Logging::ShutdownAWSLogging(); 126 | ros::shutdown(); 127 | return status; 128 | } 129 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/src/metrics_collector.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | 44 | namespace Aws { 45 | namespace CloudWatchMetrics { 46 | namespace Utils { 47 | 48 | void MetricsCollector::Initialize(std::string metric_namespace, 49 | std::map & default_dimensions, 50 | int storage_resolution, 51 | const ros::NodeHandle& node_handle, 52 | const Aws::Client::ClientConfiguration & config, 53 | const Aws::SDKOptions & sdk_options, 54 | const Aws::CloudWatchMetrics::CloudWatchOptions & cloudwatch_options, 55 | const std::shared_ptr& metric_service_factory) { 56 | 57 | this->metric_namespace_ = std::move(metric_namespace); 58 | this->default_dimensions_ = default_dimensions; 59 | this->storage_resolution_.store(storage_resolution); 60 | this->node_handle_ = node_handle; 61 | this->metric_service_ = metric_service_factory->createMetricService(this->metric_namespace_, 62 | config, 63 | sdk_options, 64 | cloudwatch_options); 65 | } 66 | 67 | void MetricsCollector::SubscribeAllTopics() 68 | { 69 | ReadTopics(topics_); 70 | for (auto & topic : topics_) { 71 | ros::Subscriber sub = node_handle_.subscribe( 72 | topic, kNodeSubQueueSize, 73 | [this](const ros_monitoring_msgs::MetricList::ConstPtr & metric_list_msg) -> void { 74 | this->RecordMetrics(metric_list_msg); 75 | }); 76 | subscriptions_.push_back(sub); 77 | } 78 | } 79 | 80 | int MetricsCollector::RecordMetrics( 81 | const ros_monitoring_msgs::MetricList::ConstPtr & metric_list_msg) 82 | { 83 | int batched_count = 0; 84 | AWS_LOGSTREAM_DEBUG(__func__, "Received " << metric_list_msg->metrics.size() << " metrics"); 85 | 86 | for (auto metric_msg = metric_list_msg->metrics.begin(); 87 | metric_msg != metric_list_msg->metrics.end(); ++metric_msg) { 88 | 89 | std::map dimensions; 90 | 91 | for (auto & default_dimension : default_dimensions_) { 92 | dimensions.emplace(default_dimension.first, default_dimension.second); // ignore the return, if we get a duplicate we're 93 | // going to stick with the first one 94 | } 95 | for (const auto & dimension : metric_msg->dimensions) { 96 | dimensions.emplace(dimension.name, dimension.value); // ignore the return, if we get a duplicate 97 | // we're going to stick with the first one 98 | } 99 | AWS_LOGSTREAM_DEBUG(__func__, "Recording metric with name=[" << metric_msg->metric_name << "]"); 100 | 101 | // create a MetricObject with message parameters to batch 102 | Aws::CloudWatchMetrics::Utils::MetricObject metric_object {metric_msg->metric_name, 103 | metric_msg->value, 104 | metric_msg->unit, 105 | GetMetricDataEpochMillis(*metric_msg), 106 | dimensions, 107 | this->storage_resolution_.load()}; 108 | bool batched = metric_service_->batchData(metric_object); 109 | 110 | if (!batched) { 111 | AWS_LOGSTREAM_ERROR(__func__, "Failed to record metric"); 112 | } 113 | 114 | batched_count++; 115 | } 116 | return batched_count; 117 | } 118 | 119 | int64_t MetricsCollector::GetMetricDataEpochMillis(const ros_monitoring_msgs::MetricData & metric_msg) 120 | { 121 | return metric_msg.time_stamp.toNSec() / 1000000; 122 | } 123 | 124 | void MetricsCollector::TriggerPublish(const ros::TimerEvent &) 125 | { 126 | AWS_LOG_DEBUG(__func__, "Flushing metrics"); 127 | this->metric_service_->publishBatchedData(); 128 | } 129 | 130 | bool MetricsCollector::start() { 131 | bool is_started = true; 132 | this->SubscribeAllTopics(); 133 | 134 | if (this->metric_service_) { 135 | is_started &= this->metric_service_->start(); 136 | } 137 | is_started &= Service::start(); 138 | return is_started; 139 | } 140 | 141 | bool MetricsCollector::shutdown() { 142 | bool is_shutdown = Service::shutdown(); 143 | if (this->metric_service_) { 144 | is_shutdown &= this->metric_service_->shutdown(); 145 | } 146 | return is_shutdown; 147 | } 148 | 149 | bool MetricsCollector::checkIfOnline(std_srvs::Trigger::Request& request, std_srvs::Trigger::Response& response) { 150 | 151 | AWS_LOGSTREAM_DEBUG(__func__, "received request " << request); 152 | 153 | if (!this->metric_service_) { 154 | response.success = false; 155 | response.message = "The MetricsCollector is not initialized"; 156 | return true; 157 | } 158 | 159 | response.success = this->metric_service_->isConnected(); 160 | response.message = response.success ? "The MetricsCollector is connected" : "The MetricsCollector is not connected"; 161 | 162 | return true; 163 | } 164 | 165 | } // namespace Utils 166 | } // namespace CloudWatchMetrics 167 | } // namespace Aws 168 | 169 | 170 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/src/metrics_collector_parameter_helper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using Aws::Client::ParameterPath; 26 | 27 | 28 | namespace Aws { 29 | namespace CloudWatchMetrics { 30 | namespace Utils { 31 | 32 | /** 33 | * Fetch the parameter for the log publishing frequency. 34 | * 35 | * @param parameter_reader to retrieve the parameters from. 36 | * @param publish_frequency the parameter is stored here when it is read successfully. 37 | * @return an error code that indicates whether the parameter was read successfully or not, 38 | * as returned by \p parameter_reader 39 | */ 40 | void ReadPublishFrequency( 41 | const std::shared_ptr& parameter_reader, 42 | double & publish_frequency) { 43 | 44 | Aws::AwsError ret = 45 | parameter_reader->ReadParam(ParameterPath(kNodeParamPublishFrequencyKey), publish_frequency); 46 | 47 | switch (ret) { 48 | case Aws::AwsError::AWS_ERR_NOT_FOUND: 49 | publish_frequency = kNodePublishFrequencyDefaultValue; 50 | AWS_LOGSTREAM_INFO(__func__, 51 | "Publish frequency configuration not found, setting to default value: " 52 | << kNodePublishFrequencyDefaultValue); 53 | break; 54 | case Aws::AwsError::AWS_ERR_OK: 55 | AWS_LOGSTREAM_INFO(__func__, "Publish frequency is set to: " << publish_frequency); 56 | break; 57 | default: 58 | publish_frequency = kNodePublishFrequencyDefaultValue; 59 | AWS_LOGSTREAM_ERROR(__func__, 60 | "Error " << ret << " retrieving publish frequency, setting to default value: " 61 | << kNodePublishFrequencyDefaultValue); 62 | 63 | } 64 | } 65 | 66 | /** 67 | * 68 | * @param parameter_reader 69 | * @param metric_namespace 70 | * @return 71 | */ 72 | void ReadMetricNamespace( 73 | const std::shared_ptr& parameter_reader, 74 | std::string & metric_namespace) { 75 | 76 | // Load the metric namespace 77 | Aws::AwsError read_namespace_status = 78 | parameter_reader->ReadParam(ParameterPath(kNodeParamMetricNamespaceKey), metric_namespace); 79 | if (Aws::AWS_ERR_OK == read_namespace_status) { 80 | AWS_LOGSTREAM_INFO(__func__, "Namespace: " << metric_namespace); 81 | } else { 82 | AWS_LOGSTREAM_INFO( 83 | __func__, 84 | "No namespace configuration found. Falling back to default namespace: " << kNodeDefaultMetricNamespace); 85 | metric_namespace = kNodeDefaultMetricNamespace; 86 | } 87 | } 88 | 89 | /** 90 | * 91 | * @param parameter_reader 92 | * @param dimensions_param 93 | * @param metric_dims 94 | * @return 95 | */ 96 | void ReadMetricDimensions( 97 | const std::shared_ptr& parameter_reader, 98 | Aws::String & dimensions_param, 99 | std::map & metric_dims) { 100 | 101 | // Load the default dimensions 102 | Aws::AwsError read_dimensions_status = 103 | parameter_reader->ReadParam(ParameterPath(kNodeParamDefaultMetricDimensionsKey), dimensions_param); 104 | 105 | Aws::OStringStream logging_stream; 106 | logging_stream << "Default Metric Dimensions: { "; 107 | if (Aws::AWS_ERR_OK == read_dimensions_status) { 108 | auto dims = Aws::Utils::StringUtils::Split(dimensions_param, ';'); 109 | for (auto & dim : dims) { 110 | if (!dim.empty()) { 111 | auto dim_vec = Aws::Utils::StringUtils::Split(dim, ':'); 112 | if (dim_vec.size() == 2) { 113 | metric_dims.emplace(dim_vec[0].c_str(), dim_vec[1].c_str()); 114 | logging_stream << dim_vec[0] << ": " << dim_vec[1] << ", "; 115 | } else { 116 | AWS_LOGSTREAM_WARN( 117 | __func__, "Could not parse dimension: " 118 | << dim << ". Should be in the format :"); 119 | } 120 | } 121 | } 122 | } 123 | logging_stream << " }"; 124 | AWS_LOGSTREAM_INFO(__func__, logging_stream.str()); 125 | } 126 | 127 | /** 128 | * 129 | * @param parameter_reader 130 | * @param storage_resolution 131 | * @return 132 | */ 133 | void ReadStorageResolution( 134 | const std::shared_ptr& parameter_reader, 135 | int & storage_resolution) { 136 | 137 | // Load the storage resolution 138 | storage_resolution = kNodeDefaultMetricDatumStorageResolution; 139 | 140 | Aws::AwsError read_storage_resolution_status = 141 | parameter_reader->ReadParam(ParameterPath(kNodeParamMetricDatumStorageResolutionKey), storage_resolution); 142 | 143 | if (Aws::AWS_ERR_OK == read_storage_resolution_status) { 144 | if (kNodeParamMetricDatumStorageResolutionValidValues.find(storage_resolution) == 145 | kNodeParamMetricDatumStorageResolutionValidValues.end()) { 146 | AWS_LOGSTREAM_WARN(__func__, 147 | "Storage Resolution value of [" 148 | << storage_resolution 149 | << "] is not allowed. Falling back to default Storage Resolution: " 150 | << kNodeDefaultMetricDatumStorageResolution); 151 | storage_resolution = kNodeDefaultMetricDatumStorageResolution; 152 | } else { 153 | AWS_LOGSTREAM_INFO(__func__, "Storage Resolution: " << storage_resolution); 154 | } 155 | } else { 156 | AWS_LOGSTREAM_INFO( 157 | __func__, 158 | "No Storage Resolution configuration found. Falling back to default Storage Resolution: " 159 | << storage_resolution); 160 | } 161 | } 162 | 163 | void ReadTopics(std::vector & topics) { 164 | 165 | std::string param_key; 166 | if (ros::param::search(kNodeParamMonitorTopicsListKey, param_key)) { 167 | ros::param::get(param_key, topics); 168 | } 169 | if (topics.empty()) { 170 | AWS_LOGSTREAM_INFO( 171 | __func__, "Topic list not defined or empty. Listening on topic: " << kNodeDefaulMetricsTopic); 172 | topics.push_back(kNodeDefaulMetricsTopic); 173 | } 174 | } 175 | 176 | void ReadCloudWatchOptions( 177 | const std::shared_ptr& parameter_reader, 178 | Aws::CloudWatchMetrics::CloudWatchOptions & cloudwatch_options) { 179 | 180 | Aws::DataFlow::UploaderOptions uploader_options{}; 181 | Aws::FileManagement::FileManagerStrategyOptions file_manager_strategy_options; 182 | 183 | ReadUploaderOptions(parameter_reader, uploader_options); 184 | ReadFileManagerStrategyOptions(parameter_reader, file_manager_strategy_options); 185 | 186 | cloudwatch_options = { 187 | uploader_options, 188 | file_manager_strategy_options 189 | }; 190 | } 191 | 192 | void ReadUploaderOptions( 193 | const std::shared_ptr& parameter_reader, 194 | Aws::DataFlow::UploaderOptions & uploader_options) { 195 | 196 | ReadOption( 197 | parameter_reader, 198 | kNodeParamFileUploadBatchSize, 199 | Aws::DataFlow::kDefaultUploaderOptions.file_upload_batch_size, 200 | uploader_options.file_upload_batch_size 201 | ); 202 | 203 | ReadOption( 204 | parameter_reader, 205 | kNodeParamFileMaxQueueSize, 206 | Aws::DataFlow::kDefaultUploaderOptions.file_max_queue_size, 207 | uploader_options.file_max_queue_size 208 | ); 209 | 210 | ReadOption( 211 | parameter_reader, 212 | kNodeParamBatchMaxQueueSize, 213 | Aws::DataFlow::kDefaultUploaderOptions.batch_max_queue_size, 214 | uploader_options.batch_max_queue_size 215 | ); 216 | 217 | ReadOption( 218 | parameter_reader, 219 | kNodeParamBatchTriggerPublishSize, 220 | Aws::DataFlow::kDefaultUploaderOptions.batch_trigger_publish_size, 221 | uploader_options.batch_trigger_publish_size 222 | ); 223 | 224 | ReadOption( 225 | parameter_reader, 226 | kNodeParamStreamMaxQueueSize, 227 | Aws::DataFlow::kDefaultUploaderOptions.stream_max_queue_size, 228 | uploader_options.stream_max_queue_size 229 | ); 230 | } 231 | 232 | void ReadFileManagerStrategyOptions( 233 | const std::shared_ptr& parameter_reader, 234 | Aws::FileManagement::FileManagerStrategyOptions & file_manager_strategy_options) { 235 | 236 | ReadOption( 237 | parameter_reader, 238 | kNodeParamStorageDirectory, 239 | Aws::CloudWatchMetrics::kDefaultMetricFileManagerStrategyOptions.storage_directory, 240 | file_manager_strategy_options.storage_directory); 241 | 242 | ReadOption( 243 | parameter_reader, 244 | kNodeParamFilePrefix, 245 | Aws::CloudWatchMetrics::kDefaultMetricFileManagerStrategyOptions.file_prefix, 246 | file_manager_strategy_options.file_prefix); 247 | 248 | ReadOption( 249 | parameter_reader, 250 | kNodeParamFileExtension, 251 | Aws::CloudWatchMetrics::kDefaultMetricFileManagerStrategyOptions.file_extension, 252 | file_manager_strategy_options.file_extension); 253 | 254 | ReadOption( 255 | parameter_reader, 256 | kNodeParamMaximumFileSize, 257 | Aws::CloudWatchMetrics::kDefaultMetricFileManagerStrategyOptions.maximum_file_size_in_kb, 258 | file_manager_strategy_options.maximum_file_size_in_kb); 259 | 260 | ReadOption( 261 | parameter_reader, 262 | kNodeParamStorageLimit, 263 | Aws::CloudWatchMetrics::kDefaultMetricFileManagerStrategyOptions.storage_limit_in_kb, 264 | file_manager_strategy_options.storage_limit_in_kb); 265 | } 266 | 267 | void ReadOption( 268 | const std::shared_ptr& parameter_reader, 269 | const std::string & option_key, 270 | const std::string & default_value, 271 | std::string & option_value) { 272 | Aws::AwsError ret = parameter_reader->ReadParam(ParameterPath(option_key), option_value); 273 | switch (ret) { 274 | case Aws::AwsError::AWS_ERR_NOT_FOUND: 275 | option_value = default_value; 276 | AWS_LOGSTREAM_INFO(__func__, 277 | option_key << " parameter not found, setting to default value: " << default_value); 278 | break; 279 | case Aws::AwsError::AWS_ERR_OK: 280 | AWS_LOGSTREAM_INFO(__func__, option_key << " is set to: " << option_value); 281 | break; 282 | default: 283 | option_value = default_value; 284 | AWS_LOGSTREAM_ERROR(__func__, 285 | "Error " << ret << " retrieving option " << option_key << ", setting to default value: " << default_value); 286 | } 287 | } 288 | 289 | void ReadOption( 290 | const std::shared_ptr& parameter_reader, 291 | const std::string & option_key, 292 | const size_t & default_value, 293 | size_t & option_value) { 294 | 295 | int param_value = 0; 296 | Aws::AwsError ret = parameter_reader->ReadParam(ParameterPath(option_key), param_value); 297 | switch (ret) { 298 | case Aws::AwsError::AWS_ERR_NOT_FOUND: 299 | option_value = default_value; 300 | AWS_LOGSTREAM_INFO(__func__, 301 | option_key << " parameter not found, setting to default value: " << default_value); 302 | break; 303 | case Aws::AwsError::AWS_ERR_OK: 304 | option_value = static_cast(param_value); 305 | AWS_LOGSTREAM_INFO(__func__, option_key << " is set to: " << option_value); 306 | break; 307 | default: 308 | option_value = default_value; 309 | AWS_LOGSTREAM_ERROR(__func__, 310 | "Error " << ret << " retrieving option " << option_key << ", setting to default value: " << default_value); 311 | } 312 | } 313 | 314 | } // namespace Utils 315 | } // namespace CloudWatchMetrics 316 | } // namespace Aws 317 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/test/cloudwatch_metrics_collector_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"). 5 | * You may not use this file except in compliance with the License. 6 | * A copy of the License is located at 7 | * 8 | * http://aws.amazon.com/apache2.0 9 | * 10 | * or in the "license" file accompanying this file. This file is distributed 11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 | * express or implied. See the License for the specific language governing 13 | * permissions and limitations under the License. 14 | */ 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | using namespace Aws::CloudWatchMetrics; 33 | using namespace Aws::CloudWatchMetrics::Utils; 34 | using namespace Aws::FileManagement; 35 | using ::testing::_; 36 | using ::testing::Return; 37 | using ::testing::StrEq; 38 | 39 | int test_argc; 40 | char** test_argv; 41 | 42 | class MetricServiceFactoryMock : public MetricServiceFactory 43 | { 44 | public: 45 | MOCK_METHOD4(createMetricService, 46 | std::shared_ptr( 47 | const std::string & metrics_namespace, 48 | const Aws::Client::ClientConfiguration & client_config, 49 | const Aws::SDKOptions & sdk_options, 50 | const CloudWatchOptions & cloudwatch_option) 51 | ); 52 | }; 53 | 54 | class MetricBatcherMock : public MetricBatcher 55 | { 56 | public: 57 | 58 | MOCK_METHOD0(publishBatchedData, bool()); 59 | }; 60 | 61 | class MetricPublisherMock : public MetricPublisher 62 | { 63 | public: 64 | MetricPublisherMock(const std::string & metrics_namespace, 65 | const Aws::Client::ClientConfiguration & client_config) 66 | : MetricPublisher(metrics_namespace, client_config) {} 67 | }; 68 | 69 | class MetricServiceMock : public MetricService 70 | { 71 | public: 72 | MetricServiceMock(std::shared_ptr> publisher, 73 | std::shared_ptr> batcher, 74 | std::shared_ptr> file_upload_streamer = nullptr) 75 | : MetricService(std::move(publisher), std::move(batcher), std::move(file_upload_streamer)) {} 76 | 77 | MOCK_METHOD1(batchData, bool(const MetricObject & data_to_batch)); 78 | MOCK_METHOD0(start, bool()); 79 | MOCK_METHOD0(shutdown, bool()); 80 | MOCK_METHOD0(publishBatchedData, bool()); 81 | }; 82 | 83 | 84 | class MetricsCollectorFixture : public ::testing::Test 85 | { 86 | protected: 87 | const std::string kMetricsTopic = "metrics"; 88 | const std::string kMetricName1 = "CWMetricsNodeTestMetric"; 89 | const std::string kMetricUnit1 = "sec"; 90 | const std::string metric_namespace = "test_namespace"; 91 | 92 | Aws::Client::ClientConfiguration config; 93 | Aws::SDKOptions sdk_options; 94 | Aws::CloudWatchMetrics::CloudWatchOptions cloudwatch_options; 95 | 96 | std::shared_ptr metric_service; 97 | std::shared_ptr metric_publisher; 98 | std::shared_ptr metric_batcher; 99 | std::shared_ptr metrics_collector; 100 | 101 | std::shared_ptr node_handle; 102 | std::shared_ptr metrics_pub; 103 | 104 | void SetUp() override 105 | { 106 | metric_batcher = std::make_shared(); 107 | metric_publisher = std::make_shared(metric_namespace, config); 108 | metric_service = std::make_shared(metric_publisher, metric_batcher); 109 | } 110 | 111 | void Initialize(std::map metric_dimensions) { 112 | 113 | ros::init(test_argc, test_argv, "CWMetricsNodeTest"); 114 | metrics_collector = std::make_shared(); 115 | 116 | node_handle = std::make_shared(); 117 | metrics_pub = std::make_shared( 118 | node_handle->advertise(kMetricsTopic.c_str(), 1)); 119 | 120 | EXPECT_CALL(*metric_service, start()).Times(1); 121 | 122 | std::shared_ptr metric_factory_mock = std::make_shared(); 123 | 124 | EXPECT_CALL(*metric_factory_mock, 125 | createMetricService(StrEq(metric_namespace), _, _, _)) 126 | .WillOnce(Return(metric_service)); 127 | 128 | node_handle = std::make_shared(); 129 | 130 | metrics_collector->Initialize(metric_namespace, 131 | metric_dimensions, 132 | 60, 133 | *node_handle, 134 | config, 135 | sdk_options, 136 | cloudwatch_options, 137 | metric_factory_mock); 138 | 139 | metrics_collector->start(); 140 | } 141 | 142 | void TearDown() override { 143 | if(metrics_collector) { 144 | EXPECT_CALL(*metric_service, shutdown()).Times(1); 145 | metrics_collector->shutdown(); 146 | } 147 | ros::param::del(kNodeParamMonitorTopicsListKey); 148 | } 149 | 150 | void SendMetricMessages(int num_msgs, ros_monitoring_msgs::MetricData & metric_data_proto) 151 | { 152 | ros_monitoring_msgs::MetricList metric_list_msg = ros_monitoring_msgs::MetricList(); 153 | for (int i = 0; i < num_msgs; i++) { 154 | metric_data_proto.value = i; 155 | metric_data_proto.time_stamp = ros::Time::now(); 156 | metric_list_msg.metrics.clear(); 157 | metric_list_msg.metrics.push_back(metric_data_proto); 158 | AWS_LOGSTREAM_DEBUG(__func__, "Publishing " << metric_list_msg.metrics.size() 159 | << " metrics to topic " << kMetricsTopic.c_str()); 160 | metrics_pub->publish(metric_list_msg); 161 | ros::spinOnce(); 162 | std::this_thread::sleep_for(std::chrono::seconds(1)); 163 | } 164 | } 165 | 166 | ros_monitoring_msgs::MetricData BasicMetricData() 167 | { 168 | ros_monitoring_msgs::MetricData metric_data = ros_monitoring_msgs::MetricData(); 169 | metric_data.metric_name = kMetricName1; 170 | metric_data.unit = kMetricUnit1; 171 | return metric_data; 172 | } 173 | }; 174 | 175 | // Test fixture Setup and TearDown 176 | TEST_F(MetricsCollectorFixture, Sanity) { 177 | ASSERT_TRUE(true); 178 | } 179 | 180 | // Test fixture init 181 | TEST_F(MetricsCollectorFixture, TestInitialize) { 182 | std::map metric_dimensions; 183 | Initialize(metric_dimensions); 184 | } 185 | 186 | struct GetMetricDataEpochMillisTestDatum { 187 | ros::Time input_time; 188 | int64_t expected_timestamp; 189 | }; 190 | 191 | class GetMetricDataEpochMillisFixture : public ::testing::TestWithParam {}; 192 | 193 | TEST_P(GetMetricDataEpochMillisFixture, getMetricDataEpochMillisTestOk) 194 | { 195 | ros_monitoring_msgs::MetricData metric_msg; 196 | metric_msg.time_stamp = GetParam().input_time; 197 | EXPECT_EQ(GetParam().expected_timestamp, MetricsCollector::GetMetricDataEpochMillis(metric_msg)); 198 | } 199 | 200 | const GetMetricDataEpochMillisTestDatum getMetricDataEpochMillisTestData [] = { 201 | GetMetricDataEpochMillisTestDatum{ros::Time(0,0), 0}, 202 | GetMetricDataEpochMillisTestDatum{ros::Time(10,0), 10 * 1000}, 203 | GetMetricDataEpochMillisTestDatum{ros::Time(0,1), 0}, 204 | GetMetricDataEpochMillisTestDatum{ros::Time(0,999999), 0}, 205 | GetMetricDataEpochMillisTestDatum{ros::Time(1,999999), 1000}, 206 | GetMetricDataEpochMillisTestDatum{ros::Time(0,1000000), 1}, 207 | GetMetricDataEpochMillisTestDatum{ros::Time(1,1000000), 1001} 208 | }; 209 | 210 | INSTANTIATE_TEST_CASE_P(getMetricDataEpochMillisTest, GetMetricDataEpochMillisFixture, 211 | ::testing::ValuesIn(getMetricDataEpochMillisTestData)); 212 | 213 | TEST_F(MetricsCollectorFixture, timerCallsMetricManagerService) 214 | { 215 | std::map metric_dimensions; 216 | Initialize(metric_dimensions); 217 | 218 | int num_msgs = 3; 219 | 220 | EXPECT_CALL(*metric_service, publishBatchedData()) 221 | .Times(::testing::AnyNumber()) 222 | .WillRepeatedly(::testing::Return(true)); 223 | 224 | EXPECT_CALL(*metric_service, 225 | batchData(::testing::_)) 226 | .Times(::testing::AtLeast(num_msgs)) 227 | .WillRepeatedly(::testing::Return(true)); 228 | 229 | ros_monitoring_msgs::MetricData metric_data = BasicMetricData(); 230 | SendMetricMessages(num_msgs, metric_data); 231 | for (int i = 0; i < num_msgs; i++) { 232 | ros::spinOnce(); 233 | std::this_thread::sleep_for(std::chrono::seconds(1)); 234 | } 235 | } 236 | 237 | /** 238 | * Helper matcher to ensure metric object data, received by the service, 239 | * matches the data given to the collector. 240 | */ 241 | MATCHER_P(metricsAreEqual, toTest, "") { 242 | return arg.metric_name == toTest.metric_name 243 | && arg.value == toTest.value 244 | && arg.unit == toTest.unit 245 | && arg.dimensions == toTest.dimensions 246 | && arg.storage_resolution == toTest.storage_resolution; 247 | // timestamp is ignored 248 | } 249 | 250 | TEST_F(MetricsCollectorFixture, metricsRecordedNoDimension) 251 | { 252 | std::map metric_dimensions; 253 | Initialize(metric_dimensions); 254 | 255 | int num_msgs = 3; 256 | 257 | MetricObject m01 = MetricObject {kMetricName1, 0.0, kMetricUnit1, 1234, std::map(), 60}; 258 | MetricObject m02 = MetricObject {kMetricName1, 1.0, kMetricUnit1, 1234, std::map(), 60}; 259 | MetricObject m03 = MetricObject {kMetricName1, 2.0, kMetricUnit1, 1234, std::map(), 60}; 260 | 261 | EXPECT_CALL(*metric_service, publishBatchedData()) 262 | .Times(::testing::AnyNumber()) 263 | .WillRepeatedly(::testing::Return(true)); 264 | { 265 | ::testing::Sequence rm_seq; 266 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m01))) 267 | .WillOnce(::testing::Return(true)); 268 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m02))) 269 | .WillOnce(::testing::Return(true)); 270 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m03))) 271 | .WillOnce(::testing::Return(true)); 272 | } 273 | 274 | ros_monitoring_msgs::MetricData metric_data = BasicMetricData(); 275 | SendMetricMessages(num_msgs, metric_data); 276 | ros::spinOnce(); 277 | } 278 | 279 | TEST_F(MetricsCollectorFixture, metricRecordedWithDimension) 280 | { 281 | 282 | int num_msgs = 3; 283 | const std::string metric_dimension_name = "CWMetricsNodeTestDim1"; 284 | const std::string metric_dimension_value = "CWMetricsNodeTestDim1Value"; 285 | 286 | std::map expected_dim; 287 | expected_dim[metric_dimension_name] = metric_dimension_value; 288 | 289 | MetricObject m01 = MetricObject {kMetricName1, 0.0, kMetricUnit1, 1234, expected_dim, 60}; 290 | MetricObject m02 = MetricObject {kMetricName1, 1.0, kMetricUnit1, 1234, expected_dim, 60}; 291 | MetricObject m03 = MetricObject {kMetricName1, 2.0, kMetricUnit1, 1234, expected_dim, 60}; 292 | 293 | EXPECT_CALL(*metric_service, publishBatchedData()) 294 | .Times(::testing::AnyNumber()) 295 | .WillRepeatedly(::testing::Return(true)); 296 | 297 | { 298 | ::testing::Sequence rm_seq; 299 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m01))) 300 | .WillOnce(::testing::Return(true)); 301 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m02))) 302 | .WillOnce(::testing::Return(true)); 303 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m03))) 304 | .WillOnce(::testing::Return(true)); 305 | } 306 | 307 | Initialize(expected_dim); 308 | 309 | ros_monitoring_msgs::MetricData metric_data = BasicMetricData(); 310 | ros_monitoring_msgs::MetricDimension metric_dimension = ros_monitoring_msgs::MetricDimension(); 311 | metric_dimension.name = metric_dimension_name; 312 | metric_dimension.value = metric_dimension_value; 313 | metric_data.dimensions.push_back(metric_dimension); 314 | 315 | SendMetricMessages(num_msgs, metric_data); 316 | ros::spinOnce(); 317 | } 318 | 319 | TEST_F(MetricsCollectorFixture, metricRecordedWithDefaultDimensions) 320 | { 321 | 322 | int num_msgs = 3; 323 | const std::string metric_dimension_name = "CWMetricsNodeTestDim1"; 324 | const std::string metric_dimension_value = "CWMetricsNodeTestDim1Value"; 325 | 326 | std::map expected_dim; 327 | expected_dim[metric_dimension_name] = metric_dimension_value; 328 | 329 | MetricObject m01 = MetricObject {kMetricName1, 0.0, kMetricUnit1, 1234, expected_dim, 60}; 330 | MetricObject m02 = MetricObject {kMetricName1, 1.0, kMetricUnit1, 1234, expected_dim, 60}; 331 | MetricObject m03 = MetricObject {kMetricName1, 2.0, kMetricUnit1, 1234, expected_dim, 60}; 332 | 333 | EXPECT_CALL(*metric_service, publishBatchedData()) 334 | .Times(::testing::AnyNumber()) 335 | .WillRepeatedly(::testing::Return(true)); 336 | 337 | { 338 | ::testing::Sequence rm_seq; 339 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m01))) 340 | .WillOnce(::testing::Return(true)); 341 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m02))) 342 | .WillOnce(::testing::Return(true)); 343 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m03))) 344 | .WillOnce(::testing::Return(true)); 345 | } 346 | 347 | std::map default_metric_dims; 348 | default_metric_dims.emplace(metric_dimension_name, metric_dimension_value); 349 | Initialize(default_metric_dims); 350 | 351 | ros_monitoring_msgs::MetricData metric_data = BasicMetricData(); 352 | 353 | SendMetricMessages(num_msgs, metric_data); 354 | ros::spinOnce(); 355 | } 356 | 357 | TEST_F(MetricsCollectorFixture, customTopicsListened) 358 | { 359 | std::vector topics; 360 | topics.emplace_back("metrics_topic0"); 361 | topics.emplace_back("metrics_topic1"); 362 | ros::param::set(kNodeParamMonitorTopicsListKey, topics); 363 | 364 | std::map default_metric_dims; 365 | 366 | MetricObject m01 = MetricObject {kMetricName1, 0.0, kMetricUnit1, 1234, default_metric_dims, 60}; 367 | MetricObject m02 = MetricObject {kMetricName1, 1.0, kMetricUnit1, 1234, default_metric_dims, 60}; 368 | 369 | EXPECT_CALL(*metric_service, publishBatchedData()) 370 | .Times(::testing::AnyNumber()) 371 | .WillRepeatedly(::testing::Return(true)); 372 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m01))) 373 | .Times(1) 374 | .WillOnce(::testing::Return(true)); 375 | EXPECT_CALL(*metric_service, batchData(metricsAreEqual(m02))) 376 | .Times(1) 377 | .WillOnce(::testing::Return(true)); 378 | 379 | Initialize(default_metric_dims); 380 | 381 | ros_monitoring_msgs::MetricList metric_list_msg = ros_monitoring_msgs::MetricList(); 382 | ros_monitoring_msgs::MetricData metric_data = BasicMetricData(); 383 | ros::Publisher metrics_pub0 = 384 | node_handle->advertise(topics[0].c_str(), 1); 385 | metric_data.value = 0; 386 | metric_data.time_stamp = ros::Time::now(); 387 | metric_list_msg.metrics.clear(); 388 | metric_list_msg.metrics.push_back(metric_data); 389 | metrics_pub0.publish(metric_list_msg); 390 | ros::spinOnce(); 391 | ros::Publisher metrics_pub1 = 392 | node_handle->advertise(topics[1].c_str(), 1); 393 | metric_data.value = 1; 394 | metric_data.time_stamp = ros::Time::now(); 395 | metric_list_msg.metrics.clear(); 396 | metric_list_msg.metrics.push_back(metric_data); 397 | metrics_pub1.publish(metric_list_msg); 398 | ros::spinOnce(); 399 | std::this_thread::sleep_for(std::chrono::seconds(1)); 400 | ros::spinOnce(); 401 | } 402 | 403 | int main(int argc, char ** argv) 404 | { 405 | testing::InitGoogleTest(&argc, argv); 406 | test_argc = argc; 407 | test_argv = argv; 408 | return RUN_ALL_TESTS(); 409 | } 410 | -------------------------------------------------------------------------------- /cloudwatch_metrics_collector/test/test_cloudwatch_metrics_collector.test: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------