cpp_client_telemetry/lib/system/EventProperties.cpp

599 строки
20 KiB
C++

//
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
//
#include "pal/PAL.hpp"
#include "CsProtocol_types.hpp"
#include "EventProperty.hpp"
#include "EventProperties.hpp"
#include "EventPropertiesStorage.hpp"
#include "DebugEvents.hpp"
#include "ILogManager.hpp"
#include "utils/StringUtils.hpp"
#include <string>
#include <algorithm>
#include <cctype>
#include <map>
#include "utils/annex_k.hpp"
using namespace std;
using namespace MAT;
namespace MAT_NS_BEGIN {
const char* const DefaultEventName = "undefined";
EventProperties::EventProperties(const std::string& name, const std::map<std::string, EventProperty> &properties) :
EventProperties(name)
{
(*this) += properties;
}
EventProperties& EventProperties::operator+=(const std::map<std::string, EventProperty> &properties)
{
for (auto &kv : properties)
{
auto key = kv.first;
auto val = kv.second;
m_storage->properties[key] = val;
}
return (*this);
}
EventProperties& EventProperties::operator=(const std::map<std::string, EventProperty> &properties)
{
m_storage->properties.clear();
(*this) += properties;
return (*this);
}
EventProperties::EventProperties()
: EventProperties(DefaultEventName)
{
}
/**
* \brief EventProperties constructor
* \param name Event name - must not be empty!
*/
EventProperties::EventProperties(const string& name)
: EventProperties(name, DIAG_LEVEL_OPTIONAL)
{
}
/**
* \brief EventProperties constructor
* \param name Event name - must not be empty!
* \param diagnosticLevel Event diagnostic level for filtering
*/
EventProperties::EventProperties(const string& name, uint8_t diagnosticLevel)
: m_storage(new EventPropertiesStorage())
{
if (!name.empty())
{
SetName(name);
}
else {
SetName(DefaultEventName);
}
SetLevel(diagnosticLevel);
}
EventProperties::EventProperties(EventProperties const& copy)
{
m_storage = new EventPropertiesStorage(*copy.m_storage);
}
EventProperties& EventProperties::operator=(EventProperties const& copy)
{
*m_storage = *copy.m_storage;
return *this;
}
EventProperties::~EventProperties() noexcept
{
delete m_storage;
}
/// <summary>
/// EventProperties constructor using C++11 initializer list
/// </summary>
EventProperties::EventProperties(const std::string& name, std::initializer_list<std::pair<std::string const, EventProperty> > properties)
: EventProperties(name)
{
(*this) = properties;
}
/// <summary>
/// EventProperties assignment operator using C++11 initializer list
/// </summary>
EventProperties& EventProperties::operator=(std::initializer_list<std::pair<std::string const, EventProperty> > properties)
{
m_storage->properties.clear();
m_storage->propertiesPartB.clear();
for (auto &kv : properties)
{
auto key = kv.first;
auto val = kv.second;
m_storage->properties[key] = val;
}
return (*this);
};
/// <summary>
/// Set the Epoch unix timestamp in milliseconds of the event.
/// This will override default timestamp generated by telemetry system.
/// <param name="timestampInEpochMillis">Unix timestamp in milliseconds since 00:00:00
/// Coordinated Universal Time (UTC), 1 January 1970 not counting leap seconds</param>
/// </summary>
void EventProperties::SetTimestamp(const int64_t timestampInEpochMillis)
{
m_storage->timestampInMillis = timestampInEpochMillis;
}
/// <summary>
/// Returns the timestamp of the event.
/// If this was not set explicitly by calling SetTimestamp, it will return 0 by default.
/// </summary>
int64_t EventProperties::GetTimestamp() const
{
return m_storage->timestampInMillis;
}
/// <summary>
/// Set transmit priority of this event
/// Default transmit priority will be used if none specified
/// </summary>
void EventProperties::SetPriority(EventPriority priority)
{
m_storage->eventLatency = (EventLatency)priority;
if (priority >= EventPriority_High)
{
m_storage->eventLatency = EventLatency_RealTime;
m_storage->eventPersistence = EventPersistence_Critical;
}
else
if (priority >= EventPriority_Low)
{
// TODO: 1438270 - [v3][1DS] Direct upload to respect low priority
// Any changes to this method needs corresponding fixes in Java code.
m_storage->eventLatency = EventLatency_Normal;
m_storage->eventPersistence = EventPersistence_Normal;
}
}
/// <summary>
/// Get transmit priority of this event
/// Default transmit priority will be used if none specified
/// </summary>
EventPriority EventProperties::GetPriority() const
{
return static_cast<EventPriority>(m_storage->eventLatency);
}
/// <summary>
/// Set transmit Latency of this event
/// Default transmit Latency will be used if none specified
/// </summary>
void EventProperties::SetLatency(EventLatency latency)
{
m_storage->eventLatency = latency;
}
/// <summary>
/// Get transmit priority of this event
/// Default transmit priority will be used if none specified
/// </summary>
EventLatency EventProperties::GetLatency() const
{
return m_storage->eventLatency;
}
/// <summary>
/// [optional] Specify Persistence priority of an event.
/// Default Persistence priority will be used for persisting the event if none was specified.
/// </summary>
void EventProperties::SetPersistence(EventPersistence persistence)
{
m_storage->eventPersistence = persistence;
}
/// <summary>
/// Get Persistence of this event
/// Default Persistence will be used if none specified
/// </summary>
EventPersistence EventProperties::GetPersistence() const
{
return m_storage->eventPersistence;
}
/// <summary>
/// [optional] Specify popSample of an event.
/// </summary>
/// <param name="priority">popSample of the event</param>
void EventProperties::SetPopsample(double popSample)
{
m_storage->eventPopSample = popSample;
}
/// <summary>
/// Get the popSample of the event.
/// </summary>
/// <returns>popSample of the event<returns>
double EventProperties::GetPopSample() const
{
return m_storage->eventPopSample;
}
/// <summary>
/// Specify Policy Bit flags for UTC usage of an event.
/// </summary>
void EventProperties::SetPolicyBitFlags(uint64_t policyBitFlags)
{
m_storage->eventPolicyBitflags = policyBitFlags;
}
/// <summary>
/// Get the Policy bit flags for UTC usage of the event.
/// </summary>
uint64_t EventProperties::GetPolicyBitFlags() const
{
return m_storage->eventPolicyBitflags;
}
/// <summary>
/// Set name of this event
/// Default name will be used if none specified (e.g. for LogPageView, name = "PageView")
/// </summary>
bool EventProperties::SetName(const string& name)
{
std::string sanitizedEventName = sanitizeIdentifier(name);
EventRejectedReason isValidEventName = validateEventName(sanitizedEventName);
if (isValidEventName != REJECTED_REASON_OK) {
LOG_ERROR("Invalid event name!");
DebugEvent evt;
evt.type = DebugEventType::EVT_REJECTED;
evt.param1 = isValidEventName;
ILogManager::DispatchEventBroadcast(evt);
return false;
}
m_storage->eventName.assign(sanitizedEventName);
return true;
}
/// <summary>
/// Returns the name for this event.
/// If this was not set explicitly by calling SetName, it will return an empty string.
/// </summary>
const string& EventProperties::GetName() const
{
return m_storage->eventName;
}
/// <summary>
/// Specify the Base Type of an event. This field is populated in Records.Type
/// </summary>
bool EventProperties::SetType(const string& recordType)
{
std::string eventType = toLower(recordType);
eventType = sanitizeIdentifier(eventType);
EventRejectedReason isValidEventName = validateEventName(eventType);
if (isValidEventName != REJECTED_REASON_OK) {
LOG_ERROR("Invalid event type!");
DebugEvent evt;
evt.type = DebugEventType::EVT_REJECTED;
evt.param1 = isValidEventName;
ILogManager::DispatchEventBroadcast(evt);
return false;
}
m_storage->eventType.assign(eventType);
return true;
}
/// <summary>
/// Returns the Base Type for this event.
/// If this was not set explicitly by calling SetType, it will return an empty string.
/// </summary>
const string& EventProperties::GetType() const
{
return m_storage->eventType;
}
std::tuple<bool, uint8_t> EventProperties::TryGetLevel() const
{
const auto& findResult = GetProperties().find(COMMONFIELDS_EVENT_LEVEL);
if (findResult == GetProperties().cend())
return std::make_tuple<bool, uint8_t>(false, 0);
const auto& property = findResult->second;
if (property.type != EventProperty::TYPE_INT64)
return std::make_tuple<bool, uint8_t>(false, 0);
const auto& value = property.as_int64;
if (value < 0 || value > UINT8_MAX)
return std::make_tuple<bool, uint8_t>(false, 0);
return std::make_tuple<bool, uint8_t>(true, static_cast<uint8_t>(value));
}
/// <summary>
/// Specify a property of an event
/// It creates a new property if none exists or overwrites an existing one
/// <param name='name'>Name of the property</param>
/// <param name='value'>String value of the property</param>
/// <param name='piiKind'>PIIKind of the property</param>
/// </summary>
void EventProperties::SetProperty(const string& name, EventProperty prop)
{
EventRejectedReason isValidPropertyName = validatePropertyName(name);
if (isValidPropertyName != REJECTED_REASON_OK)
{
LOG_ERROR("Context name is invalid: %s", name.c_str());
DebugEvent evt;
evt.type = DebugEventType::EVT_REJECTED;
evt.param1 = isValidPropertyName;
ILogManager::DispatchEventBroadcast(evt);
return;
}
m_storage->properties[name] = prop;
}
//
void EventProperties::SetProperty(const std::string& name, char const* value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, const std::string& value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, double value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, int64_t value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, bool value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, time_ticks_t value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, GUID_t value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, std::vector<int64_t>& value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, std::vector<double>& value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, std::vector<GUID_t>& value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
void EventProperties::SetProperty(const std::string& name, std::vector<std::string>& value, PiiKind piiKind, DataCategory category) { SetProperty(name, EventProperty(value, piiKind, category)); }
const map<string, EventProperty>& EventProperties::GetProperties(DataCategory category) const
{
if (category == DataCategory_PartC)
{
return m_storage->properties;
}
else
{
return m_storage->propertiesPartB;
}
}
/// <summary>
/// Erase property from event.
/// </summary>
size_t EventProperties::erase(const std::string& key, DataCategory category)
{
size_t result = 0;
auto &props = (category == DataCategory_PartC) ? m_storage->properties : m_storage->propertiesPartB;
result = props.erase(key);
return result;
}
/// <summary>
/// Get Pii properties map
/// </summary>
/// <returns></returns>
const map<string, pair<string, PiiKind> > EventProperties::GetPiiProperties(DataCategory category) const
{
std::map<string, pair<string, PiiKind> > pIIExtensions;
auto &props = (category == DataCategory_PartC) ? m_storage->properties : m_storage->propertiesPartB;
for (const auto &kv : props)
{
auto k = kv.first;
auto v = kv.second;
if (v.piiKind != PiiKind_None)
{
pIIExtensions[k] = std::pair<string, PiiKind>(v.to_string(), v.piiKind);
}
}
return pIIExtensions;
}
#ifdef MAT_C_API
static inline void cppprop_to_cprop(EventProperty &rhs, evt_prop &lhs)
{
switch (rhs.type)
{
case TYPE_STRING:
lhs.value.as_string = rhs.as_string;
break;
case TYPE_INT64:
lhs.value.as_int64 = rhs.as_int64;
break;
case TYPE_DOUBLE:
lhs.value.as_double = rhs.as_double;
break;
case TYPE_TIME:
lhs.value.as_time = rhs.as_time_ticks.ticks;
break;
case TYPE_BOOLEAN:
lhs.value.as_bool = rhs.as_bool;
break;
case TYPE_GUID:
lhs.value.as_guid = new evt_guid_t();
break;
#if 0
case TYPE_STRING_ARRAY:
// TODO: Not implemented
break;
case TYPE_INT64_ARRAY:
// TODO: Not implemented
break;
case TYPE_DOUBLE_ARRAY:
// TODO: Not implemented
break;
case TYPE_GUID_ARRAY:
// TODO: Not implemented
break;
#endif
default:
break;
}
}
evt_prop* EventProperties::pack()
{
size_t size = m_storage->properties.size() + m_storage->propertiesPartB.size() + 1;
evt_prop * result = static_cast<evt_prop *>(calloc(sizeof(evt_prop), size));
if (result==nullptr)
{
LOG_ERROR("Unable to allocate memory to pack EventProperties");
return result;
};
size_t i = 0;
for(auto &props : { m_storage->properties, m_storage->propertiesPartB })
for (auto &kv : props)
{
auto k = kv.first;
auto v = kv.second;
result[i].name = (char *)k.c_str();
result[i].type = static_cast<evt_prop_t>(v.type);
result[i].piiKind = v.piiKind;
cppprop_to_cprop(v, result[i]);
};
result[size-1].type = TYPE_NULL;
return result;
}
// List of attributes going into envelope section. Construct string objects once, reuse for comparison.
// Short names for event field keys passed via C API
static const std::string KEY_NAME = "name";
static const std::string KEY_TIME = "time";
static const std::string KEY_POPSAMPLE = "popSample";
// Long names for event field keys passed via C API
static const std::string KEY_EVENTNAME = COMMONFIELDS_EVENT_NAME;
static const std::string KEY_EVENTTIME = COMMONFIELDS_EVENT_TIME;
static const std::string KEY_PRIORITY = COMMONFIELDS_EVENT_PRIORITY;
static const std::string KEY_LATENCY = COMMONFIELDS_EVENT_LATENCY;
static const std::string KEY_PRSIST = COMMONFIELDS_EVENT_PERSISTENCE;
static const std::string KEY_POLICY = COMMONFIELDS_EVENT_POLICYFLAGS;
bool EventProperties::unpack(const evt_prop *packed, size_t size)
{
const evt_prop *curr = packed;
if (packed==nullptr)
{
// Invalid input (nullptr) from C API results in an empty property bag
return false;
};
// Verify size using size_t size parameter passed down by evt_log_s API call
if (size == 0)
{
size = SIZE_MAX;
}
for (size_t i = 0; (i<size)&&(curr->type != TYPE_NULL); i++, curr++)
{
// Event priority
if (KEY_PRIORITY == curr->name)
{
SetPriority(static_cast<MAT::EventPriority>(curr->value.as_int64));
continue;
}
// Event latency
if (KEY_LATENCY == curr->name)
{
SetLatency(static_cast<MAT::EventLatency>(curr->value.as_int64));
continue;
}
// Event persistence
if (KEY_PRSIST == curr->name)
{
SetPersistence(static_cast<MAT::EventPersistence>(curr->value.as_int64));
continue;
}
// Event name
if ((KEY_NAME == curr->name) || (KEY_EVENTNAME == curr->name))
{
SetName(curr->value.as_string);
continue;
}
// Event time
if ((KEY_EVENTTIME == curr->name) || (KEY_TIME == curr->name))
{
SetTimestamp(curr->value.as_int64);
continue;
}
// Event popSample attribute
if (KEY_POPSAMPLE == curr->name)
{
SetPopsample(curr->value.as_double);
continue;
}
// Event policy for UTC mode
if (KEY_POLICY == curr->name)
{
SetPolicyBitFlags(static_cast<uint64_t>(curr->value.as_int64));
continue;
}
switch (curr->type)
{
case TYPE_STRING:
SetProperty(curr->name, curr->value.as_string, (PiiKind)curr->piiKind);
break;
case TYPE_INT64:
SetProperty(curr->name, curr->value.as_int64, (PiiKind)curr->piiKind);
break;
case TYPE_DOUBLE:
SetProperty(curr->name, curr->value.as_double, (PiiKind)curr->piiKind);
break;
case TYPE_TIME:
SetProperty(curr->name, time_ticks_t(curr->value.as_time), (PiiKind)curr->piiKind);
break;
case TYPE_BOOLEAN:
SetProperty(curr->name, curr->value.as_bool, (PiiKind)curr->piiKind);
break;
case TYPE_GUID:
{
GUID_t guid(curr->value.as_string);
SetProperty(curr->name, guid, (PiiKind)curr->piiKind);
}
break;
/* TODO: add support for arrays passing across C API
// Arrays of basic types
TYPE_STRING_ARRAY
TYPE_INT64_ARRAY
TYPE_DOUBLE_ARRAY
TYPE_TIME_ARRAY
TYPE_BOOL_ARRAY
TYPE_GUID_ARRAY
*/
default:
break;
}
}
return true;
}
#endif /* end of MAT_C_API */
} MAT_NS_END