v2/inc/c_logging/log_context.h (205 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#ifndef LOG_CONTEXT_H
#define LOG_CONTEXT_H
#ifdef __cplusplus
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#else
#include <inttypes.h>
#include <stdio.h>
#include <wchar.h>
#include <stdlib.h>
#endif
#include "macro_utils/macro_utils.h"
#include "c_logging/log_context_property_value_pair.h"
#include "c_logging/log_context_property_basic_types.h"
#include "c_logging/log_context_property_type_ascii_char_ptr.h"
#include "c_logging/log_context_property_type_wchar_t_ptr.h"
#include "c_logging/log_context_property_type_struct.h"
#include "c_logging/log_internal_error.h"
#ifdef __cplusplus
extern "C" {
#endif
#define LOG_MAX_STACK_DATA_SIZE 4096
#define LOG_MAX_STACK_PROPERTY_VALUE_PAIR_COUNT 64
typedef struct LOG_CONTEXT_TAG
{
uint8_t* values_data;
uint32_t values_data_length;
LOG_CONTEXT_PROPERTY_VALUE_PAIR* property_value_pairs_ptr;
uint32_t property_value_pair_count;
LOG_CONTEXT_PROPERTY_VALUE_PAIR property_value_pairs[];
} LOG_CONTEXT;
typedef struct LOG_CONTEXT_TAG* LOG_CONTEXT_HANDLE;
uint32_t log_context_get_property_value_pair_count(LOG_CONTEXT_HANDLE log_context);
const LOG_CONTEXT_PROPERTY_VALUE_PAIR* log_context_get_property_value_pairs(LOG_CONTEXT_HANDLE log_context);
// These is an internal API and it is not meant to be called by the users of this module
int internal_log_context_init_from_parent(LOG_CONTEXT_HANDLE dest_log_context, LOG_CONTEXT_HANDLE parent_log_context);
uint32_t internal_log_context_get_values_data_length_or_zero(LOG_CONTEXT_HANDLE log_context);
// macro set used to define a parameter in a function signature in order
// to make sure that no properties with the same name are added in one context
// DEFINE_PROPERTY_AS_PARAMETER
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_MESSAGE(...) \
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_CONTEXT_STRING_PROPERTY(property_name, ...) \
, int property_name
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_CONTEXT_WSTRING_PROPERTY(property_name, ...) \
, int property_name
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_CONTEXT_NAME(log_context_name) \
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_CONTEXT_PROPERTY(property_type, property_name, field_value) \
, int property_name
#define EXPAND_DEFINE_PROPERTY_AS_PARAMETER_LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION(property_type, property_name, value_function, ...) \
, int property_name
#define DEFINE_PROPERTY_AS_PARAMETER(field_desc) \
MU_C2(EXPAND_DEFINE_PROPERTY_AS_PARAMETER_, field_desc)
// macro set used to define a parameter in a function signature every time LOG_CONTEXT_NAME is used.
// if used twice a field with the same name is defined => compiler error
// DEFINE_LOG_CONTEXT_NAME_AS_FIELD
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_MESSAGE(...) \
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_CONTEXT_STRING_PROPERTY(property_name, ...) \
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_CONTEXT_WSTRING_PROPERTY(property_name, ...) \
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_CONTEXT_NAME(log_context_name) \
, int log_context_is_used_multiple_times
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_CONTEXT_PROPERTY(property_type, property_name, field_value) \
#define EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION(property_type, property_name, value_function, ...) \
#define DEFINE_CONTEXT_NAME_AS_PARAMETER(field_desc) \
MU_C2(EXPAND_DEFINE_CONTEXT_NAME_AS_PARAMETER_, field_desc)
// ... here is a list of properties, for example:
// LOG_CONTEXT_PROPERTY(int32_t, x, 42), LOG_CONTEXT_STRING_PROPERTY(y, "gogu"), ...
#define LOG_CONTEXT_CHECK_VARIABLE_ARGS(...) \
/* Codes_SRS_LOG_CONTEXT_01_019: [ If 2 properties have the same property_name for a context a compiler error shall be emitted. ]*/ \
/* Codes_SRS_LOG_CONTEXT_01_026: [ If 2 properties have the same property_name for a context a compiler error shall be emitted. ]*/ \
/* Codes_SRS_LOG_CONTEXT_01_030: [ If 2 properties have the same property_name for a context a compiler error shall be emitted. ]*/ \
(void)(void (*)(int MU_FOR_EACH_1(DEFINE_PROPERTY_AS_PARAMETER, __VA_ARGS__)))0x4242; \
/* Codes_SRS_LOG_CONTEXT_01_011: [ If LOG_CONTEXT_NAME is specified multiple times a compiler error shall be emitted. ]*/ \
(void)(void (*)(int MU_FOR_EACH_1(DEFINE_CONTEXT_NAME_AS_PARAMETER, __VA_ARGS__)))0x4242; \
// SETUP_PROPERTY_PAIR
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_MESSAGE(...) \
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_CONTEXT_STRING_PROPERTY(property_name, ...) \
/* Codes_SRS_LOG_CONTEXT_01_007: [ LOG_CONTEXT_STRING_PROPERTY shall expand to code allocating a property/value pair of type ascii_char_ptr and the name property_name. ]*/ \
property_value_pair->value = data_pos; \
property_value_pair->name = MU_TOSTRING(property_name); \
property_value_pair->type = &ascii_char_ptr##_log_context_property_type; \
/* Codes_SRS_LOG_CONTEXT_01_008: [ LOG_CONTEXT_STRING_PROPERTY shall expand to code that stores as value a string that is constructed using printf-like formatting based on format and all the arguments in .... ]*/ \
data_pos += sprintf(property_value_pair->value, __VA_ARGS__) + 1; \
property_value_pair++; \
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_CONTEXT_WSTRING_PROPERTY(property_name, ...) \
/* Codes_SRS_LOG_CONTEXT_07_001: [ LOG_CONTEXT_WSTRING_PROPERTY shall expand to code allocating a property/value pair of type wchar_t_ptr and the name property_name. ]*/ \
property_value_pair->value = data_pos; \
property_value_pair->name = MU_TOSTRING(property_name); \
property_value_pair->type = &MU_C2A(wchar_t_ptr, _log_context_property_type); \
/* Codes_SRS_LOG_CONTEXT_07_002: [ LOG_CONTEXT_WSTRING_PROPERTY shall expand to code that stores as value a wchar string that is constructed using wprintf-like formatting based on format and all the arguments in .... ]*/ \
data_pos += swprintf(property_value_pair->value, LOG_MAX_WCHAR_STRING_LENGTH, __VA_ARGS__)*sizeof(wchar_t) + sizeof(wchar_t); \
property_value_pair++; \
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_CONTEXT_NAME(log_context_name) \
/* Codes_SRS_LOG_CONTEXT_01_012: [ The name of the struct property shall be the context name specified by using LOG_CONTEXT_NAME (if specified). ]*/ \
/* Codes_SRS_LOG_CONTEXT_01_017: [ The name of the struct property shall be the context name specified by using LOG_CONTEXT_NAME (if specified). ]*/ \
first_property_value_pair->name = MU_TOSTRING(log_context_name); \
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_CONTEXT_PROPERTY(property_type, property_name, field_value) \
/* Codes_SRS_LOG_CONTEXT_01_004: [ LOG_CONTEXT_PROPERTY shall expand to code allocating a property/value pair entry with the type property_type and the name property_name. ]*/ \
property_value_pair->value = data_pos; \
property_value_pair->name = MU_TOSTRING(property_name); \
property_value_pair->type = &property_type##_log_context_property_type; \
/* Codes_SRS_LOG_CONTEXT_01_005: [ LOG_CONTEXT_PROPERTY shall expand to code copying the value property_value to be the value of the property/value pair. ]*/ \
(void)property_type##_log_context_property_type_init((void*)data_pos, field_value); \
data_pos += sizeof(property_type); \
property_value_pair++; \
#define EXPAND_SETUP_PROPERTY_PAIR_LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION(property_type, property_name, value_function, ...) \
/* Codes_SRS_LOG_CONTEXT_01_027: [ LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION shall expand to code allocating a property/value pair entry with the type property_type and the name property_name. ]*/ \
property_value_pair->value = data_pos; \
property_value_pair->name = MU_TOSTRING(property_name); \
property_value_pair->type = &property_type##_log_context_property_type; \
/* Codes_SRS_LOG_CONTEXT_01_029: [ LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION shall expand to code filling the property value by calling value_function. ]*/ \
data_pos += value_function((void*)data_pos, __VA_ARGS__); \
property_value_pair++; \
#define SETUP_PROPERTY_PAIR(field_desc) \
MU_C2(EXPAND_SETUP_PROPERTY_PAIR_, field_desc)
// COUNT_PROPERTY
#define EXPAND_COUNT_PROPERTY_LOG_MESSAGE(...) \
#define EXPAND_COUNT_PROPERTY_LOG_CONTEXT_STRING_PROPERTY(property_name, ...) \
+ 1
#define EXPAND_COUNT_PROPERTY_LOG_CONTEXT_WSTRING_PROPERTY(property_name, ...) \
+ 1
#define EXPAND_COUNT_PROPERTY_LOG_CONTEXT_NAME(log_context_name) \
#define EXPAND_COUNT_PROPERTY_LOG_CONTEXT_PROPERTY(property_type, property_name, field_value) \
+ 1
#define EXPAND_COUNT_PROPERTY_LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION(property_type, property_name, value_function, ...) \
+ 1
#define COUNT_PROPERTY(field_desc) \
MU_C2(EXPAND_COUNT_PROPERTY_, field_desc)
// COUNT_DATA_BYTES
#define EXPAND_COUNT_DATA_BYTES_LOG_MESSAGE(...) \
// ... here is expected to be (format, ...) for a printf invocation
#define EXPAND_COUNT_DATA_BYTES_LOG_CONTEXT_STRING_PROPERTY(property_name, ...) \
+ ascii_char_ptr_log_context_property_type_get_init_data_size(__VA_ARGS__)
#define EXPAND_COUNT_DATA_BYTES_LOG_CONTEXT_WSTRING_PROPERTY(property_name, ...) \
+ wchar_t_ptr_log_context_property_type_get_init_data_size(__VA_ARGS__)
#define EXPAND_COUNT_DATA_BYTES_LOG_CONTEXT_NAME(log_context_name) \
#define EXPAND_COUNT_DATA_BYTES_LOG_CONTEXT_PROPERTY(property_type, property_name, field_value) \
+ property_type##_log_context_property_type_get_init_data_size()
#define EXPAND_COUNT_DATA_BYTES_LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION(property_type, property_name, value_function, ...) \
/* Codes_SRS_LOG_CONTEXT_01_028: [ LOG_CONTEXT_PROPERTY_CUSTOM_FUNCTION shall expand to code that calls value_function with NULL in order to determine how much memory shall be reserved for the property. ] */ \
+ value_function(NULL, __VA_ARGS__)
#define COUNT_DATA_BYTES(field_desc) \
MU_C2(EXPAND_COUNT_DATA_BYTES_, field_desc)
// Macro that can be used to create a context on the stack
// We allocate on the stack enough space for a max payload for the context.
#define LOG_CONTEXT_LOCAL_DEFINE(destination_context, parent_context, ...) \
uint8_t MU_C2(values_log_data_, destination_context)[LOG_MAX_STACK_DATA_SIZE]; \
MU_SUPPRESS_WARNING(4815) /* warning C4815: 'local_context_3DFCB6F0_39A4_4C45_881B_A3BDA8B18CC1': zero-sized array in stack object will have no elements (unless the object is an aggregate that has been aggregate initialized) */ \
LOG_CONTEXT destination_context; \
MU_IF(MU_COUNT_ARG(__VA_ARGS__), LOG_CONTEXT_CHECK_VARIABLE_ARGS(__VA_ARGS__),) \
LOG_CONTEXT_PROPERTY_VALUE_PAIR MU_C2(property_values_pair_, destination_context)[LOG_MAX_STACK_PROPERTY_VALUE_PAIR_COUNT]; \
{ \
destination_context.values_data = MU_C2(values_log_data_, destination_context); \
destination_context.values_data_length = internal_log_context_get_values_data_length_or_zero(parent_context) + 1 /* 1 byte for the number of fields in the struct */ MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_DATA_BYTES, __VA_ARGS__),); \
destination_context.property_value_pairs_ptr = MU_C2(property_values_pair_, destination_context); \
destination_context.property_value_pair_count = log_context_get_property_value_pair_count(parent_context) + 1 /* 1 extra property entry for struct entry with the context name and property count */ MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_PROPERTY, __VA_ARGS__),); \
/* Codes_SRS_LOG_CONTEXT_01_024: [ If the number of properties to be stored in the log context exceeds LOG_MAX_STACK_PROPERTY_VALUE_PAIR_COUNT, an error shall be reported by calling log_internal_error_report and no properties shall be stored in the context. ]*/ \
if (destination_context.property_value_pair_count > LOG_MAX_STACK_PROPERTY_VALUE_PAIR_COUNT) \
{ \
(void)printf("Too many properties: property_value_pair_count = %" PRIu32 "\r\n", destination_context.property_value_pair_count); \
destination_context.property_value_pair_count = 0; \
destination_context.values_data_length = 0; \
log_internal_error_report(); \
} \
/* Codes_SRS_LOG_CONTEXT_01_025: [ If the memory size needed for all properties to be stored in the context exceeds LOG_MAX_STACK_DATA_SIZE, an error shall be reported by calling log_internal_error_report and no properties shall be stored in the context. ]*/ \
else if (destination_context.values_data_length > LOG_MAX_STACK_DATA_SIZE) \
{ \
(void)printf("Data length too big: values_data_length = %" PRIu32 "\r\n", destination_context.values_data_length); \
destination_context.property_value_pair_count = 0; \
destination_context.values_data_length = 0; \
log_internal_error_report(); \
} \
else \
{ \
/* Codes_SRS_LOG_CONTEXT_01_018: [ If parent_context is non-NULL, the created context shall copy all the property/value pairs of parent_context. ]*/ \
internal_log_context_init_from_parent(&destination_context, parent_context); \
LOG_CONTEXT_PROPERTY_VALUE_PAIR* property_value_pair = destination_context.property_value_pairs_ptr + log_context_get_property_value_pair_count(parent_context) + 1; \
LOG_CONTEXT_PROPERTY_VALUE_PAIR* first_property_value_pair = destination_context.property_value_pairs_ptr; \
(void)property_value_pair; \
uint8_t* data_pos = destination_context.values_data; \
*data_pos = (uint8_t)((parent_context != NULL ? 1 : 0) MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_PROPERTY, __VA_ARGS__),)); \
/* Codes_SRS_LOG_CONTEXT_01_015: [ LOG_CONTEXT_LOCAL_DEFINE shall store one property/value pair that with a property type of struct with as many fields as the total number of properties passed to LOG_CONTEXT_LOCAL_DEFINE in the ... arguments. ]*/ \
first_property_value_pair->value = data_pos; \
first_property_value_pair->name = ""; \
first_property_value_pair->type = &struct_log_context_property_type; \
data_pos += 1 + internal_log_context_get_values_data_length_or_zero(parent_context); \
/* Codes_SRS_LOG_CONTEXT_01_016: [ LOG_CONTEXT_LOCAL_DEFINE shall store the property types and values specified by using LOG_CONTEXT_PROPERTY in the context. ]*/ \
MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(SETUP_PROPERTY_PAIR, __VA_ARGS__),) \
} \
} \
LOG_CONTEXT_HANDLE log_context_create(LOG_CONTEXT_HANDLE parent_context, uint32_t properties_count, uint32_t data_size);
void log_context_destroy(LOG_CONTEXT_HANDLE log_context);
// macro that can be used to create a dynamically allocated context
#define LOG_CONTEXT_CREATE(destination_context, parent_context, ...) \
{ \
destination_context = log_context_create(parent_context, log_context_get_property_value_pair_count(parent_context) + 1 /* 1 extra property entry for struct entry with the context name and property count */ MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_PROPERTY, __VA_ARGS__),), internal_log_context_get_values_data_length_or_zero(parent_context) + 1 /* 1 byte for the number of fields in the struct */ MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_DATA_BYTES, __VA_ARGS__),)); \
MU_IF(MU_COUNT_ARG(__VA_ARGS__), LOG_CONTEXT_CHECK_VARIABLE_ARGS(__VA_ARGS__),) \
if (destination_context != NULL) \
{ \
/* Codes_SRS_LOG_CONTEXT_01_003: [ LOG_CONTEXT_CREATE shall store the property types and values specified by using LOG_CONTEXT_PROPERTY in the context. ]*/ \
LOG_CONTEXT_PROPERTY_VALUE_PAIR* property_value_pair = destination_context->property_value_pairs_ptr + log_context_get_property_value_pair_count(parent_context) + 1; \
LOG_CONTEXT_PROPERTY_VALUE_PAIR* first_property_value_pair = destination_context->property_value_pairs_ptr; \
(void)property_value_pair; \
uint8_t* data_pos = destination_context->values_data; \
*data_pos = (uint8_t)((parent_context != NULL ? 1 : 0) MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(COUNT_PROPERTY, __VA_ARGS__),)); \
/* Codes_SRS_LOG_CONTEXT_01_013: [ LOG_CONTEXT_CREATE shall store one property/value pair that with a property type of struct with as many fields as the total number of properties passed to LOG_CONTEXT_CREATE. ]*/ \
first_property_value_pair->value = data_pos; \
/* Codes_SRS_LOG_CONTEXT_01_009: [ LOG_CONTEXT_NAME shall be optional. ]*/ \
/* Codes_SRS_LOG_CONTEXT_01_010: [** If LOG_CONTEXT_NAME is not used the name for the context shall be empty string. ]*/ \
first_property_value_pair->name = ""; \
first_property_value_pair->type = &struct_log_context_property_type; \
data_pos += 1 + internal_log_context_get_values_data_length_or_zero(parent_context); \
MU_IF(MU_COUNT_ARG(__VA_ARGS__), MU_FOR_EACH_1(SETUP_PROPERTY_PAIR, __VA_ARGS__),) \
} \
} \
// destroy a dynamically allocated context
#define LOG_CONTEXT_DESTROY(log_context_handle) \
log_context_destroy(log_context_handle)
// when a user wants to specify the log context name to be printed or published, the user can use this macro
// otherwise, the default is to use empty string ("") as context name
// LOG_CONTEXT_NAME(log_context_name)
// when a user wants to specify properties for a context, they can use LOG_CONTEXT_PROPERTY
// LOG_CONTEXT_PROPERTY(property_type, property_name, property_value)
// when a user wants to specify string fields in a context using printf-style formatting, they can use LOG_CONTEXT_STRING_PROPERTY
// LOG_CONTEXT_STRING_PROPERTY(property_name, property_conversion_specifier, property_value)
#ifdef __cplusplus
}
#endif
#endif /* LOG_CONTEXT_H */