Skip to content
This repository was archived by the owner on Feb 12, 2022. It is now read-only.

Commit ae6fe0b

Browse files
authored
Add option to not publish topic names to Cloudwatch Logs (#58)
* add option to not publish topic names to cloudwatch logs * fix unit test failure * add unit tests, improve code coverage * update documentation * address PR comments * make the deprecated constructor use constructor delegation
1 parent 214fc77 commit ae6fe0b

File tree

10 files changed

+175
-43
lines changed

10 files changed

+175
-43
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ An example configuration file called `sample_configuration.yaml` is provided. Wh
119119
| log_group_name | AWS CloudWatch log group name | *std::string* | 'string'<br/>*note*: Log group names must be unique within a region foran AWS account | ros_log_group |
120120
| log_stream_name | AWS CloudWatch log stream name | *std::string* | 'string'<br/>*note*: The : (colon) and * (asterisk) characters are not allowed | ros_log_stream |
121121
| topics | A list of topics to get logs from (excluding `rosout_agg`) | *std::vector<std::string>* | ['string', 'string', 'string'] | `[]` |
122-
| min_log_verbosity| The minimum log severity for sending logs selectively to AWS CloudWatch Logs, log messages with a severity lower than `min_log_verbosity` will be ignored | *std::string* | DEBUG/INFO/WARN/ERROR/FATAL | DEBUG |
122+
| min_log_verbosity | The minimum log severity for sending logs selectively to AWS CloudWatch Logs, log messages with a severity lower than `min_log_verbosity` will be ignored | *std::string* | DEBUG/INFO/WARN/ERROR/FATAL | DEBUG |
123+
| publish_topic_names | Whether or not to include topic name information in the log messsages that are uploaded to AWS CloudWatch Logs | *bool* | true/false | true |
123124
| storage_directory | The location where all offline metrics will be stored | *string* | string | ~/.ros/cwlogs/ |
124125
| 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* | number | 1048576 |
125126
| aws_client_configuration | AWS region configuration | *std::string* | *region*: "us-west-2"/"us-east-1"/"us-east-2"/etc. | region: us-west-2 |

cloudwatch_logger/config/sample_configuration.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ ignore_nodes: ["/cloudwatch_logger", "/cloudwatch_metrics_collector"]
3434
# default value is: DEBUG == logs of all log verbosity levels get sent to CloudWatch Logs
3535
min_log_verbosity: DEBUG
3636

37+
# whether or not to include topic name information in the log messsages that are uploaded to cloudwatch
38+
# default value is: true
39+
publish_topic_names: true
40+
3741
# The absolute path to a folder that all offline logs will be stored in
3842
storage_directory: "~/.ros/cwlogs/"
3943

cloudwatch_logger/include/cloudwatch_logger/log_node.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,32 @@ namespace Utils {
3333
class LogNode : public Service
3434
{
3535
public:
36+
struct Options {
37+
int8_t min_log_severity;
38+
bool publish_topic_names;
39+
std::unordered_set<std::string> ignore_nodes;
40+
};
41+
42+
/**
43+
* Creates a new CloudWatchLogNode
44+
*
45+
* @param options an options struct that specifies some behaviors of this CloudWatchLogNode
46+
*/
47+
explicit LogNode(const Options & options);
48+
3649
/**
37-
* @brief Creates a new CloudWatchLogNode
50+
* @deprecated Creates a new CloudWatchLogNode
3851
*
3952
* @param min_log_severity the minimum log severity level defined in the configuration file
4053
* logs with severity level equal or above get sent to CloudWatch Logs
4154
* @param ignore_nodes The set of node names to ignore logs from
4255
*/
4356
explicit LogNode(int8_t min_log_severity, std::unordered_set<std::string> ignore_nodes);
4457

58+
LogNode(const LogNode & other) = delete;
59+
60+
LogNode & operator=(const LogNode & other) = delete;
61+
4562
/**
4663
* @brief Tears down a AWSCloudWatchLogNode object
4764
*/
@@ -91,9 +108,11 @@ class LogNode : public Service
91108
private:
92109
bool ShouldSendToCloudWatchLogs(const int8_t log_severity_level);
93110
const std::string FormatLogs(const rosgraph_msgs::Log::ConstPtr & log_msg);
111+
94112
std::shared_ptr<Aws::CloudWatchLogs::LogService> log_service_;
95113
int8_t min_log_severity_;
96114
std::unordered_set<std::string> ignore_nodes_;
115+
bool publish_topic_names_;
97116
};
98117

99118
} // namespace Utils

cloudwatch_logger/include/cloudwatch_logger/log_node_param_helper.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ constexpr char kNodeParamSubscribeToRosoutKey[] = "sub_to_rosout";
3535
constexpr char kNodeParamLogGroupNameKey[] = "log_group_name";
3636
constexpr char kNodeParamLogTopicsListKey[] = "topics";
3737
constexpr char kNodeParamMinLogVerbosityKey[] = "min_log_verbosity";
38+
constexpr char kNodeParamPublishTopicNamesKey[] = "publish_topic_names";
3839
constexpr char kNodeParamIgnoreNodesKey[] = "ignore_nodes";
3940

4041
/** Configuration params for Aws::DataFlow::UploaderOptions **/
@@ -51,11 +52,13 @@ constexpr char kNodeParamFileExtension[] = "file_extension";
5152
constexpr char kNodeParamMaximumFileSize[] = "maximum_file_size";
5253
constexpr char kNodeParamStorageLimit[] = "storage_limit";
5354

55+
/** Default values for parameters **/
5456
constexpr char kNodeLogGroupNameDefaultValue[] = "ros_log_group";
5557
constexpr char kNodeLogStreamNameDefaultValue[] = "ros_log_stream";
5658
constexpr int8_t kNodeMinLogVerbosityDefaultValue = rosgraph_msgs::Log::DEBUG;
5759
constexpr double kNodePublishFrequencyDefaultValue = 5.0;
5860
constexpr bool kNodeSubscribeToRosoutDefaultValue = true;
61+
constexpr bool kNodePublishTopicNamesDefaultValue = true;
5962

6063
/**
6164
* Fetch the parameter for the log publishing frequency.
@@ -116,6 +119,19 @@ Aws::AwsError ReadMinLogVerbosity(
116119
std::shared_ptr<Aws::Client::ParameterReaderInterface> parameter_reader,
117120
int8_t & min_log_verbosity);
118121

122+
/**
123+
* Fetch the parameter for whether or not to include topic name information in the log messsages
124+
* that are uploaded to AWS CloudWatch Logs.
125+
*
126+
* @param parameter_reader to retrieve the parameters from.
127+
* @param publish_topic_names the parameter is stored here when it is read successfully.
128+
* @return an error code that indicates whether the parameter was read successfully or not,
129+
* as returned by \p parameter_reader
130+
*/
131+
Aws::AwsError ReadPublishTopicNames(
132+
const std::shared_ptr<Aws::Client::ParameterReaderInterface>& parameter_reader,
133+
bool & publish_topic_names);
134+
119135
/**
120136
* Fetch the parameter for the list of topics to get logs from, and subscribe \p nh
121137
* to them. Also subscribe to rout if \p subscribe_to_rosout is true.

cloudwatch_logger/package.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0"?>
22
<package format="2">
33
<name>cloudwatch_logger</name>
4-
<version>2.2.1</version>
4+
<version>2.3.1</version>
55
<description>CloudWatch Logger node for publishing logs to AWS CloudWatch Logs</description>
66

77
<url>http://wiki.ros.org/cloudwatch_logger</url>

cloudwatch_logger/src/log_node.cpp

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@
3030

3131
using namespace Aws::CloudWatchLogs::Utils;
3232

33-
LogNode::LogNode(int8_t min_log_severity, std::unordered_set<std::string> ignore_nodes)
34-
: ignore_nodes_(std::move(ignore_nodes))
35-
{
36-
this->log_service_ = nullptr;
37-
this->min_log_severity_ = min_log_severity;
38-
}
33+
LogNode::LogNode(const Options & options)
34+
: min_log_severity_(options.min_log_severity),
35+
ignore_nodes_(options.ignore_nodes),
36+
publish_topic_names_(options.publish_topic_names) {}
37+
38+
LogNode::LogNode(int8_t min_log_severity, std::unordered_set<std::string> ignore_nodes)
39+
: LogNode(Options{min_log_severity, true, std::move(ignore_nodes)}) {}
3940

4041
LogNode::~LogNode() { this->log_service_ = nullptr; }
4142

@@ -130,19 +131,21 @@ const std::string LogNode::FormatLogs(const rosgraph_msgs::Log::ConstPtr & log_m
130131
}
131132
ss << "[node name: " << log_msg->name << "] ";
132133

133-
ss << "[topics: ";
134-
std::vector<std::string>::const_iterator it = log_msg->topics.begin();
135-
std::vector<std::string>::const_iterator end = log_msg->topics.end();
136-
for (; it != end; ++it) {
137-
const std::string & topic = *it;
138-
139-
if (it != log_msg->topics.begin()) {
140-
ss << ", ";
134+
if (publish_topic_names_) {
135+
ss << "[topics: ";
136+
auto it = log_msg->topics.begin();
137+
auto end = log_msg->topics.end();
138+
for (; it != end; ++it) {
139+
const std::string & topic = *it;
140+
if (it != log_msg->topics.begin()) {
141+
ss << ", ";
142+
}
143+
ss << topic;
141144
}
142-
143-
ss << topic;
145+
ss << "] ";
144146
}
145-
ss << "] " << log_msg->msg << "\n";
147+
148+
ss << log_msg->msg << "\n";
146149

147150
return ss.str();
148-
}
151+
}

cloudwatch_logger/src/log_node_param_helper.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,37 @@ Aws::AwsError ReadMinLogVerbosity(
171171
return ret;
172172
}
173173

174+
Aws::AwsError ReadPublishTopicNames(
175+
const std::shared_ptr<Aws::Client::ParameterReaderInterface>& parameter_reader,
176+
bool & publish_topic_names)
177+
{
178+
Aws::AwsError ret =
179+
parameter_reader->ReadParam(ParameterPath(kNodeParamPublishTopicNamesKey), publish_topic_names);
180+
181+
switch (ret) {
182+
case Aws::AwsError::AWS_ERR_NOT_FOUND:
183+
publish_topic_names = kNodePublishTopicNamesDefaultValue;
184+
AWS_LOGSTREAM_INFO(
185+
__func__,
186+
"Whether to publish topic names to Cloudwatch Logs configuration not found, setting to default value: "
187+
<< kNodePublishTopicNamesDefaultValue);
188+
break;
189+
case Aws::AwsError::AWS_ERR_OK:
190+
AWS_LOGSTREAM_INFO(
191+
__func__, "Whether to publish topic names to Cloudwatch Logs is set to: " << publish_topic_names);
192+
break;
193+
default:
194+
publish_topic_names = kNodePublishTopicNamesDefaultValue;
195+
AWS_LOGSTREAM_ERROR(
196+
__func__,
197+
"Error " << ret
198+
<< "retrieving parameter for whether to publish topic names to Cloudwatch Logs"
199+
<< ", setting to default value: " << kNodePublishTopicNamesDefaultValue);
200+
}
201+
202+
return ret;
203+
}
204+
174205
Aws::AwsError ReadSubscriberList(
175206
const bool subscribe_to_rosout,
176207
std::shared_ptr<Aws::Client::ParameterReaderInterface> parameter_reader,

cloudwatch_logger/src/main.cpp

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525

2626
#include <cloudwatch_logs_common/cloudwatch_options.h>
2727

28-
using namespace Aws::CloudWatchLogs::Utils;
29-
3028
constexpr char kNodeName[] = "cloudwatch_logger";
3129

3230
int main(int argc, char ** argv)
@@ -41,9 +39,8 @@ int main(int argc, char ** argv)
4139
std::string log_group;
4240
std::string log_stream;
4341
bool subscribe_to_rosout;
44-
int8_t min_log_verbosity;
45-
std::vector<ros::Subscriber> subscriptions;
46-
std::unordered_set<std::string> ignore_nodes;
42+
Aws::SDKOptions sdk_options;
43+
Aws::CloudWatchLogs::Utils::LogNode::Options logger_options;
4744
Aws::CloudWatchLogs::CloudWatchOptions cloudwatch_options;
4845

4946
ros::NodeHandle nh;
@@ -52,22 +49,20 @@ int main(int argc, char ** argv)
5249
std::make_shared<Aws::Client::Ros1NodeParameterReader>();
5350

5451
// checking configurations to set values or set to default values;
55-
ReadPublishFrequency(parameter_reader, publish_frequency);
56-
ReadLogGroup(parameter_reader, log_group);
57-
ReadLogStream(parameter_reader, log_stream);
58-
ReadSubscribeToRosout(parameter_reader, subscribe_to_rosout);
59-
ReadMinLogVerbosity(parameter_reader, min_log_verbosity);
60-
ReadIgnoreNodesSet(parameter_reader, ignore_nodes);
61-
62-
ReadCloudWatchOptions(parameter_reader, cloudwatch_options);
52+
Aws::CloudWatchLogs::Utils::ReadPublishFrequency(parameter_reader, publish_frequency);
53+
Aws::CloudWatchLogs::Utils::ReadLogGroup(parameter_reader, log_group);
54+
Aws::CloudWatchLogs::Utils::ReadLogStream(parameter_reader, log_stream);
55+
Aws::CloudWatchLogs::Utils::ReadSubscribeToRosout(parameter_reader, subscribe_to_rosout);
56+
Aws::CloudWatchLogs::Utils::ReadMinLogVerbosity(parameter_reader, logger_options.min_log_severity);
57+
Aws::CloudWatchLogs::Utils::ReadPublishTopicNames(parameter_reader, logger_options.publish_topic_names);
58+
Aws::CloudWatchLogs::Utils::ReadIgnoreNodesSet(parameter_reader, logger_options.ignore_nodes);
59+
Aws::CloudWatchLogs::Utils::ReadCloudWatchOptions(parameter_reader, cloudwatch_options);
6360

6461
// configure aws settings
6562
Aws::Client::ClientConfigurationProvider client_config_provider(parameter_reader);
6663
Aws::Client::ClientConfiguration config = client_config_provider.GetClientConfiguration();
6764

68-
Aws::SDKOptions sdk_options;
69-
70-
Aws::CloudWatchLogs::Utils::LogNode cloudwatch_logger(min_log_verbosity, ignore_nodes);
65+
Aws::CloudWatchLogs::Utils::LogNode cloudwatch_logger(logger_options);
7166
cloudwatch_logger.Initialize(log_group, log_stream, config, sdk_options, cloudwatch_options);
7267

7368
ros::ServiceServer service = nh.advertiseService(kNodeName,
@@ -83,7 +78,8 @@ int main(int argc, char ** argv)
8378
};
8479

8580
// subscribe to additional topics, if any
86-
ReadSubscriberList(subscribe_to_rosout, parameter_reader, callback, nh, subscriptions);
81+
std::vector<ros::Subscriber> subscriptions;
82+
Aws::CloudWatchLogs::Utils::ReadSubscriberList(subscribe_to_rosout, parameter_reader, callback, nh, subscriptions);
8783
AWS_LOGSTREAM_INFO(__func__, "Initialized " << kNodeName << ".");
8884

8985
bool publish_when_size_reached = cloudwatch_options.uploader_options.batch_trigger_publish_size

cloudwatch_logger/test/log_node_param_helper_test.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,41 @@ TEST_F(LogNodeParamHelperFixture, TestReadReadMinLogVerbosity)
252252
EXPECT_EQ(kNodeMinLogVerbosityDefaultValue, param);
253253
}
254254

255+
TEST_F(LogNodeParamHelperFixture, TestPublishTopicNames)
256+
{
257+
{
258+
InSequence read_param_seq;
259+
260+
EXPECT_CALL(*param_reader_, ReadParam(Eq(ParameterPath(kNodeParamPublishTopicNamesKey)), A<bool &>()))
261+
.WillOnce(Return(AwsError::AWS_ERR_FAILURE));
262+
263+
EXPECT_CALL(*param_reader_, ReadParam(Eq(ParameterPath(kNodeParamPublishTopicNamesKey)), A<bool &>()))
264+
.WillOnce(Return(AwsError::AWS_ERR_NOT_FOUND));
265+
266+
EXPECT_CALL(*param_reader_, ReadParam(Eq(ParameterPath(kNodeParamPublishTopicNamesKey)), A<bool &>()))
267+
.WillOnce(DoAll(SetArgReferee<1>(true), Return(AwsError::AWS_ERR_OK)));
268+
269+
EXPECT_CALL(*param_reader_, ReadParam(Eq(ParameterPath(kNodeParamPublishTopicNamesKey)), A<bool &>()))
270+
.WillOnce(DoAll(SetArgReferee<1>(false), Return(AwsError::AWS_ERR_OK)));
271+
}
272+
273+
bool param = false;
274+
EXPECT_EQ(AwsError::AWS_ERR_FAILURE, ReadPublishTopicNames(param_reader_, param));
275+
EXPECT_EQ(kNodePublishTopicNamesDefaultValue, param);
276+
277+
param = false;
278+
EXPECT_EQ(AwsError::AWS_ERR_NOT_FOUND, ReadPublishTopicNames(param_reader_, param));
279+
EXPECT_EQ(kNodePublishTopicNamesDefaultValue, param);
280+
281+
param = false;
282+
EXPECT_EQ(AwsError::AWS_ERR_OK, ReadPublishTopicNames(param_reader_, param));
283+
EXPECT_EQ(true, param);
284+
285+
param = true;
286+
EXPECT_EQ(AwsError::AWS_ERR_OK, ReadPublishTopicNames(param_reader_, param));
287+
EXPECT_EQ(false, param);
288+
}
289+
255290
AwsError MockReadParamAddStringToList(const ParameterPath & param_path, std::vector<std::string> & out)
256291
{
257292
(void)param_path;

cloudwatch_logger/test/log_node_test.cpp

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,17 @@ class LogNodeFixture : public ::testing::Test
103103
log_service_factory = std::make_shared<LogServiceFactoryMock>();
104104
}
105105

106-
std::shared_ptr<LogNode> build_test_subject(int8_t severity_level = rosgraph_msgs::Log::DEBUG,
107-
std::unordered_set<std::string> ignore_nodes = std::unordered_set<std::string>())
106+
std::shared_ptr<LogNode> build_test_subject(
107+
int8_t severity_level = rosgraph_msgs::Log::DEBUG,
108+
bool publish_topic_names = true,
109+
const std::unordered_set<std::string> & ignore_nodes = std::unordered_set<std::string>())
108110
{
109-
return std::make_shared<LogNode>(severity_level, ignore_nodes);
111+
Aws::CloudWatchLogs::Utils::LogNode::Options logger_options = {
112+
severity_level,
113+
publish_topic_names,
114+
ignore_nodes
115+
};
116+
return std::make_shared<LogNode>(logger_options);
110117
}
111118

112119
rosgraph_msgs::Log::ConstPtr message_to_constptr(rosgraph_msgs::Log log_message)
@@ -190,6 +197,26 @@ TEST_F(LogNodeFixture, TestRecordLogSevBelowMinSeverity)
190197
test_subject->RecordLogs(message_to_constptr(log_message_));
191198
}
192199

200+
TEST_F(LogNodeFixture, TestDontPublishTopicNames)
201+
{
202+
std::shared_ptr<LogNode> test_subject = build_test_subject(rosgraph_msgs::Log::DEBUG, false);
203+
initialize_log_node(test_subject);
204+
205+
const std::string log_name_reference_str = std::string("[node name: ") + log_message_.name + "]";
206+
const std::string log_topics_reference_str = "[topics: ]";
207+
208+
EXPECT_CALL(*log_service,
209+
batchData(AllOf(
210+
HasSubstr("DEBUG"),
211+
HasSubstr(log_message_.msg),
212+
HasSubstr(log_name_reference_str),
213+
Not(HasSubstr(log_topics_reference_str))
214+
)))
215+
.WillOnce(Return(true));
216+
217+
test_subject->RecordLogs(message_to_constptr(log_message_));
218+
}
219+
193220
TEST_F(LogNodeFixture, TestRecordLogSevEqGtMinSeverity)
194221
{
195222
std::shared_ptr<LogNode> test_subject = build_test_subject(rosgraph_msgs::Log::ERROR);
@@ -218,7 +245,7 @@ TEST_F(LogNodeFixture, TestRecordLogSevEqGtMinSeverity)
218245

219246
TEST_F(LogNodeFixture, TestRecordLogTopicsOk)
220247
{
221-
std::shared_ptr<LogNode> test_subject = build_test_subject(rosgraph_msgs::Log::DEBUG);
248+
std::shared_ptr<LogNode> test_subject = build_test_subject();
222249

223250
initialize_log_node(test_subject);
224251

@@ -280,7 +307,7 @@ TEST_F(LogNodeFixture, TestRecordLogIgnoreList)
280307
{
281308
std::unordered_set<std::string> ignore_nodes;
282309
ignore_nodes.emplace(log_message_.name);
283-
std::shared_ptr<LogNode> test_subject = build_test_subject(rosgraph_msgs::Log::DEBUG, ignore_nodes);
310+
std::shared_ptr<LogNode> test_subject = build_test_subject(rosgraph_msgs::Log::DEBUG, true, ignore_nodes);
284311

285312
initialize_log_node(test_subject);
286313

0 commit comments

Comments
 (0)