1+ /* *
2+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+ * SPDX-License-Identifier: Apache-2.0.
4+ */
5+
6+ #include < aws/core/client/AWSClient.h>
7+ #include < aws/core/monitoring/CoreMetrics.h>
8+ #include < aws/core/monitoring/HttpClientMetrics.h>
9+ #include < aws/core/monitoring/MonitoringInterface.h>
10+ #include < aws/core/utils/Array.h>
11+ #include < aws/core/utils/DateTime.h>
12+ #include < aws/core/utils/StringUtils.h>
13+ #include < aws/core/utils/json/JsonSerializer.h>
14+ #include < aws/core/utils/memory/AWSMemory.h>
15+ #include < aws/core/utils/memory/stl/AWSMap.h>
16+ #include < aws/core/utils/memory/stl/AWSSet.h>
17+ #include < aws/core/utils/memory/stl/AWSString.h>
18+ #include < aws/core/utils/memory/stl/AWSVector.h>
19+ #include < performance_tests/reporting/JsonReportingMetrics.h>
20+
21+ #include < cstddef>
22+ #include < cstdint>
23+ #include < fstream>
24+ #include < memory>
25+ #include < utility>
26+
27+ using namespace PerformanceTest ::Reporting;
28+
29+ Aws::Vector<std::pair<Aws::String, Aws::String>> JsonReportingMetrics::TestDimensions;
30+ Aws::Set<Aws::String> JsonReportingMetrics::MonitoredOperations;
31+ Aws::String JsonReportingMetrics::ProductId = " unknown" ;
32+ Aws::String JsonReportingMetrics::SdkVersion = " unknown" ;
33+ Aws::String JsonReportingMetrics::CommitId = " unknown" ;
34+ Aws::String JsonReportingMetrics::OutputFilename = " performance-test-results.json" ;
35+
36+ void JsonReportingMetrics::SetTestContext (const Aws::Vector<std::pair<Aws::String, Aws::String>>& dimensions) {
37+ TestDimensions = dimensions;
38+ }
39+
40+ void JsonReportingMetrics::RegisterOperationsToMonitor (const Aws::Vector<Aws::String>& operations) {
41+ MonitoredOperations.clear ();
42+ for (const auto & operation : operations) {
43+ MonitoredOperations.insert (operation);
44+ }
45+ }
46+
47+ void JsonReportingMetrics::SetProductInfo (const Aws::String& productId, const Aws::String& sdkVersion, const Aws::String& commitId) {
48+ ProductId = productId;
49+ SdkVersion = sdkVersion;
50+ CommitId = commitId;
51+ }
52+
53+ void JsonReportingMetrics::SetOutputFilename (const Aws::String& filename) { OutputFilename = filename; }
54+
55+ JsonReportingMetrics::~JsonReportingMetrics () { DumpJson (); }
56+
57+ Aws::UniquePtr<Aws::Monitoring::MonitoringInterface> JsonReportingMetricsFactory::CreateMonitoringInstance () const {
58+ return Aws::MakeUnique<JsonReportingMetrics>(" JsonReportingMetrics" );
59+ }
60+
61+ void JsonReportingMetrics::AddPerformanceRecord (const Aws::String& serviceName, const Aws::String& requestName,
62+ const Aws::Monitoring::CoreMetricsCollection& metricsFromCore) const {
63+ // If no operations are registered, monitor all operations. Otherwise, only monitor registered operations
64+ if (!MonitoredOperations.empty () && MonitoredOperations.find (requestName) == MonitoredOperations.end ()) {
65+ return ;
66+ }
67+
68+ int64_t durationMs = 0 ;
69+ Aws::String const latencyKey = Aws::Monitoring::GetHttpClientMetricNameByType (Aws::Monitoring::HttpClientMetricsType::RequestLatency);
70+
71+ auto iterator = metricsFromCore.httpClientMetrics .find (latencyKey);
72+ if (iterator != metricsFromCore.httpClientMetrics .end ()) {
73+ durationMs = iterator->second ;
74+ }
75+
76+ PerformanceMetricRecord record;
77+ record.name =
78+ Aws::Utils::StringUtils::ToLower (serviceName.c_str ()) + " ." + Aws::Utils::StringUtils::ToLower (requestName.c_str ()) + " .latency" ;
79+ record.description = " Time to complete " + requestName + " operation" ;
80+ record.unit = " Milliseconds" ;
81+ record.date = Aws::Utils::DateTime::Now ().Seconds ();
82+ record.measurements .push_back (durationMs);
83+ record.dimensions = TestDimensions;
84+
85+ m_performanceRecords.push_back (record);
86+ }
87+
88+ void * JsonReportingMetrics::OnRequestStarted (const Aws::String&, const Aws::String&,
89+ const std::shared_ptr<const Aws::Http::HttpRequest>&) const {
90+ return nullptr ;
91+ }
92+
93+ void JsonReportingMetrics::OnRequestSucceeded (const Aws::String& serviceName, const Aws::String& requestName,
94+ const std::shared_ptr<const Aws::Http::HttpRequest>&, const Aws::Client::HttpResponseOutcome&,
95+ const Aws::Monitoring::CoreMetricsCollection& metricsFromCore, void *) const {
96+ AddPerformanceRecord (serviceName, requestName, metricsFromCore);
97+ }
98+
99+ void JsonReportingMetrics::OnRequestFailed (const Aws::String& serviceName, const Aws::String& requestName,
100+ const std::shared_ptr<const Aws::Http::HttpRequest>&, const Aws::Client::HttpResponseOutcome&,
101+ const Aws::Monitoring::CoreMetricsCollection& metricsFromCore, void *) const {
102+ AddPerformanceRecord (serviceName, requestName, metricsFromCore);
103+ }
104+
105+ void JsonReportingMetrics::OnRequestRetry (const Aws::String&, const Aws::String&, const std::shared_ptr<const Aws::Http::HttpRequest>&,
106+ void *) const {}
107+
108+ void JsonReportingMetrics::OnFinish (const Aws::String&, const Aws::String&, const std::shared_ptr<const Aws::Http::HttpRequest>&,
109+ void *) const {}
110+
111+ void JsonReportingMetrics::DumpJson () const {
112+ if (m_performanceRecords.empty ()) {
113+ return ;
114+ }
115+
116+ // Group performance records by name and dimensions
117+ Aws::Map<Aws::String, PerformanceMetricRecord> aggregatedRecords;
118+
119+ for (const auto & record : m_performanceRecords) {
120+ Aws::String key = record.name ;
121+ for (const auto & dim : record.dimensions ) {
122+ key += " :" + Aws::String (dim.first ) + " =" + Aws::String (dim.second );
123+ }
124+
125+ if (aggregatedRecords.find (key) == aggregatedRecords.end ()) {
126+ aggregatedRecords[key] = record;
127+ } else {
128+ for (const auto & measurement : record.measurements ) {
129+ aggregatedRecords[key].measurements .push_back (measurement);
130+ }
131+ }
132+ }
133+
134+ // Create the JSON output
135+ Aws::Utils::Json::JsonValue root;
136+ root.WithString (" productId" , ProductId);
137+ root.WithString (" sdkVersion" , SdkVersion);
138+ root.WithString (" commitId" , CommitId);
139+
140+ Aws::Utils::Array<Aws::Utils::Json::JsonValue> results (aggregatedRecords.size ());
141+ size_t index = 0 ;
142+
143+ for (const auto & pair : aggregatedRecords) {
144+ const auto & record = pair.second ;
145+ Aws::Utils::Json::JsonValue jsonMetric;
146+ jsonMetric.WithString (" name" , record.name );
147+ jsonMetric.WithString (" description" , record.description );
148+ jsonMetric.WithString (" unit" , record.unit );
149+ jsonMetric.WithInt64 (" date" , record.date );
150+
151+ if (!record.dimensions .empty ()) {
152+ Aws::Utils::Array<Aws::Utils::Json::JsonValue> dimensionsArray (record.dimensions .size ());
153+ for (size_t j = 0 ; j < record.dimensions .size (); ++j) {
154+ Aws::Utils::Json::JsonValue dimension;
155+ dimension.WithString (" name" , record.dimensions [j].first );
156+ dimension.WithString (" value" , record.dimensions [j].second );
157+ dimensionsArray[j] = std::move (dimension);
158+ }
159+ jsonMetric.WithArray (" dimensions" , std::move (dimensionsArray));
160+ }
161+
162+ Aws::Utils::Array<Aws::Utils::Json::JsonValue> measurementsArray (record.measurements .size ());
163+ for (size_t j = 0 ; j < record.measurements .size (); ++j) {
164+ Aws::Utils::Json::JsonValue measurementValue;
165+ measurementValue.AsInt64 (record.measurements [j]);
166+ measurementsArray[j] = std::move (measurementValue);
167+ }
168+ jsonMetric.WithArray (" measurements" , std::move (measurementsArray));
169+
170+ results[index++] = std::move (jsonMetric);
171+ }
172+
173+ root.WithArray (" results" , std::move (results));
174+
175+ std::ofstream outFile (OutputFilename.c_str ());
176+ if (outFile.is_open ()) {
177+ outFile << root.View ().WriteReadable ();
178+ }
179+ }
0 commit comments