MetricsEvent.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <MetricsEvent.h>
  9. #include <AWSMetricsConstant.h>
  10. #include <Framework/JsonWriter.h>
  11. #include <AzCore/JSON/schema.h>
  12. #include <AzCore/Serialization/Json/JsonSerialization.h>
  13. #include <AzCore/Serialization/Json/JsonUtils.h>
  14. #include <sstream>
  15. namespace AWSMetrics
  16. {
  17. void MetricsEvent::AddAttribute(const MetricsAttribute& attribute)
  18. {
  19. AZStd::string attributeName = attribute.GetName();
  20. if (attributeName.empty())
  21. {
  22. AZ_Error("AWSMetrics", false, "Invalid metrics attribute. Attribute name is empty.");
  23. return;
  24. }
  25. if (AttributeExists(attributeName))
  26. {
  27. // Avoid overwriting the existing attribute value since it's not clear which one developers need to keep.
  28. AZ_Error("AWSMetrics", false, "Metrics attribute %s already exists.", attributeName.c_str());
  29. return;
  30. }
  31. m_sizeSerializedToJson += attribute.GetSizeInBytes();
  32. m_attributes.emplace_back(AZStd::move(attribute));
  33. }
  34. bool MetricsEvent::AttributeExists(const AZStd::string& attributeName) const
  35. {
  36. auto itr = AZStd::find_if(m_attributes.begin(), m_attributes.end(),
  37. [attributeName](const MetricsAttribute& existingAttribute) -> bool
  38. {
  39. return (attributeName == existingAttribute.GetName());
  40. });
  41. return itr != m_attributes.end();
  42. }
  43. void MetricsEvent::AddAttributes(const AZStd::vector<MetricsAttribute>& attributes)
  44. {
  45. for (const MetricsAttribute& attribute : attributes)
  46. {
  47. AddAttribute(attribute);
  48. }
  49. }
  50. int MetricsEvent::GetNumAttributes() const
  51. {
  52. return static_cast<int>(m_attributes.size());
  53. }
  54. size_t MetricsEvent::GetSizeInBytes() const
  55. {
  56. return m_sizeSerializedToJson;
  57. }
  58. bool MetricsEvent::SerializeToJson(AWSCore::JsonWriter& writer) const
  59. {
  60. bool ok = true;
  61. ok = ok && writer.StartObject();
  62. AZStd::vector<MetricsAttribute> customAttributes;
  63. for (const auto& attr : m_attributes)
  64. {
  65. if (attr.IsDefault())
  66. {
  67. ok = ok && writer.Key(attr.GetName().c_str());
  68. ok = ok && attr.SerializeToJson(writer);
  69. }
  70. else
  71. {
  72. customAttributes.emplace_back(attr);
  73. }
  74. }
  75. if (customAttributes.size() > 0)
  76. {
  77. // Wrap up the cutom event attributes in a separate event_data field
  78. ok = ok && writer.Key(AwsMetricsAttributeKeyEventData);
  79. ok = ok && writer.StartObject();
  80. for (const auto& attr : customAttributes)
  81. {
  82. ok = ok && writer.Key(attr.GetName().c_str());
  83. ok = ok && attr.SerializeToJson(writer);
  84. }
  85. ok = ok && writer.EndObject();
  86. }
  87. ok = ok && writer.EndObject();
  88. return ok;
  89. }
  90. bool MetricsEvent::ReadFromJson(rapidjson::Value& metricsObjVal)
  91. {
  92. if (!metricsObjVal.IsObject())
  93. {
  94. AZ_Error("AWSMetrics", false, "Invalid JSON value type. Expect an object");
  95. return false;
  96. }
  97. int attributeIndex = 0;
  98. for (auto it = metricsObjVal.MemberBegin(); it != metricsObjVal.MemberEnd(); ++it, ++attributeIndex)
  99. {
  100. if (strcmp(it->name.GetString(), AwsMetricsAttributeKeyEventData) == 0)
  101. {
  102. // The event_data field contains a flat json dictionary.
  103. // Read the JSON value of this field to add all the custom metrics attributes.
  104. if (!ReadFromJson(it->value))
  105. {
  106. return false;
  107. }
  108. }
  109. else
  110. {
  111. MetricsAttribute attribute;
  112. // Read through each element in the array and add it as a new metrics attribute
  113. if (!attribute.ReadFromJson(it->name, it->value))
  114. {
  115. AZ_Error("AWSMetrics", false, "Metrics attribute %s is invalid", it->name.GetString());
  116. return false;
  117. }
  118. AddAttribute(attribute);
  119. }
  120. }
  121. return true;
  122. }
  123. bool MetricsEvent::ValidateAgainstSchema()
  124. {
  125. std::stringstream stringStream;
  126. AWSCore::JsonOutputStream jsonStream{stringStream};
  127. AWSCore::JsonWriter writer{jsonStream};
  128. if (!SerializeToJson(writer))
  129. {
  130. return false;
  131. }
  132. auto result = AZ::JsonSerializationUtils::ReadJsonString(stringStream.str().c_str());
  133. if (!result.IsSuccess())
  134. {
  135. return false;
  136. }
  137. rapidjson::Document jsonSchemaDocument;
  138. if (jsonSchemaDocument.Parse(AwsMetricsEventJsonSchema).HasParseError())
  139. {
  140. AZ_Error("AWSMetrics", false, "Invalid metrics event json schema.");
  141. return false;
  142. }
  143. auto jsonSchema = rapidjson::SchemaDocument(jsonSchemaDocument);
  144. rapidjson::SchemaValidator validator(jsonSchema);
  145. if (!result.GetValue().Accept(validator))
  146. {
  147. rapidjson::StringBuffer error;
  148. validator.GetInvalidSchemaPointer().StringifyUriFragment(error);
  149. AZ_Warning("AWSMetrics", false, "Failed to load the metrics event, invalid schema: %s.", error.GetString());
  150. AZ_Warning("AWSMetrics", false, "Failed to load the metrics event, invalid keyword: %s.", validator.GetInvalidSchemaKeyword());
  151. error.Clear();
  152. validator.GetInvalidDocumentPointer().StringifyUriFragment(error);
  153. AZ_Warning("AWSMetrics", false, "Failed to load the metrics event, invalid document: %s.", error.GetString());
  154. return false;
  155. }
  156. return true;
  157. }
  158. void MetricsEvent::MarkFailedSubmission()
  159. {
  160. ++m_numFailures;
  161. }
  162. int MetricsEvent::GetNumFailures() const
  163. {
  164. return m_numFailures;
  165. }
  166. void MetricsEvent::SetEventPriority(int priority)
  167. {
  168. m_eventPriority = priority;
  169. }
  170. int MetricsEvent::GetEventPriority() const
  171. {
  172. return m_eventPriority;
  173. }
  174. }