cloudwatch_logs_facade.cpp
Go to the documentation of this file.
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 <aws/core/Aws.h>
17 #include <aws/core/utils/Outcome.h>
18 #include <aws/core/utils/logging/LogMacros.h>
19 #include <aws/logs/CloudWatchLogsErrors.h>
20 #include <aws/logs/model/CreateLogGroupRequest.h>
21 #include <aws/logs/model/CreateLogStreamRequest.h>
22 #include <aws/logs/model/DescribeLogGroupsRequest.h>
23 #include <aws/logs/model/DescribeLogGroupsResult.h>
24 #include <aws/logs/model/DescribeLogStreamsRequest.h>
25 #include <aws/logs/model/DescribeLogStreamsResult.h>
26 #include <aws/logs/model/InputLogEvent.h>
27 #include <aws/logs/model/LogStream.h>
28 #include <aws/logs/model/PutLogEventsRequest.h>
32 
33 namespace {
34 // Return the current monotonic time in milliseconds since the epoch
35 std::chrono::milliseconds now() {
36  return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch());
37 }
38 } // unnamed namespace
39 
40 namespace Aws {
41 namespace CloudWatchLogs {
42 namespace Utils {
43 
44 constexpr uint16_t kMaxLogsPerRequest = 100;
45 
50 const std::chrono::milliseconds kMinPutLogsPeriod{200};
51 
52 CloudWatchLogsFacade::CloudWatchLogsFacade(const Aws::Client::ClientConfiguration & client_config)
53 {
54  this->cw_client_ = std::make_shared<Aws::CloudWatchLogs::CloudWatchLogsClient>(client_config);
55 }
56 
57 CloudWatchLogsFacade::CloudWatchLogsFacade(const std::shared_ptr<Aws::CloudWatchLogs::CloudWatchLogsClient>& cw_client)
58 {
59  this->cw_client_ = cw_client;
60 }
61 
63  const Aws::CloudWatchLogs::Model::PutLogEventsRequest & request, Aws::String & next_token)
64 {
66 
67  // Rate limiting
68  const auto time_since_last_put = now() - last_put_time_;
69  if (time_since_last_put < kMinPutLogsPeriod) {
70  // This will wait _at most_ kMinPutLogsPeriod.
71  const auto sleep_for = kMinPutLogsPeriod - time_since_last_put;
72  AWS_LOG_WARN(__func__, "PutLogEvents occurring too quickly, rate limiting in effect. Delaying PutLogs call by %d ms", sleep_for.count());
73  std::this_thread::sleep_for(sleep_for);
74  }
75 
76  auto response = this->cw_client_->PutLogEvents(request);
77  last_put_time_ = now();
78  if (!response.IsSuccess()) {
79 
80  AWS_LOGSTREAM_ERROR(__func__, "Send log request failed due to: "
81  << response.GetError().GetMessage() << ", with error code: "
82  << static_cast<int>(response.GetError().GetErrorType()));
83 
84  switch(response.GetError().GetErrorType()) {
85 
86  case Aws::CloudWatchLogs::CloudWatchLogsErrors::NETWORK_CONNECTION:
87  status = CW_LOGS_NOT_CONNECTED;
88  break;
89  case Aws::CloudWatchLogs::CloudWatchLogsErrors::INVALID_PARAMETER_COMBINATION:
90  case Aws::CloudWatchLogs::CloudWatchLogsErrors::INVALID_PARAMETER_VALUE:
91  case Aws::CloudWatchLogs::CloudWatchLogsErrors::MISSING_PARAMETER:
93  break;
94  default:
95  status = CW_LOGS_FAILED;
96  }
97 
98  } else {
99  AWS_LOG_DEBUG(__func__, "Setting the sequence token to use for the next send log request.");
100  next_token = response.GetResult().GetNextSequenceToken();
101  }
102 
103  return status;
104 }
105 
107  Aws::String & next_token, const std::string & log_group, const std::string & log_stream,
108  LogCollection & logs)
109 {
111  Aws::Vector<Aws::CloudWatchLogs::Model::InputLogEvent> events;
112  if (logs.empty()) {
113  status = CW_LOGS_EMPTY_PARAMETER;
114  AWS_LOGSTREAM_WARN(__func__,
115  "Log set is empty, "
116  << log_group << " Log Stream: " <<
117  log_stream << ".");
118  return status;
119  }
120 
121  Aws::CloudWatchLogs::Model::PutLogEventsRequest request;
122  request.SetLogGroupName(log_group.c_str());
123  request.SetLogStreamName(log_stream.c_str());
124 
125  if (next_token != "") {
126  request.SetSequenceToken(next_token);
127  }
128 
129  for (auto & log : logs) {
130  events.push_back(log);
131  if (events.size() >= kMaxLogsPerRequest) {
132  request.SetLogEvents(events);
133  status = SendLogsRequest(request, next_token);
134  events.clear();
135  request.SetSequenceToken(next_token);
136  }
137  if (CW_LOGS_SUCCEEDED != status) {
138  AWS_LOGSTREAM_ERROR(__func__, "Failed to send to CloudWatch in Log Group: "
139  << log_group << " Log Stream: " << log_stream
140  << " with error code: " << status);
141  return status;
142  } else {
143  AWS_LOGSTREAM_DEBUG(__func__,
144  "A batch of logs was successfully sent to CloudWatch in Log Group: "
145  << log_group << " Log Stream: " << log_stream << ".");
146  }
147  }
148 
149  if (!events.empty()) {
150  request.SetLogEvents(events);
151  status = SendLogsRequest(request, next_token);
152  if (CW_LOGS_SUCCEEDED != status) {
153  AWS_LOGSTREAM_ERROR(__func__, "Failed to send to CloudWatch in Log Group: "
154  << log_group << " Log Stream: " << log_stream
155  << " with error code: " << status);
156  } else {
157  AWS_LOGSTREAM_DEBUG(__func__, "All queued logs were successfully sent to CloudWatch in Log Group: "
158  << log_group << " Log Stream: " << log_stream << ".");
159  }
160  }
161 
162  return status;
163 }
164 
166  const std::string & log_group)
167 {
169 
170  Aws::CloudWatchLogs::Model::CreateLogGroupRequest log_group_request;
171  log_group_request.SetLogGroupName(log_group.c_str());
172 
173  const auto & response = this->cw_client_->CreateLogGroup(log_group_request);
174  if (!response.IsSuccess()) {
175 
176  AWS_LOGSTREAM_ERROR(
177  __func__, "Failed to create Log Group :"
178  << log_group << " due to: " << response.GetError().GetMessage()
179  << ", with error code: " << static_cast<int>(response.GetError().GetErrorType()));
180 
181  if (response.GetError().GetErrorType() ==
182  Aws::CloudWatchLogs::CloudWatchLogsErrors::RESOURCE_ALREADY_EXISTS) {
184 
185  } else if(response.GetError().GetErrorType() == Aws::CloudWatchLogs::CloudWatchLogsErrors::NETWORK_CONNECTION) {
186  status = CW_LOGS_NOT_CONNECTED;
187  } else {
189  }
190  }
191 
192  return status;
193 }
194 
196  const std::string & log_group)
197 {
199  Aws::CloudWatchLogs::Model::DescribeLogGroupsRequest describe_log_group_request;
200  Aws::String next_token;
201 
202  describe_log_group_request.SetLogGroupNamePrefix(log_group.c_str());
203 
204  while (CW_LOGS_LOG_GROUP_NOT_FOUND == status) {
205  if (next_token.size() != 0) {
206  describe_log_group_request.SetNextToken(next_token);
207  }
208 
209  const auto & response = this->cw_client_->DescribeLogGroups(describe_log_group_request);
210  if (!response.IsSuccess()) {
211 
212  if(response.GetError().GetErrorType() == Aws::CloudWatchLogs::CloudWatchLogsErrors::NETWORK_CONNECTION) {
213  status = CW_LOGS_NOT_CONNECTED;
214  } else {
215  status = CW_LOGS_FAILED;
216  }
217 
218  AWS_LOGSTREAM_WARN(__func__, "Request to check if log group named "
219  << log_group << " exists failed. Error message: "
220  << response.GetError().GetMessage() << ", with error code: "
221  << static_cast<int>(response.GetError().GetErrorType()));
222 
223  break;
224  }
225 
226  auto & log_group_list = response.GetResult().GetLogGroups();
227  next_token = response.GetResult().GetNextToken();
228 
229  for (const auto & curr_log_group : log_group_list) {
230  if (curr_log_group.GetLogGroupName().c_str() == log_group) {
231  AWS_LOGSTREAM_DEBUG(__func__, "Found Log Group named: " << log_group << ".");
232  status = CW_LOGS_SUCCEEDED;
233  break;
234  }
235  }
236  if (CW_LOGS_SUCCEEDED != status && next_token.size() == 0) {
237  AWS_LOGSTREAM_INFO(__func__, "Failed to find Log Group named: " << log_group << ".");
238  break;
239  }
240  }
241 
242  return status;
243 }
244 
246  const std::string & log_group, const std::string & log_stream)
247 {
249 
250  Aws::CloudWatchLogs::Model::CreateLogStreamRequest log_stream_request;
251  log_stream_request.SetLogGroupName(log_group.c_str());
252  log_stream_request.SetLogStreamName(log_stream.c_str());
253 
254  const auto & response = this->cw_client_->CreateLogStream(log_stream_request);
255  if (!response.IsSuccess()) {
256 
257  AWS_LOGSTREAM_ERROR(__func__, "Failed to create Log Stream :"
258  << log_stream << " in Log Group :" << log_group << " due to: "
259  << response.GetError().GetMessage() << ", with error code: "
260  << static_cast<int>(response.GetError().GetErrorType()));
261  if (response.GetError().GetErrorType() ==
262  Aws::CloudWatchLogs::CloudWatchLogsErrors::RESOURCE_ALREADY_EXISTS) {
264  } else if(response.GetError().GetErrorType() == Aws::CloudWatchLogs::CloudWatchLogsErrors::NETWORK_CONNECTION) {
265  status = CW_LOGS_NOT_CONNECTED;
266  } else {
268  }
269  }
270 
271  return status;
272 }
273 
275  const std::string & log_group, const std::string & log_stream,
276  Aws::CloudWatchLogs::Model::LogStream * log_stream_object)
277 {
279  Aws::CloudWatchLogs::Model::DescribeLogStreamsRequest describe_log_stream_request;
280  Aws::String next_token;
281 
282  describe_log_stream_request.SetLogGroupName(log_group.c_str());
283  describe_log_stream_request.SetLogStreamNamePrefix(log_stream.c_str());
284 
285  while (CW_LOGS_LOG_STREAM_NOT_FOUND == status) {
286  if (next_token.size() != 0) {
287  describe_log_stream_request.SetNextToken(next_token);
288  }
289 
290  const auto & response = this->cw_client_->DescribeLogStreams(describe_log_stream_request);
291  if (!response.IsSuccess()) {
292 
293  if(response.GetError().GetErrorType() == Aws::CloudWatchLogs::CloudWatchLogsErrors::NETWORK_CONNECTION) {
294  status = CW_LOGS_NOT_CONNECTED;
295 
296  } else {
297  status = CW_LOGS_FAILED;
298  }
299 
300  AWS_LOGSTREAM_WARN(
301  __func__, "Request to check if log stream named "
302  << log_stream << " exists in log group named: " << log_group
303  << ". Error message: " << response.GetError().GetMessage()
304  << ", with error code: " << static_cast<int>(response.GetError().GetErrorType()));
305  break;
306  }
307 
308  auto & log_stream_list = response.GetResult().GetLogStreams();
309  next_token = response.GetResult().GetNextToken();
310 
311  for (const auto & curr_log_stream : log_stream_list) {
312  if (curr_log_stream.GetLogStreamName().c_str() == log_stream) {
313  AWS_LOGSTREAM_DEBUG(__func__, "Found Log Stream named: " << log_stream << " in Log Group :"
314  << log_group << ".");
315  if (nullptr != log_stream_object) {
316  *log_stream_object = curr_log_stream;
317  }
318  status = CW_LOGS_SUCCEEDED;
319  break;
320  }
321  }
322  if (CW_LOGS_SUCCEEDED != status && next_token.size() == 0) {
323  AWS_LOGSTREAM_INFO(__func__, "Failed to find Log Stream named: " << log_stream
324  << ".");
325  break;
326  }
327  }
328 
329  return status;
330 }
331 
333  const std::string & log_group, const std::string & log_stream, Aws::String & next_token)
334 {
336  Aws::CloudWatchLogs::Model::LogStream log_stream_object;
337  if (CW_LOGS_SUCCEEDED != CheckLogStreamExists(log_group, log_stream, &log_stream_object)) {
338 
339  if ( status != CW_LOGS_NOT_CONNECTED) {
341  }
342 
343  AWS_LOGSTREAM_ERROR(__func__, "Failed to obtain sequence token due to Log Stream: "
344  << log_stream << " in Log Group :" << log_group
345  << " doesn't exist.");
346  } else {
347  next_token = log_stream_object.GetUploadSequenceToken();
348  }
349 
350  return status;
351 }
352 
353 } // namespace Utils
354 } // namespace CloudWatchLogs
355 } // namespace Aws
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors SendLogsToCloudWatch(Aws::String &next_token, const std::string &log_group, const std::string &log_stream, LogCollection &logs)
Sends a list of logs to CloudWatch.
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors GetLogStreamToken(const std::string &log_group, const std::string &log_stream, Aws::String &next_token)
Gets the next sequence token to use for sending logs to cloudwatch.
std::shared_ptr< Aws::CloudWatchLogs::CloudWatchLogsClient > cw_client_
Aws::CloudWatchLogs::ROSCloudWatchLogsErrors SendLogsRequest(const Aws::CloudWatchLogs::Model::PutLogEventsRequest &request, Aws::String &next_token)
std::list< LogType > LogCollection
Definition: definitions.h:29
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors CheckLogStreamExists(const std::string &log_group, const std::string &log_stream, Aws::CloudWatchLogs::Model::LogStream *log_stream_object)
Check if a log stream in a log group exists.
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors CreateLogStream(const std::string &log_group, const std::string &log_stream)
Creates a log stream in the specified log group.
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors CheckLogGroupExists(const std::string &log_group)
Check if a log group exists.
Contains Error handling functionality for ROS AWS CloudWatch Logs libraries.
virtual Aws::CloudWatchLogs::ROSCloudWatchLogsErrors CreateLogGroup(const std::string &log_group)
Creates a log group.
const std::chrono::milliseconds kMinPutLogsPeriod
ROSCloudWatchLogsErrors
Defines error return codes for functions This enum defines standard error codes that will be returned...


cloudwatch_logs_common
Author(s): AWS RoboMaker
autogenerated on Fri May 7 2021 02:18:24