diff --git a/agent/Makefile.frag b/agent/Makefile.frag index a6cec7adb..8a4b6705c 100644 --- a/agent/Makefile.frag +++ b/agent/Makefile.frag @@ -89,6 +89,7 @@ TEST_BINARIES = \ tests/test_internal_instrument \ tests/test_hash \ tests/test_mongodb \ + tests/test_monolog \ tests/test_mysql \ tests/test_mysqli \ tests/test_output \ diff --git a/agent/lib_monolog.c b/agent/lib_monolog.c index 3ea482b9a..f85848d4f 100644 --- a/agent/lib_monolog.c +++ b/agent/lib_monolog.c @@ -12,9 +12,12 @@ #include "php_wrapper.h" #include "fw_hooks.h" #include "fw_support.h" +#include "lib_monolog_private.h" #include "nr_datastore_instance.h" #include "nr_segment_datastore.h" +#include "nr_txn.h" #include "util_logging.h" +#include "util_object.h" #include "util_memory.h" #include "util_strings.h" #include "util_sleep.h" @@ -27,19 +30,6 @@ #define LOG_DECORATE_PROC_FUNC_NAME \ "newrelic_phpagent_monolog_decorating_processor" -// clang-format off -/* - * This macro affects how instrumentation $context argument of - * Monolog\Logger::addRecord works: - * - * 0 - $context argument will not be instrumented: its existance and value - * are ignored - * 1 - the message of the log record forwarded by the agent will have the value - * of $context appended to the value of $message. - */ -// clang-format on -#define HAVE_CONTEXT_IN_MESSAGE 0 - /* * Purpose : Convert Monolog\Logger::API to integer * @@ -155,123 +145,78 @@ static char* nr_monolog_get_message(NR_EXECUTE_PROTO TSRMLS_DC) { return message; } -#if HAVE_CONTEXT_IN_MESSAGE /* - * Purpose : Format key of $context array's element as string + * Purpose : Convert a zval value from context data to a nrobj_t * - * Params : zend_hash_key - * * - * Returns : A new string representing zval; caller must free - * - */ -static char* nr_monolog_fmt_context_key(const zend_hash_key* hash_key) { - char* key_str = NULL; - zval* key = nr_php_zval_alloc(); - if (nr_php_zend_hash_key_is_string(hash_key)) { - nr_php_zval_str(key, nr_php_zend_hash_key_string_value(hash_key)); - key_str = nr_formatf("%s", Z_STRVAL_P(key)); - } else if (nr_php_zend_hash_key_is_numeric(hash_key)) { - ZVAL_LONG(key, (zend_long)nr_php_zend_hash_key_integer(hash_key)); - key_str = nr_formatf("%ld", (long)Z_LVAL_P(key)); - } else { - /* - * This is a warning because this really, really shouldn't ever happen. - */ - nrl_warning(NRL_INSTRUMENT, "%s: unexpected key type", __func__); - key_str = nr_formatf("unsupported-key-type"); - } - nr_php_zval_free(&key); - return key_str; -} - -/* - * Purpose : Format value of $context array's element as string + * Params : zval * - * Params : zval value - * * - * Returns : A new string representing zval; caller must free + * Returns : nrobj_t* holding converted value + * NULL otherwise * + * Notes : Only scalar and string types are supported. + * Nested arrays are not converted and are ignored. + * Other zval types are also ignored. */ -static char* nr_monolog_fmt_context_value(zval* zv) { - char* val_str = NULL; - zval* zv_str = NULL; +nrobj_t* nr_monolog_context_data_zval_to_attribute_obj( + const zval* z TSRMLS_DC) { + nrobj_t* retobj = NULL; - if (NULL == zv) { - return nr_strdup(""); + if (NULL == z) { + return NULL; } - zv_str = nr_php_zval_alloc(); - if (NULL == zv_str) { - return nr_strdup(""); - } + nr_php_zval_unwrap(z); - ZVAL_DUP(zv_str, zv); - convert_to_string(zv_str); - val_str = nr_strdup(Z_STRVAL_P(zv_str)); - nr_php_zval_free(&zv_str); + switch (Z_TYPE_P(z)) { + case IS_NULL: + retobj = NULL; + break; - return val_str; -} + case IS_LONG: + retobj = nro_new_long((long)Z_LVAL_P(z)); + break; -/* - * Purpose : Format an element of $context array as "key => value" string - * - * Params : zval value, pointer to string buffer to store formatted output - * and hash key - * - * Side effect : string buffer is reallocated with each call. - * - * Returns : ZEND_HASH_APPLY_KEEP to keep iteration - * - */ -static int nr_monolog_fmt_context_item(zval* value, - char** strbuf, - zend_hash_key* hash_key TSRMLS_DC) { - NR_UNUSED_TSRMLS; - char* key = nr_monolog_fmt_context_key(hash_key); - char* val = nr_monolog_fmt_context_value(value); - - char* kv_str = nr_formatf("%s => %s", key, val); - nr_free(key); - nr_free(val); - - char* sep = nr_strlen(*strbuf) > 1 ? ", " : ""; - *strbuf = nr_str_append(*strbuf, kv_str, sep); - nr_free(kv_str); - - return ZEND_HASH_APPLY_KEEP; -} + case IS_DOUBLE: + retobj = nro_new_double(Z_DVAL_P(z)); + break; -/* - * Purpose : Iterate over $context array and format each element - * - * Params : string buffer to store formatted output and - * Monolog\Logger::addRecord argument list - * - * Returns : A new string with Monolog's log context - */ -static char* nr_monolog_fmt_context(char* strbuf, - HashTable* context TSRMLS_DC) { - strbuf = nr_str_append(strbuf, "[", ""); + case IS_TRUE: + retobj = nro_new_boolean(true); + break; + + case IS_FALSE: + retobj = nro_new_boolean(false); + break; - nr_php_zend_hash_zval_apply(context, - (nr_php_zval_apply_t)nr_monolog_fmt_context_item, - (void*)&strbuf TSRMLS_CC); + case IS_STRING: + if (!nr_php_is_zval_valid_string(z)) { + retobj = NULL; + } else { + retobj = nro_new_string(Z_STRVAL_P(z)); + } + break; + + default: + /* any other type conversion to attribute not supported */ + retobj = NULL; + break; + } - return nr_str_append(strbuf, "]", ""); + return retobj; } /* - * Purpose : Convert $context argument of Monolog\Logger::addRecord to a string + * Purpose : Get $context argument of Monolog\Logger::addRecord as `zval *`. * * Params : # of Monolog\Logger::addRecord arguments, and * Monolog\Logger::addRecord argument list * - * Returns : A new string with Monolog's log context + * Returns : zval* for context array on success (must be freed by caller) + * NULL otherwise + * */ -static char* nr_monolog_get_context(const size_t argc, - NR_EXECUTE_PROTO TSRMLS_DC) { - char* context = nr_strdup(""); +static zval* nr_monolog_extract_context_data(const size_t argc, + NR_EXECUTE_PROTO TSRMLS_DC) { zval* context_arg = NULL; if (3 > argc) { @@ -298,45 +243,57 @@ static char* nr_monolog_get_context(const size_t argc, goto return_context; } - context = nr_monolog_fmt_context(context, Z_ARRVAL_P(context_arg) TSRMLS_CC); - return_context: - nr_php_arg_release(&context_arg); - return context; + return context_arg; } -#endif /* - * Purpose : Combine $message and $context arguments of - * Monolog\Logger::addRecord into a single string to be used as a message - * property of the log event. + * Purpose : Convert $context array of Monolog\Logger::addRecord to + * attributes * - * Params : # of Monolog\Logger::addRecord arguments, and - * Monolog\Logger::addRecord argument list + * Params : zval* for context array from Monolog + * + * Returns : nr_attributes representation of $context on success + * NULL otherwise * - * Returns : A new string with a log record message; caller must free */ -static char* nr_monolog_build_message(const size_t argc, - NR_EXECUTE_PROTO TSRMLS_DC) { -#if !HAVE_CONTEXT_IN_MESSAGE - /* Make the compiler happy - argc is not used when $context is ignored */ - (void)argc; -#endif - char* message_and_context = nr_strdup(""); - - char* message = nr_monolog_get_message(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - message_and_context = nr_str_append(message_and_context, message, ""); - nr_free(message); +nr_attributes_t* nr_monolog_convert_context_data_to_attributes( + zval* context_data TSRMLS_DC) { + zend_string* key; + zval* val; -#if HAVE_CONTEXT_IN_MESSAGE - char* context = nr_monolog_get_context(argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - if (!nr_strempty(context)) { - message_and_context = nr_str_append(message_and_context, context, " "); + nr_attributes_t* attributes = NULL; + + if (NULL == context_data || !nr_php_is_zval_valid_array(context_data)) { + return NULL; + } + + attributes = nr_attributes_create(NRPRG(txn)->attribute_config); + if (NULL == attributes) { + return NULL; + } + + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARR_P(context_data), key, val) { + if (NULL == key) { + continue; + } + + nrobj_t* obj = nr_monolog_context_data_zval_to_attribute_obj(val); + + if (NULL != obj) { + nr_attributes_user_add(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + ZSTR_VAL(key), obj); + nro_delete(obj); + } else { + nrl_verbosedebug(NRL_INSTRUMENT, + "%s: log context attribute '%s' dropped due to value " + "being of unsupported type %d", + __func__, ZSTR_VAL(key), Z_TYPE_P(val)); + } } - nr_free(context); -#endif + ZEND_HASH_FOREACH_END(); - return message_and_context; + return attributes; } /* @@ -397,13 +354,22 @@ NR_PHP_WRAPPER(nr_monolog_logger_addrecord) { int api = 0; size_t argc = 0; char* message = NULL; + nr_attributes_t* context_attributes = NULL; nrtime_t timestamp = nr_get_time(); /* Values of $message and $timestamp arguments are needed only if log * forwarding is enabled so agent will get them conditionally */ if (nr_txn_log_forwarding_enabled(NRPRG(txn))) { argc = nr_php_get_user_func_arg_count(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); - message = nr_monolog_build_message(argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); + message = nr_monolog_get_message(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); + + if (nr_txn_log_forwarding_context_data_enabled(NRPRG(txn))) { + zval* context_data = nr_monolog_extract_context_data( + argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); + context_attributes + = nr_monolog_convert_context_data_to_attributes(context_data); + nr_php_arg_release(&context_data); + } api = nr_monolog_version(this_var TSRMLS_CC); timestamp = nr_monolog_get_timestamp(api, argc, NR_EXECUTE_ORIG_ARGS TSRMLS_CC); @@ -411,7 +377,7 @@ NR_PHP_WRAPPER(nr_monolog_logger_addrecord) { /* Record the log event */ nr_txn_record_log_event(NRPRG(txn), level_name, message, timestamp, - NRPRG(app)); + context_attributes, NRPRG(app)); nr_free(level_name); nr_free(message); diff --git a/agent/lib_monolog_private.h b/agent/lib_monolog_private.h new file mode 100644 index 000000000..de6f9c97b --- /dev/null +++ b/agent/lib_monolog_private.h @@ -0,0 +1,29 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LIB_MONOLOG_PRIVATE_HDR +#define LIB_MONOLOG_PRIVATE_HDR + +/* + * Purpose : ONLY for testing to verify that the appropriate behavior of + * the conversion of zvals to attribute via nro. + * + * Returns : Pointer to nr_object_t representation of zval or + * NULL if zval is not a supported type for conversion + * to an attribute + */ +extern nrobj_t* nr_monolog_context_data_zval_to_attribute_obj( + const zval* z TSRMLS_DC); + +/* + * Purpose : ONLY for testing to verify that the appropriate behavior of + * the conversion of a Monolog context array to attributes. + * + * Returns : Caller takes ownership of attributes struct + * + */ +extern nr_attributes_t* nr_monolog_convert_context_data_to_attributes( + zval* context_data TSRMLS_DC); +#endif /* LIB_MONOLOG_PRIVATE_HDR */ diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index 3f1f4b9b5..d7edaba65 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -308,6 +308,9 @@ nr_php_ini_attribute_config_t nr_php_ini_attribute_config_t browser_monitoring_attributes; /* newrelic.browser_monitoring.attributes.* */ +nr_php_ini_attribute_config_t + log_context_data_attributes; /* newrelic.application_logging.forwarding.context_data.* + */ nrinibool_t custom_events_enabled; /* newrelic.custom_insights_events.enabled */ nriniuint_t custom_events_max_samples_stored; /* newrelic.custom_events.max_samples_stored diff --git a/agent/php_nrini.c b/agent/php_nrini.c index ef1be6694..cea0f216f 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -2984,6 +2984,30 @@ STD_PHP_INI_ENTRY_EX("newrelic.application_logging.metrics.enabled", zend_newrelic_globals, newrelic_globals, nr_enabled_disabled_dh) +STD_PHP_INI_ENTRY_EX("newrelic.application_logging.forwarding.context_data.enabled", + "0", + NR_PHP_REQUEST, + nr_boolean_mh, + log_context_data_attributes.enabled, + zend_newrelic_globals, + newrelic_globals, + nr_enabled_disabled_dh) +STD_PHP_INI_ENTRY_EX("newrelic.application_logging.forwarding.context_data.include", + "", + NR_PHP_REQUEST, + nr_string_mh, + log_context_data_attributes.include, + zend_newrelic_globals, + newrelic_globals, + 0) +STD_PHP_INI_ENTRY_EX("newrelic.application_logging.forwarding.context_data.exclude", + "", + NR_PHP_REQUEST, + nr_string_mh, + log_context_data_attributes.exclude, + zend_newrelic_globals, + newrelic_globals, + 0) PHP_INI_END() /* } */ diff --git a/agent/php_txn.c b/agent/php_txn.c index 61a32a872..0ebb8cd00 100644 --- a/agent/php_txn.c +++ b/agent/php_txn.c @@ -447,6 +447,11 @@ static nr_attribute_config_t* nr_php_create_attribute_config(TSRMLS_D) { NRINI(browser_monitoring_capture_attributes), NR_ATTRIBUTE_DESTINATION_BROWSER); + disabled_destinations |= nr_php_attribute_disable_destination_helper( + "newrelic.application_logging.forwarding.context_data.enabled", + NRINI(log_context_data_attributes.enabled), 0, + NR_ATTRIBUTE_DESTINATION_LOG); + if (0 == NRINI(attributes.enabled)) { disabled_destinations |= NR_ATTRIBUTE_DESTINATION_ALL; } @@ -491,6 +496,13 @@ static nr_attribute_config_t* nr_php_create_attribute_config(TSRMLS_D) { config, 0, NRINI(browser_monitoring_attributes.exclude), 0, NR_ATTRIBUTE_DESTINATION_BROWSER); + nr_php_modify_attribute_destinations( + config, 0, NRINI(log_context_data_attributes.include), + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_php_modify_attribute_destinations( + config, 0, NRINI(log_context_data_attributes.exclude), 0, + NR_ATTRIBUTE_DESTINATION_LOG); + nr_php_modify_attribute_destinations(config, 0, NRINI(attributes.include), NR_ATTRIBUTE_DESTINATION_ALL, 0); nr_php_modify_attribute_destinations(config, 0, NRINI(attributes.exclude), 0, @@ -747,6 +759,8 @@ nr_status_t nr_php_txn_begin(const char* appnames, opts.logging_enabled = NRINI(logging_enabled); opts.log_decorating_enabled = NRINI(log_decorating_enabled); opts.log_forwarding_enabled = NRINI(log_forwarding_enabled); + opts.log_forwarding_context_data_enabled + = NRINI(log_context_data_attributes.enabled); opts.log_forwarding_log_level = NRINI(log_forwarding_log_level); opts.log_events_max_samples_stored = NRINI(log_events_max_samples_stored); opts.log_metrics_enabled = NRINI(log_metrics_enabled); diff --git a/agent/scripts/newrelic.ini.template b/agent/scripts/newrelic.ini.template index 29b125083..e55296cd4 100644 --- a/agent/scripts/newrelic.ini.template +++ b/agent/scripts/newrelic.ini.template @@ -1239,6 +1239,47 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" ; ;newrelic.application_logging.metrics.enabled = true +; Setting: newrelic.application_logging.forwarding.context_data.enabled +; Type : boolean +; Scope : per-directory +; Default: false +; Info : Control if context data associated with log messages is +; converted to log event attributes which are forwarded to New Relic. +; +;newrelic.application_logging.forwarding.context_data.enabled = false + +; Setting: newrelic.application_logging.forwarding.context_data.include +; newrelic.application_logging.forwarding.context_data.exclude +; Type : string +; Scope : per-directory +; Default: none +; Info : This configuration options allow complete control over the +; context data array keys which are converted to log event attributes. +; +; To include context data whose key is 'alpha', the configuration is: +; newrelic.application_logging.forwarding.context_data.include = alpha +; +; To exclude context data whose key is 'alpha', the configuration is: +; newrelic.application_logging.forwarding.context_data.exclude = alpha +; +; The newrelic.attributes.exclude and newrelic.attributes.include +; settings affect the conversion of custom data as well. +; +; To exclude the attributes 'beta' and 'gamma' from all destinations, +; including log events, the configuration is: +; newrelic.attributes.exclude = beta,gamma +; +; If one of the values in the comma separated list ends in a '*', +; it will match any suffix. For example, to exclude any attribute +; which begin with 'psi', the configuration is: +; newrelic.attributes.exclude = psi* +; +; For more information, please refer to: +; https://docs.newrelic.com/docs/agents/manage-apm-agents/agent-metrics/agent-attributes +; +;newrelic.application_logging.forwarding.context_data.include = "" +;newrelic.application_logging.forwarding.context_data.exclude = "" + ; Setting: newrelic.code_level_metrics.enabled ; Type : boolean ; Scope : per-directory diff --git a/agent/tests/test_monolog.c b/agent/tests/test_monolog.c new file mode 100644 index 000000000..c1d415628 --- /dev/null +++ b/agent/tests/test_monolog.c @@ -0,0 +1,319 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ +#include "tlib_php.h" +#include "tlib_datastore.h" + +#include "php_agent.h" +#include "nr_attributes.h" +#include "lib_monolog_private.h" + +tlib_parallel_info_t parallel_info + = {.suggested_nthreads = -1, .state_size = 0}; + +static void test_convert_zval_to_attribute_obj(TSRMLS_D) { + zval* obj; + nrobj_t* nrobj; + nr_status_t err; + + tlib_php_request_start(); + + /* test null zval */ + obj = nr_php_zval_alloc(); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_null("NULL zval", nrobj); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* test boolean */ + obj = tlib_php_request_eval_expr("True;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Boolean converted", nrobj); + tlib_pass_if_equal("Boolean type correct", NR_OBJECT_BOOLEAN, nro_type(nrobj), + int, "%d"); + tlib_pass_if_true("Boolean value correct", nro_get_boolean(nrobj, &err), + "expected true"); + tlib_pass_if_equal("Boolean GET successful", NR_SUCCESS, err, int, "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* long */ + obj = tlib_php_request_eval_expr("1234567;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Long converted", nrobj); + tlib_pass_if_equal("Long type correct", NR_OBJECT_LONG, nro_type(nrobj), int, + "%d"); + tlib_pass_if_equal("Long value correct", 1234567, nro_get_long(nrobj, &err), + int, "%d"); + tlib_pass_if_equal("Long GET successful", NR_SUCCESS, err, int, "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* double */ + obj = tlib_php_request_eval_expr("1.234567;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Double converted", nrobj); + tlib_pass_if_equal("Double type correct", NR_OBJECT_DOUBLE, nro_type(nrobj), + int, "%d"); + tlib_pass_if_equal("Double value correct", 1.234567, + nro_get_double(nrobj, &err), int, "%d"); + tlib_pass_if_equal("Double GET successful", NR_SUCCESS, err, int, "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* string */ + obj = tlib_php_request_eval_expr("\"A\";" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("String converted", nrobj); + tlib_pass_if_equal("String type correct", NR_OBJECT_STRING, nro_type(nrobj), + int, "%d"); + tlib_pass_if_str_equal("String value correct", "A", + nro_get_string(nrobj, &err)); + tlib_pass_if_equal("String GET successful", NR_SUCCESS, err, int, "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* constant boolean */ + tlib_php_request_eval("define(\"CONSTANT_DEFINE_BOOLEAN\", True);" TSRMLS_CC); + obj = tlib_php_request_eval_expr("CONSTANT_DEFINE_BOOLEAN;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Constant Boolean converted", nrobj); + tlib_pass_if_equal("Constant Boolean type correct", NR_OBJECT_BOOLEAN, + nro_type(nrobj), int, "%d"); + tlib_pass_if_true("Constant Boolean value correct", + nro_get_boolean(nrobj, &err), "expected true"); + tlib_pass_if_equal("Constant Boolean GET successful", NR_SUCCESS, err, int, + "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* constant long */ + tlib_php_request_eval("define(\"CONSTANT_DEFINE_LONG\",1234567);" TSRMLS_CC); + obj = tlib_php_request_eval_expr("CONSTANT_DEFINE_LONG;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Constant Long converted", nrobj); + tlib_pass_if_equal("Constant Long type correct", NR_OBJECT_LONG, + nro_type(nrobj), int, "%d"); + tlib_pass_if_equal("Constant Long value correct", 1234567, + nro_get_long(nrobj, &err), int, "%d"); + tlib_pass_if_equal("Constant Long GET successful", NR_SUCCESS, err, int, + "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* double */ + tlib_php_request_eval( + "define(\"CONSTANT_DEFINE_DOUBLE\",1.234567);" TSRMLS_CC); + obj = tlib_php_request_eval_expr("CONSTANT_DEFINE_DOUBLE;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Constant Double converted", nrobj); + tlib_pass_if_equal("Constant Double type correct", NR_OBJECT_DOUBLE, + nro_type(nrobj), int, "%d"); + tlib_pass_if_equal("Constant Double value correct", 1.234567, + nro_get_double(nrobj, &err), int, "%d"); + tlib_pass_if_equal("Constant Double GET successful", NR_SUCCESS, err, int, + "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* test constant string */ + tlib_php_request_eval("define(\"CONSTANT_DEFINE_STRING\", \"A\");" TSRMLS_CC); + obj = tlib_php_request_eval_expr("CONSTANT_DEFINE_STRING;" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_not_null("Constant String converted", nrobj); + tlib_pass_if_equal("Constant tring type correct", NR_OBJECT_STRING, + nro_type(nrobj), int, "%d"); + tlib_pass_if_str_equal("Constant String value correct", "A", + nro_get_string(nrobj, &err)); + tlib_pass_if_equal("Constant String GET successful", NR_SUCCESS, err, int, + "%d"); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* test array */ + obj = tlib_php_request_eval_expr("array(1, 2, 3);" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_null("Array not converted", nrobj); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + /* test object */ + obj = tlib_php_request_eval_expr("new stdClass();" TSRMLS_CC); + nrobj = nr_monolog_context_data_zval_to_attribute_obj(obj); + tlib_pass_if_null("Object not converted", nrobj); + nr_php_zval_free(&obj); + nro_delete(nrobj); + + tlib_php_request_end(); +} + +#define TEST_ATTRIBUTES_CREATION(CONTEXT_DATA, EXPECTED_JSON) \ + do { \ + char* actual_json; \ + nr_attributes_t* attributes \ + = nr_monolog_convert_context_data_to_attributes(context_data); \ + \ + tlib_fail_if_null("attributes is not NULL", attributes); \ + \ + nrobj_t* log_attributes = nr_attributes_logcontext_to_obj( \ + attributes, NR_ATTRIBUTE_DESTINATION_LOG); \ + \ + tlib_fail_if_null("log_attributes is not NULL", log_attributes); \ + tlib_fail_if_bool_equal("At least one attribute created", 1, \ + 0 > nro_getsize(log_attributes)); \ + \ + if (0 < nro_getsize(log_attributes)) { \ + actual_json = nro_to_json(log_attributes); \ + } \ + \ + tlib_pass_if_str_equal("Converted array", expected_json, actual_json); \ + nr_free(actual_json); \ + nro_delete(log_attributes); \ + nr_attributes_destroy(&attributes); \ + } while (0) + +static void test_convert_context_data_to_attributes(TSRMLS_D) { + zval* context_data; + + tlib_php_request_start(); + nrtxn_t* txn = NRPRG(txn); + + /* enable context data filtering */ + nr_attribute_config_t* orig_config + = nr_attribute_config_copy(NRPRG(txn)->attribute_config); + txn->options.log_forwarding_context_data_enabled = 1; + nr_attribute_config_enable_destinations(txn->attribute_config, + NR_ATTRIBUTE_DESTINATION_LOG); + + context_data = tlib_php_request_eval_expr( + "array(" + "1=>\"one\"," + "\"null_attr\"=>null," + "\"string_attr\"=>\"string_value\"," + "\"double_attr\"=>3.1," + "\"int_attr\"=>1234," + "\"true_bool_attr\"=>True," + "\"false_bool_attr\"=>False," + "\"array_attr\"=>array(\"nested_string\"=>\"nested_string_value\")," + "\"object_attr\"=>new StdClass())" TSRMLS_CC); + + /* test without any filters and all attributes allowed */ + char* expected_json + = "{" + "\"context.false_bool_attr\":false," + "\"context.true_bool_attr\":true," + "\"context.int_attr\":1234," + "\"context.double_attr\":3.10000," + "\"context.string_attr\":\"string_value\"" + "}"; + + TEST_ATTRIBUTES_CREATION(context_data, expected_json); + + /* add filtering rules and try again */ + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, + "string_attr", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "i*", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "f*", 0, + NR_ATTRIBUTE_DESTINATION_LOG); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "t*", 0, + NR_ATTRIBUTE_DESTINATION_LOG); + expected_json + = "{" + "\"context.int_attr\":1234," + "\"context.string_attr\":\"string_value\"" + "}"; + + TEST_ATTRIBUTES_CREATION(context_data, expected_json); + + /* another case to add filtering rules and try again */ + nr_attribute_config_destroy(&(NRPRG(txn)->attribute_config)); + NRPRG(txn)->attribute_config = nr_attribute_config_copy(orig_config); + nr_attribute_config_enable_destinations(txn->attribute_config, + NR_ATTRIBUTE_DESTINATION_LOG); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "d*", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "i*", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "*", 0, + NR_ATTRIBUTE_DESTINATION_LOG); + expected_json + = "{" + "\"context.int_attr\":1234," + "\"context.double_attr\":3.10000" + "}"; + + TEST_ATTRIBUTES_CREATION(context_data, expected_json); + + /* test global and context_data include/exclude rules */ + nr_attribute_config_destroy(&(NRPRG(txn)->attribute_config)); + NRPRG(txn)->attribute_config = nr_attribute_config_copy(orig_config); + nr_attribute_config_enable_destinations(txn->attribute_config, + NR_ATTRIBUTE_DESTINATION_LOG); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "d*", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "i*", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, + "true_bool_attr", + NR_ATTRIBUTE_DESTINATION_LOG, 0); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, "t*", 0, + NR_ATTRIBUTE_DESTINATION_ALL); + nr_attribute_config_modify_destinations(NRPRG(txn)->attribute_config, + "false_bool_attr", 0, + NR_ATTRIBUTE_DESTINATION_ALL); + expected_json + = "{" + "\"context.true_bool_attr\":true," + "\"context.int_attr\":1234," + "\"context.double_attr\":3.10000" + "}"; + + TEST_ATTRIBUTES_CREATION(context_data, expected_json); + + nr_attribute_config_destroy(&orig_config); + nr_php_zval_free(&context_data); + + tlib_php_request_end(); +} + +static void test_convert_context_data_to_attributes_bad_params(TSRMLS_D) { + tlib_php_request_start(); + + /* enable context data destination */ + nrtxn_t* txn = NRPRG(txn); + txn->options.log_forwarding_context_data_enabled = 1; + nr_attribute_config_enable_destinations(txn->attribute_config, + NR_ATTRIBUTE_DESTINATION_LOG); + + nr_attributes_t* attributes + = nr_monolog_convert_context_data_to_attributes(NULL); + + tlib_pass_if_null("NULL context yields attributes is NULL", attributes); + + // create an undefined zval - nr_php_zval_alloc() returns undefined + zval* z = nr_php_zval_alloc(); + + tlib_pass_if_equal("zval is undefined type", IS_UNDEF, Z_TYPE_P(z), int, "%d"); + + attributes = nr_monolog_convert_context_data_to_attributes(z); + + tlib_pass_if_null("zval of undefined type yields attributes is NULL", + attributes); + nr_php_zval_free(&z); + + tlib_php_request_end(); +} + +void test_main(void* p NRUNUSED) { + + tlib_php_engine_create(""); + + test_convert_zval_to_attribute_obj(); + test_convert_context_data_to_attributes(); + test_convert_context_data_to_attributes_bad_params(); + + tlib_php_engine_destroy(TSRMLS_C); +} diff --git a/axiom/nr_attributes.c b/axiom/nr_attributes.c index 68d0d8c73..54cee95ca 100644 --- a/axiom/nr_attributes.c +++ b/axiom/nr_attributes.c @@ -17,6 +17,13 @@ #include "util_memory.h" #include "util_strings.h" +static void nr_attribute_config_modify_destinations_internal( + nr_attribute_config_t* config, + const char* match, + uint32_t include_destinations, + uint32_t exclude_destinations, + bool finalize_destinations); + /* * Returns : 1 if there is a match and 0 otherwise. */ @@ -97,10 +104,20 @@ void nr_attribute_config_disable_destinations(nr_attribute_config_t* config, config->disabled_destinations |= disabled_destinations; } -nr_attribute_destination_modifier_t* nr_attribute_destination_modifier_create( - const char* match, - uint32_t include_destinations, - uint32_t exclude_destinations) { +void nr_attribute_config_enable_destinations(nr_attribute_config_t* config, + uint32_t enabled_destinations) { + if (0 == config) { + return; + } + + config->disabled_destinations &= ~enabled_destinations; +} + +static nr_attribute_destination_modifier_t* +nr_attribute_destination_modifier_create_internal(const char* match, + uint32_t include_destinations, + uint32_t exclude_destinations, + int is_finalize_rule) { nr_attribute_destination_modifier_t* new_entry; int match_len; int has_wildcard_suffix; @@ -121,6 +138,8 @@ nr_attribute_destination_modifier_t* nr_attribute_destination_modifier_create( new_entry = (nr_attribute_destination_modifier_t*)nr_zalloc( sizeof(nr_attribute_destination_modifier_t)); new_entry->has_wildcard_suffix = has_wildcard_suffix; + new_entry->is_finalize_rule = is_finalize_rule; + /* Use nr_strndup with match_len to avoid copying '*' suffix */ new_entry->match = nr_strndup(match, match_len); new_entry->match_len = match_len; @@ -132,6 +151,14 @@ nr_attribute_destination_modifier_t* nr_attribute_destination_modifier_create( return new_entry; } +nr_attribute_destination_modifier_t* nr_attribute_destination_modifier_create( + const char* match, + uint32_t include_destinations, + uint32_t exclude_destinations) { + return nr_attribute_destination_modifier_create_internal( + match, include_destinations, exclude_destinations, false); +} + /* * Purpose : Determine the precedence order of two destination modifiers. * @@ -162,10 +189,132 @@ static int nr_attribute_destination_modifier_compare( return 1; } -void nr_attribute_config_modify_destinations(nr_attribute_config_t* config, - const char* match, - uint32_t include_destinations, - uint32_t exclude_destinations) { +/* + * Purpose : Inspects current modifier list and adds finalize rules as needed. + * + * Params : 1. Attribute configuration + * + * Notes : Certain attributes like log context attributes expect the "include" + * rules to act exclusively. + * For example: + * include = "A" + * exclude = "B" + * input = "A" "B" "C" + * expected = "A" + * + * Note that "C" was excluded because it was not in the include rules. + * Also note an empty include rule means include everything and + * and to exclude nothing. + * + * All other attributes besides log context attributes do NOT have + * this exclusive behavior for the include rules. This function only + * considers rules of the NR_ATTRIBUTE_DESTINATION_LOG destination. + * + * The algorithm examines all input rules for the + * NR_ATTRIBUTE_DESTINATION_LOG destination, and will create a new + * rule of "exclude=*" with "is_finalize_rule=true" if any input + * rules exist. The exception is if there is an input rule of "*" + * for destination NR_ATTRIBUTE_DESTINATION_LOG. In this case no + * finalize rule is added. + * + * The net resulting effect is the exclusion of any attributes not + * contained in the set of input rules for + * NR_ATTRIBUTE_DESTINATION_LOG. The exception for if an "input=*" + * rule is necessary since this input rule excludes nothing, making + * the finalize rule of "exclude=*" unnecessary. + * + */ +static void nr_attribute_config_finalize_log_destination( + nr_attribute_config_t* config) { + nr_attribute_destination_modifier_t* cur = NULL; + nr_attribute_destination_modifier_t* prev = NULL; + nr_attribute_destination_modifier_t* next = NULL; + bool add_finalize_rule = false; + + if (NULL == config || NULL == config->modifier_list) { + /* Since there is no configuration, no work to do. */ + return; + } + + /* remove any existing rules with is_finalize_rule = true */ + cur = config->modifier_list; + while (NULL != cur) { + next = cur->next; + + /* currently only finalize rules being created are for the + * NR_ATTRIBUTE_DESTINATION_LOG destination but check to + * be thorough and in case other finalize rules are created + * in the future. + */ + if (cur->is_finalize_rule + && (cur->include_destinations & NR_ATTRIBUTE_DESTINATION_LOG)) { + nr_attribute_destination_modifier_destroy(&cur); + if (NULL != prev) { + prev->next = next; + } else { + config->modifier_list = next; + } + } else { + prev = cur; + } + cur = next; + } + + /* unlikely but if all rules were finalize rules then no more work to do */ + if (NULL == config->modifier_list) { + return; + } + + /* now look for any include rules with a destination of + * NR_ATTRIBUTE_DESTINATION_LOG and evaluate if any + * finalize rules need to be added. + */ + for (cur = config->modifier_list; cur; cur = cur->next) { + if (cur->include_destinations & NR_ATTRIBUTE_DESTINATION_LOG) { + if (cur->has_wildcard_suffix && 0 == cur->match_len) { + /* there is an include rule of "*" so no finalize is needed + * since all attributes are being explicitely included */ + return; + } + add_finalize_rule = true; + } + } + + /* a finalize rule is needed + * add an exclude rule of "*" which will remove any attributes which passed + * through the include rules and therefore are excluded implicitely + */ + if (add_finalize_rule) { + nr_attribute_config_modify_destinations_internal( + config, "*", 0, NR_ATTRIBUTE_DESTINATION_LOG, true); + } +} + +/* + * Purpose : Inserts a modifier rule into an attribute configuration. + * + * Params : 1. Attribute config to add modifier to + * 2. String containing matching text for modifier + * 3. Destinations to apply match to as include rule + * 4. Destinations to apply match to as exclude rule + * 5. If true this is a finalize rule used to cause + * include rules (on log destinations only currently) + * to be exclusive to anything not in the set of all + * include rules. + * For normal modifiers (from user rules) this can be false. + * + * Notes : The is_finalize_rule option is currently only used in + * nr_attribute_config_finalize_log_destination() as it + * is called while adding an attribute and without this + * option there would be an infinite loop of adding and + * finalizing. + */ +static void nr_attribute_config_modify_destinations_internal( + nr_attribute_config_t* config, + const char* match, + uint32_t include_destinations, + uint32_t exclude_destinations, + bool is_finalize_rule) { nr_attribute_destination_modifier_t* entry; nr_attribute_destination_modifier_t* new_entry; nr_attribute_destination_modifier_t** entry_ptr; @@ -174,8 +323,8 @@ void nr_attribute_config_modify_destinations(nr_attribute_config_t* config, return; } - new_entry = nr_attribute_destination_modifier_create( - match, include_destinations, exclude_destinations); + new_entry = nr_attribute_destination_modifier_create_internal( + match, include_destinations, exclude_destinations, is_finalize_rule); if (0 == new_entry) { return; } @@ -190,11 +339,14 @@ void nr_attribute_config_modify_destinations(nr_attribute_config_t* config, break; } - if (0 == cmp) { + /* if a finalize rule with the same name as a user rule is added (or vice + * verse), do not remove the existing one or else the user rule could be + * lost when finalizing */ + if (0 == cmp && new_entry->is_finalize_rule == entry->is_finalize_rule) { entry->include_destinations |= new_entry->include_destinations; entry->exclude_destinations |= new_entry->exclude_destinations; nr_attribute_destination_modifier_destroy(&new_entry); - return; + goto finalize_modifier; } entry_ptr = &entry->next; @@ -203,6 +355,24 @@ void nr_attribute_config_modify_destinations(nr_attribute_config_t* config, new_entry->next = entry; *entry_ptr = new_entry; + +finalize_modifier: + /* if an include modifier was added need to also add an exclude rule of "*" + * to have include rule act to exclude anything not included. Exception is + * if include rule was simply "*" which would allow everything so no + * exclude=* is required + */ + if (!is_finalize_rule) { + nr_attribute_config_finalize_log_destination(config); + } +} + +void nr_attribute_config_modify_destinations(nr_attribute_config_t* config, + const char* match, + uint32_t include_destinations, + uint32_t exclude_destinations) { + nr_attribute_config_modify_destinations_internal( + config, match, include_destinations, exclude_destinations, false); } static nr_attribute_destination_modifier_t* @@ -213,6 +383,7 @@ nr_attribute_destination_modifier_copy( new_entry = (nr_attribute_destination_modifier_t*)nr_zalloc(sizeof(*new_entry)); new_entry->has_wildcard_suffix = entry->has_wildcard_suffix; + new_entry->is_finalize_rule = entry->is_finalize_rule; new_entry->match = nr_strdup(entry->match); new_entry->match_len = entry->match_len; new_entry->match_hash = entry->match_hash; @@ -650,14 +821,23 @@ nr_status_t nr_attributes_agent_add_string(nr_attributes_t* ats, return rv; } +/* + * Purpose : Internal function to convert list of attributes to nro + * + * Params : 1. List of attributes + * 2. Prefix to prepend to all attribute names + * NULL indicates to use no prefix and is more efficient than "" + * 3. Attribute destinations + */ static nrobj_t* nr_attributes_to_obj_internal( const nr_attribute_t* attribute_list, + const char* attribute_prefix, uint32_t destination) { nrobj_t* obj; const nr_attribute_t* attribute; - if (0 == attribute_list) { - return 0; + if (NULL == attribute_list) { + return NULL; } obj = nro_new_hash(); @@ -666,7 +846,14 @@ static nrobj_t* nr_attributes_to_obj_internal( if (0 == (attribute->destinations & destination)) { continue; } - nro_set_hash(obj, attribute->key, attribute->value); + + if (nrlikely(NULL == attribute_prefix)) { + nro_set_hash(obj, attribute->key, attribute->value); + } else { + char* key = nr_formatf("%s%s", attribute_prefix, attribute->key); + nro_set_hash(obj, key, attribute->value); + nr_free(key); + } } return obj; @@ -677,7 +864,7 @@ nrobj_t* nr_attributes_user_to_obj(const nr_attributes_t* attributes, if (0 == attributes) { return 0; } - return nr_attributes_to_obj_internal(attributes->user_attribute_list, + return nr_attributes_to_obj_internal(attributes->user_attribute_list, NULL, destination); } @@ -686,7 +873,17 @@ nrobj_t* nr_attributes_agent_to_obj(const nr_attributes_t* attributes, if (0 == attributes) { return 0; } - return nr_attributes_to_obj_internal(attributes->agent_attribute_list, + return nr_attributes_to_obj_internal(attributes->agent_attribute_list, NULL, + destination); +} + +nrobj_t* nr_attributes_logcontext_to_obj(const nr_attributes_t* attributes, + uint32_t destination) { + if (NULL == attributes) { + return NULL; + } + return nr_attributes_to_obj_internal(attributes->user_attribute_list, + NR_LOG_CONTEXT_DATA_ATTRIBUTE_PREFIX, destination); } @@ -714,6 +911,9 @@ static char* nr_attribute_debug_json(const nr_attribute_t* attribute) { if (NR_ATTRIBUTE_DESTINATION_BROWSER & attribute->destinations) { nro_set_array_string(dests, 0, "browser"); } + if (NR_ATTRIBUTE_DESTINATION_LOG & attribute->destinations) { + nro_set_array_string(dests, 0, "log"); + } nro_set_hash(obj, "dests", dests); nro_delete(dests); diff --git a/axiom/nr_attributes.h b/axiom/nr_attributes.h index f324354b8..7c8774b00 100644 --- a/axiom/nr_attributes.h +++ b/axiom/nr_attributes.h @@ -38,10 +38,11 @@ typedef struct _nr_attributes_t nr_attributes_t; #define NR_ATTRIBUTE_DESTINATION_ERROR 4 #define NR_ATTRIBUTE_DESTINATION_BROWSER 8 #define NR_ATTRIBUTE_DESTINATION_SPAN 16 +#define NR_ATTRIBUTE_DESTINATION_LOG 32 #define NR_ATTRIBUTE_DESTINATION_ALL \ (NR_ATTRIBUTE_DESTINATION_TXN_EVENT | NR_ATTRIBUTE_DESTINATION_TXN_TRACE \ | NR_ATTRIBUTE_DESTINATION_ERROR | NR_ATTRIBUTE_DESTINATION_BROWSER \ - | NR_ATTRIBUTE_DESTINATION_SPAN) + | NR_ATTRIBUTE_DESTINATION_SPAN | NR_ATTRIBUTE_DESTINATION_LOG) /* * Attribute keys and value string lengths are limited. If a string exceeds @@ -56,6 +57,11 @@ typedef struct _nr_attributes_t nr_attributes_t; */ #define NR_ATTRIBUTE_USER_LIMIT 64 +/* + * APM log forwarding context attributes SHOULD have a prefix of "context." + */ +#define NR_LOG_CONTEXT_DATA_ATTRIBUTE_PREFIX "context." + /* * Configuration * @@ -99,6 +105,22 @@ extern void nr_attribute_config_disable_destinations( nr_attribute_config_t* config, uint32_t disabled_destinations); +/* + * Purpose : Enable attribute destinations. + * + * Params : 1. The configuration to modify. + * 2. The set of destinations to enable. + * + * Note : Destinations are enabled by default when a config + * is created. This function can be used to enable + * a previously disabled destination. Current + * use is to enable destinations disabled by default + * for testing purposes. + */ +extern void nr_attribute_config_enable_destinations( + nr_attribute_config_t* config, + uint32_t enabled_destinations); + /* * Purpose : Modify the destinations of a particular attribute, or all * attributes matching a certain prefix. @@ -185,6 +207,19 @@ extern nrobj_t* nr_attributes_user_to_obj(const nr_attributes_t* attributes, extern nrobj_t* nr_attributes_agent_to_obj(const nr_attributes_t* attributes, uint32_t destination); +/* + * Purpose : Specialized conversion function for attributes created on + * log events. These SHOULD have a prefix of "context." + * to avoid name collision with attributes from other + * destinations. + * + * Params : 1. Pointer to list of log context attributes + * 2. Attribute destinations for attribute + */ +extern nrobj_t* nr_attributes_logcontext_to_obj( + const nr_attributes_t* attributes, + uint32_t destination); + /* * Purpose : Check if an attribute with the given key exists. */ diff --git a/axiom/nr_attributes_private.h b/axiom/nr_attributes_private.h index 74edbfac4..3df7973a2 100644 --- a/axiom/nr_attributes_private.h +++ b/axiom/nr_attributes_private.h @@ -13,6 +13,8 @@ typedef struct _nr_attribute_destination_modifier_t { int has_wildcard_suffix; /* Whether 'match' is exact or a prefix. */ + int is_finalize_rule; /* if true this rule was added by finalize step + and not by a user rule */ char* match; /* The string to match against. This will not contain a trailing '*'. */ int match_len; /* The length of 'match'. */ diff --git a/axiom/nr_log_event.c b/axiom/nr_log_event.c index 0fbd90bef..dd3675868 100644 --- a/axiom/nr_log_event.c +++ b/axiom/nr_log_event.c @@ -33,6 +33,9 @@ void nr_log_event_destroy(nr_log_event_t** ptr) { nr_free(event->entity_guid); nr_free(event->entity_name); nr_free(event->hostname); + if (NULL != event->context_attributes) { + nr_attributes_destroy(&(event->context_attributes)); + } nr_realfree((void**)ptr); } @@ -46,6 +49,8 @@ void nr_log_event_destroy(nr_log_event_t** ptr) { * 3. Value of the field (JSON value) * 4. Boolean indicating if this is the first field * 5. Boolean indicating if this field is required + * 6. Boolean indicating if this field should be quoted + * (use false if including a JSON string, for example) * * Returns : True is data was added to buf. */ @@ -53,7 +58,8 @@ static bool add_log_field_to_buf(nrbuf_t* buf, const char* field_name, const char* field_value, const bool first, - const bool required) { + const bool required, + const bool quoted) { const char* final_value = field_value; if (NULL == buf || nr_strempty(field_name)) { @@ -75,7 +81,11 @@ static bool add_log_field_to_buf(nrbuf_t* buf, nr_buffer_add(buf, field_name, nr_strlen(field_name)); nr_buffer_add(buf, NR_PSTR("\"")); nr_buffer_add(buf, NR_PSTR(":")); - nr_buffer_add_escape_json(buf, final_value); + if (quoted) { + nr_buffer_add_escape_json(buf, final_value); + } else { + nr_buffer_add(buf, final_value, nr_strlen(final_value)); + } return true; } @@ -99,6 +109,9 @@ char* nr_log_event_to_json(const nr_log_event_t* event) { } bool nr_log_event_to_json_buffer(const nr_log_event_t* event, nrbuf_t* buf) { + char* json = NULL; + nrobj_t* log_attributes = NULL; + if (NULL == event || NULL == buf) { return false; } @@ -107,18 +120,34 @@ bool nr_log_event_to_json_buffer(const nr_log_event_t* event, nrbuf_t* buf) { nr_buffer_add(buf, NR_PSTR("{")); // only add non-empty fields - add_log_field_to_buf(buf, "message", event->message, true, true); - add_log_field_to_buf(buf, "level", event->log_level, false, true); - add_log_field_to_buf(buf, "trace.id", event->trace_id, false, false); - add_log_field_to_buf(buf, "span.id", event->span_id, false, false); - add_log_field_to_buf(buf, "entity.guid", event->entity_guid, false, false); - add_log_field_to_buf(buf, "entity.name", event->entity_name, false, false); - add_log_field_to_buf(buf, "hostname", event->hostname, false, false); + add_log_field_to_buf(buf, "message", event->message, true, true, true); + add_log_field_to_buf(buf, "level", event->log_level, false, true, true); + add_log_field_to_buf(buf, "trace.id", event->trace_id, false, false, true); + add_log_field_to_buf(buf, "span.id", event->span_id, false, false, true); + add_log_field_to_buf(buf, "entity.guid", event->entity_guid, false, false, + true); + add_log_field_to_buf(buf, "entity.name", event->entity_name, false, false, + true); + add_log_field_to_buf(buf, "hostname", event->hostname, false, false, true); // timestamp always present nr_buffer_add(buf, NR_PSTR(",\"timestamp\":")); nr_buffer_write_uint64_t_as_text(buf, event->timestamp); + // add attributes if present + if (NULL != event->context_attributes) { + log_attributes = nr_attributes_logcontext_to_obj( + event->context_attributes, NR_ATTRIBUTE_DESTINATION_LOG); + + if (0 < nro_getsize(log_attributes)) { + json = nro_to_json(log_attributes); + add_log_field_to_buf(buf, "attributes", json, false, false, false); + nr_free(json); + } + + nro_delete(log_attributes); + } + nr_buffer_add(buf, NR_PSTR("}")); return true; @@ -153,6 +182,14 @@ void nr_log_event_set_timestamp(nr_log_event_t* event, const nrtime_t time) { event->timestamp = time / NR_TIME_DIVISOR_MS; } +void nr_log_event_set_context_attributes(nr_log_event_t* event, + nr_attributes_t* context_attributes) { + if (NULL == event) { + return; + } + event->context_attributes = context_attributes; +} + void nr_log_event_set_trace_id(nr_log_event_t* event, const char* trace_id) { if (NULL == event || NULL == trace_id) { return; diff --git a/axiom/nr_log_event.h b/axiom/nr_log_event.h index f3010b4a3..32d7581dc 100644 --- a/axiom/nr_log_event.h +++ b/axiom/nr_log_event.h @@ -8,6 +8,7 @@ #include "util_buffer.h" #include "util_time.h" +#include "nr_attributes.h" #include #include @@ -99,5 +100,7 @@ extern void nr_log_event_set_entity_name(nr_log_event_t* event, extern void nr_log_event_set_hostname(nr_log_event_t* event, const char* name); extern void nr_log_event_set_timestamp(nr_log_event_t* event, nrtime_t time); extern void nr_log_event_set_priority(nr_log_event_t* event, int priority); - +extern void nr_log_event_set_context_attributes( + nr_log_event_t* event, + nr_attributes_t* context_attributes); #endif /* NR_LOG_EVENT_HDR */ diff --git a/axiom/nr_log_event_private.h b/axiom/nr_log_event_private.h index ded8af50a..8e059e0dd 100644 --- a/axiom/nr_log_event_private.h +++ b/axiom/nr_log_event_private.h @@ -6,12 +6,12 @@ #ifndef NR_LOG_EVENT_PRIVATE_H #define NR_LOG_EVENT_PRIVATE_H -#include "util_object.h" - +#include "nr_attributes.h" struct _nr_log_event_t { char* message; char* log_level; nrtime_t timestamp; + nr_attributes_t* context_attributes; char* trace_id; char* span_id; char* entity_guid; diff --git a/axiom/nr_txn.c b/axiom/nr_txn.c index aa0c91c92..27f326d16 100644 --- a/axiom/nr_txn.c +++ b/axiom/nr_txn.c @@ -3276,6 +3276,18 @@ bool nr_txn_log_forwarding_enabled(nrtxn_t* txn) { return true; } +bool nr_txn_log_forwarding_context_data_enabled(nrtxn_t* txn) { + if (!nr_txn_log_forwarding_enabled(txn)) { + return false; + } + + if (!txn->options.log_forwarding_context_data_enabled) { + return false; + } + + return true; +} + bool nr_txn_log_forwarding_log_level_verify(nrtxn_t* txn, const char* log_level_name) { int log_level; @@ -3371,6 +3383,7 @@ static void log_event_set_linking_metadata(nr_log_event_t* e, static nr_log_event_t* log_event_create(const char* log_level_name, const char* log_message, nrtime_t timestamp, + nr_attributes_t* context_attributes, nrtxn_t* txn, nrapp_t* app) { nr_log_event_t* e = nr_log_event_create(); @@ -3380,6 +3393,7 @@ static nr_log_event_t* log_event_create(const char* log_level_name, nr_log_event_set_log_level(e, ENSURE_LOG_LEVEL_NAME(log_level_name)); nr_log_event_set_message(e, log_message); nr_log_event_set_timestamp(e, timestamp); + nr_log_event_set_context_attributes(e, context_attributes); log_event_set_linking_metadata(e, txn, app); @@ -3390,6 +3404,7 @@ static void nr_txn_add_log_event(nrtxn_t* txn, const char* log_level_name, const char* log_message, nrtime_t timestamp, + nr_attributes_t* context_attributes, nrapp_t* app) { nr_log_event_t* e = NULL; bool event_dropped = false; @@ -3411,7 +3426,7 @@ static void nr_txn_add_log_event(nrtxn_t* txn, event_dropped = true; } else { /* event passed log level filter so add it */ - e = log_event_create(log_level_name, log_message, timestamp, txn, app); + e = log_event_create(log_level_name, log_message, timestamp, context_attributes, txn, app); if (NULL == e) { nrl_debug(NRL_TXN, "%s: failed to create log event", __func__); event_dropped = true; @@ -3448,12 +3463,14 @@ void nr_txn_record_log_event(nrtxn_t* txn, const char* log_level_name, const char* log_message, nrtime_t timestamp, + nr_attributes_t* context_attributes, nrapp_t* app) { if (nrunlikely(NULL == txn)) { return; } - nr_txn_add_log_event(txn, log_level_name, log_message, timestamp, app); + nr_txn_add_log_event(txn, log_level_name, log_message, timestamp, + context_attributes, app); nr_txn_add_logging_metrics(txn, log_level_name); } diff --git a/axiom/nr_txn.h b/axiom/nr_txn.h index a51424b6a..e3e063214 100644 --- a/axiom/nr_txn.h +++ b/axiom/nr_txn.h @@ -41,7 +41,6 @@ #include "util_string_pool.h" #define NR_TXN_REQUEST_PARAMETER_ATTRIBUTE_PREFIX "request.parameters." - typedef enum _nr_tt_recordsql_t { NR_SQL_NONE = 0, NR_SQL_RAW = 1, @@ -122,6 +121,7 @@ typedef struct _nrtxnopt_t { application logging features */ bool log_decorating_enabled; /* Whether log decorating is enabled */ bool log_forwarding_enabled; /* Whether log forwarding is enabled */ + bool log_forwarding_context_data_enabled; /* Whether context data is forwarded with logs */ int log_forwarding_log_level; /* minimum log level to forward to the collector */ size_t log_events_max_samples_stored; /* The maximum number of log events per @@ -628,6 +628,11 @@ extern void nr_txn_record_custom_event(nrtxn_t* txn, */ extern bool nr_txn_log_forwarding_enabled(nrtxn_t* txn); +/* + * Purpose : Check log forwarding context data configuration + */ +extern bool nr_txn_log_forwarding_context_data_enabled(nrtxn_t* txn); + /* * Purpose : Check log forwarding log level configuration */ @@ -651,13 +656,15 @@ extern bool nr_txn_log_decorating_enabled(nrtxn_t* txn); * 2. Log record level name * 3. Log record message * 4. Log record timestamp - * 5. The application (to get linking meta data) + * 5. Attribute data for Monolog context data (can be NULL) + * 6. The application (to get linking meta data) * */ extern void nr_txn_record_log_event(nrtxn_t* txn, const char* level_name, const char* message, nrtime_t timestamp, + nr_attributes_t* context_attributes, nrapp_t* app); /* diff --git a/axiom/tests/test_attributes.c b/axiom/tests/test_attributes.c index e6aa81ff2..e1d9d3b5e 100644 --- a/axiom/tests/test_attributes.c +++ b/axiom/tests/test_attributes.c @@ -32,6 +32,7 @@ static char* nr_attribute_destination_modifier_to_json( obj = nro_new_hash(); nro_set_hash_boolean(obj, "has_wildcard_suffix", modifier->has_wildcard_suffix); + nro_set_hash_boolean(obj, "is_finalize_rule", modifier->is_finalize_rule); nro_set_hash_string(obj, "match", modifier->match); nro_set_hash_int(obj, "match_len", modifier->match_len); nro_set_hash_long(obj, "match_hash", modifier->match_hash); @@ -241,6 +242,7 @@ static void test_destination_modifier_create(void) { test_modifier_as_json("exact match modifier created", modifier, "{" "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," "\"match\":\"alpha\"," "\"match_len\":5," "\"match_hash\":2000440672," @@ -253,6 +255,7 @@ static void test_destination_modifier_create(void) { test_modifier_as_json("wildcard modifier created", modifier, "{" "\"has_wildcard_suffix\":true," + "\"is_finalize_rule\":false," "\"match\":\"alpha\"," "\"match_len\":5," "\"match_hash\":2000440672," @@ -268,7 +271,7 @@ static void test_config_modify_destinations(void) { uint32_t trace = NR_ATTRIBUTE_DESTINATION_TXN_TRACE; uint32_t error = NR_ATTRIBUTE_DESTINATION_ERROR; uint32_t browser = NR_ATTRIBUTE_DESTINATION_BROWSER; - + uint32_t log = NR_ATTRIBUTE_DESTINATION_LOG; /* NULL config: Don't blow up! */ nr_attribute_config_modify_destinations(0, "alpha", event, error); @@ -283,6 +286,10 @@ static void test_config_modify_destinations(void) { nr_attribute_config_modify_destinations(config, "beta.*", 0, trace); nr_attribute_config_modify_destinations(config, "beta.alpha", 0, browser); + nr_attribute_config_modify_destinations(config, "beta.al*", 0, log); + + nr_attribute_config_modify_destinations(config, "beta.alp", log, 0); + nr_attribute_config_modify_destinations(config, "beta.alph", 0, browser); test_config_as_json("modifiers created and in correct order", config, "{" @@ -291,6 +298,16 @@ static void test_config_modify_destinations(void) { "[" "{" "\"has_wildcard_suffix\":true," + "\"is_finalize_rule\":true," + "\"match\":\"\"," + "\"match_len\":0," + "\"match_hash\":0," + "\"include_destinations\":0," + "\"exclude_destinations\":32" + "}," + "{" + "\"has_wildcard_suffix\":true," + "\"is_finalize_rule\":false," "\"match\":\"beta.\"," "\"match_len\":5," "\"match_hash\":1419915658," @@ -299,6 +316,7 @@ static void test_config_modify_destinations(void) { "}," "{" "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," "\"match\":\"beta.\"," "\"match_len\":5," "\"match_hash\":1419915658," @@ -307,6 +325,7 @@ static void test_config_modify_destinations(void) { "}," "{" "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," "\"match\":\"beta.a\"," "\"match_len\":6," "\"match_hash\":4222617845," @@ -314,7 +333,17 @@ static void test_config_modify_destinations(void) { "\"exclude_destinations\":0" "}," "{" + "\"has_wildcard_suffix\":true," + "\"is_finalize_rule\":false," + "\"match\":\"beta.al\"," + "\"match_len\":7," + "\"match_hash\":3041978671," + "\"include_destinations\":0," + "\"exclude_destinations\":32" + "}," + "{" "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," "\"match\":\"beta.al\"," "\"match_len\":7," "\"match_hash\":3041978671," @@ -323,6 +352,25 @@ static void test_config_modify_destinations(void) { "}," "{" "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," + "\"match\":\"beta.alp\"," + "\"match_len\":8," + "\"match_hash\":4236011699," + "\"include_destinations\":32," + "\"exclude_destinations\":0" + "}," + "{" + "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," + "\"match\":\"beta.alph\"," + "\"match_len\":9," + "\"match_hash\":2625680436," + "\"include_destinations\":0," + "\"exclude_destinations\":8" + "}," + "{" + "\"has_wildcard_suffix\":false," + "\"is_finalize_rule\":false," "\"match\":\"beta.alpha\"," "\"match_len\":10," "\"match_hash\":2601622409," @@ -344,6 +392,7 @@ static void test_config_copy(void) { uint32_t trace = NR_ATTRIBUTE_DESTINATION_TXN_TRACE; uint32_t error = NR_ATTRIBUTE_DESTINATION_ERROR; uint32_t browser = NR_ATTRIBUTE_DESTINATION_BROWSER; + uint32_t log = NR_ATTRIBUTE_DESTINATION_LOG; config_copy = nr_attribute_config_copy(0); tlib_pass_if_true("copy NULL config", 0 == config_copy, "config_copy=%p", @@ -374,6 +423,9 @@ static void test_config_copy(void) { nr_attribute_config_modify_destinations(config, "beta.", browser, 0); nr_attribute_config_modify_destinations(config, "beta.*", 0, trace); + nr_attribute_config_modify_destinations(config, "gamma.", log, 0); + nr_attribute_config_modify_destinations(config, "gamma.*", 0, trace); + config_copy = nr_attribute_config_copy(config); config_copy_json = nr_attribute_config_to_json(config_copy); config_json = nr_attribute_config_to_json(config); @@ -394,6 +446,7 @@ static void test_config_apply(void) { uint32_t trace = NR_ATTRIBUTE_DESTINATION_TXN_TRACE; uint32_t error = NR_ATTRIBUTE_DESTINATION_ERROR; uint32_t browser = NR_ATTRIBUTE_DESTINATION_BROWSER; + uint32_t log = NR_ATTRIBUTE_DESTINATION_LOG; config = nr_attribute_config_create(); @@ -414,9 +467,10 @@ static void test_config_apply(void) { /* * Test that the destination modifier are applied in the correct order. */ - nr_attribute_config_modify_destinations(config, "alpha.*", browser | trace, + nr_attribute_config_modify_destinations(config, "alpha.*", browser | trace | log, 0); nr_attribute_config_modify_destinations(config, "alpha.beta", error, browser); + nr_attribute_config_modify_destinations(config, "alpha.b*", 0, log); destinations = nr_attribute_config_apply(config, "alpha.beta", nr_mkhash("alpha.beta", 0), event); @@ -568,6 +622,7 @@ static void test_add(void) { uint32_t trace = NR_ATTRIBUTE_DESTINATION_TXN_TRACE; uint32_t error = NR_ATTRIBUTE_DESTINATION_ERROR; uint32_t browser = NR_ATTRIBUTE_DESTINATION_BROWSER; + uint32_t log = NR_ATTRIBUTE_DESTINATION_LOG; uint32_t all = NR_ATTRIBUTE_DESTINATION_ALL; nr_status_t st; nrobj_t* obj; @@ -605,6 +660,8 @@ static void test_add(void) { tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); st = nr_attributes_user_add_string(attributes, error, "gamma", "789"); tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); + st = nr_attributes_user_add_string(attributes, log, "delta", "abc"); + tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); st = nr_attributes_agent_add_long(attributes, browser | event, "psi", 123); tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); @@ -613,6 +670,8 @@ static void test_add(void) { st = nr_attributes_agent_add_string(attributes, browser | error, "theta", "789"); tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); + st = nr_attributes_agent_add_string(attributes, log, "chi", "abc"); + tlib_pass_if_true("add success", NR_SUCCESS == st, "st=%d", (int)st); st = nr_attributes_agent_add_string(attributes, 0, "no_destinations_ignore_me", "789"); @@ -625,10 +684,10 @@ static void test_add(void) { test_user_attributes_as_json( "user attributes: all", attributes, all, - "{\"gamma\":\"789\",\"beta\":456,\"alpha\":123}"); + "{\"delta\":\"abc\",\"gamma\":\"789\",\"beta\":456,\"alpha\":123}"); test_agent_attributes_as_json( "agent attributes: all", attributes, all, - "{\"theta\":\"789\",\"omega\":456,\"psi\":123}"); + "{\"chi\":\"abc\",\"theta\":\"789\",\"omega\":456,\"psi\":123}"); test_user_attributes_as_json("user attributes: event", attributes, event, "{\"alpha\":123}"); diff --git a/axiom/tests/test_cmd_txndata.c b/axiom/tests/test_cmd_txndata.c index e840e0614..0871d7b30 100644 --- a/axiom/tests/test_cmd_txndata.c +++ b/axiom/tests/test_cmd_txndata.c @@ -761,6 +761,137 @@ static void test_encode_custom_events(void) { nro_delete(params); } +static void test_encode_log_events(void) { + nrtxn_t txn; + nr_flatbuffers_table_t tbl; + nr_flatbuffer_t* fb; + nr_aoffset_t events; + nrtime_t now; + uint32_t count; + int data_type; + int did_pass; + nr_log_event_t* log1 = NULL; + nr_log_event_t* log2 = NULL; + nr_attributes_t* attributes = NULL; + nr_attribute_config_t* config = NULL; + + now = 123 * NR_TIME_DIVISOR; + + // create 2 log event events + log1 = nr_log_event_create(); + nr_log_event_set_log_level(log1, "LOG_LEVEL_TEST_ERROR"); + nr_log_event_set_message(log1, "\" \\ / \b \f \n \r \t GBP sign \xc2\xa3xxx"); + nr_log_event_set_timestamp(log1, now); + nr_log_event_set_trace_id(log1, "test id 1"); + nr_log_event_set_span_id(log1, "test id 2"); + nr_log_event_set_guid(log1, "test id 3"); + nr_log_event_set_entity_name(log1, "entity name here"); + nr_log_event_set_hostname(log1, "host name here"); + config = nr_attribute_config_create(); + attributes = nr_attributes_create(config); + nr_attribute_config_destroy(&config); + nr_attributes_user_add_string(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "string_attr", "string_attr_value"); + nr_attributes_user_add_long(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "long_attr", 12345); + nr_log_event_set_context_attributes(log1, attributes); + + log2 = nr_log_event_create(); + nr_log_event_set_log_level(log2, "LOG_LEVEL_TEST_WARN"); + nr_log_event_set_message(log2, "\" \\ / \b \f \n \r \t GBP sign \xc2\xa3xxx"); + nr_log_event_set_timestamp(log2, now); + nr_log_event_set_trace_id(log2, "test id 3"); + nr_log_event_set_span_id(log2, "test id 4"); + nr_log_event_set_guid(log2, "test id 5"); + nr_log_event_set_entity_name(log2, "entity name here 2"); + nr_log_event_set_hostname(log2, "host name here 2"); + + // add event2 + nr_memset(&txn, 0, sizeof(txn)); + txn.status.recording = 1; + txn.app_limits.log_events = 100; + txn.log_events = nr_log_events_create(100); + nr_log_events_add_event(txn.log_events, log1); + nr_log_events_add_event(txn.log_events, log2); + + fb = nr_txndata_encode(&txn); + nr_flatbuffers_table_init_root(&tbl, nr_flatbuffers_data(fb), + nr_flatbuffers_len(fb)); + + data_type = nr_flatbuffers_table_read_i8(&tbl, MESSAGE_FIELD_DATA_TYPE, + MESSAGE_BODY_NONE); + did_pass = tlib_pass_if_true(__func__, MESSAGE_BODY_TXN == data_type, + "data_type=%d", data_type); + if (0 != did_pass) { + goto done; + } + + did_pass = tlib_pass_if_true( + __func__, + 0 != nr_flatbuffers_table_read_union(&tbl, &tbl, MESSAGE_FIELD_DATA), + "transaction data missing"); + if (0 != did_pass) { + goto done; + } + + count = nr_flatbuffers_table_read_vector_len(&tbl, + TRANSACTION_FIELD_LOG_EVENTS); + if (0 != tlib_pass_if_true(__func__, 2 == count, "count=%d", count)) { + goto done; + } + + events = nr_flatbuffers_table_read_vector(&tbl, TRANSACTION_FIELD_LOG_EVENTS); + + nr_flatbuffers_table_init( + &tbl, tbl.data, tbl.length, + nr_flatbuffers_read_indirect(tbl.data, events).offset); + tlib_pass_if_bytes_equal_f( + __func__, + NR_PSTR("{" + "\"message\":\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t GBP sign " + "\\u00a3xxx\"," + "\"level\":\"LOG_LEVEL_TEST_WARN\"," + "\"trace.id\":\"test id 3\"," + "\"span.id\":\"test id 4\"," + "\"entity.guid\":\"test id 5\"," + "\"entity.name\":\"entity name here 2\"," + "\"hostname\":\"host name here 2\"," + "\"timestamp\":123000" + "}"), + nr_flatbuffers_table_read_bytes(&tbl, EVENT_FIELD_DATA), + nr_flatbuffers_table_read_vector_len(&tbl, EVENT_FIELD_DATA), __FILE__, + __LINE__); + + events.offset += sizeof(uint32_t); + nr_flatbuffers_table_init( + &tbl, tbl.data, tbl.length, + nr_flatbuffers_read_indirect(tbl.data, events).offset); + tlib_pass_if_bytes_equal_f( + __func__, + NR_PSTR("{" + "\"message\":\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t GBP sign " + "\\u00a3xxx\"," + "\"level\":\"LOG_LEVEL_TEST_ERROR\"," + "\"trace.id\":\"test id 1\"," + "\"span.id\":\"test id 2\"," + "\"entity.guid\":\"test id 3\"," + "\"entity.name\":\"entity name here\"," + "\"hostname\":\"host name here\"," + "\"timestamp\":123000," + "\"attributes\":{" + "\"context.long_attr\":12345," + "\"context.string_attr\":\"string_attr_value\"" + "}" + "}"), + nr_flatbuffers_table_read_bytes(&tbl, EVENT_FIELD_DATA), + nr_flatbuffers_table_read_vector_len(&tbl, EVENT_FIELD_DATA), __FILE__, + __LINE__); + +done: + nr_flatbuffers_destroy(&fb); + nr_txn_destroy_fields(&txn); +} + static void test_encode_trace(void) { nrtxn_t txn; nr_flatbuffers_table_t tbl; @@ -1081,6 +1212,7 @@ void test_main(void* p NRUNUSED) { test_encode_span_events(); test_encode_trace(); test_encode_txn_event(); + test_encode_log_events(); test_bad_daemon_fd(); test_null_txn(); diff --git a/axiom/tests/test_log_event.c b/axiom/tests/test_log_event.c index 38468892e..a5554411a 100644 --- a/axiom/tests/test_log_event.c +++ b/axiom/tests/test_log_event.c @@ -29,6 +29,8 @@ static void test_log_event_create_destroy(void) { static void test_log_event_to_json(void) { char* json; nr_log_event_t* log; + nr_attributes_t* attributes = NULL; + nr_attribute_config_t* config = NULL; /* * Test : Bad parameters. @@ -99,6 +101,14 @@ static void test_log_event_to_json(void) { nr_log_event_set_guid(log, "test id 3"); nr_log_event_set_entity_name(log, "entity name here"); nr_log_event_set_hostname(log, "host name here"); + config = nr_attribute_config_create(); + attributes = nr_attributes_create(config); + nr_attribute_config_destroy(&config); + nr_attributes_user_add_string(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "string_attr", "string_attr_value"); + nr_attributes_user_add_long(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "long_attr", 12345); + nr_log_event_set_context_attributes(log, attributes); json = nr_log_event_to_json(log); tlib_pass_if_str_equal( "requires escaping for JSON event", @@ -110,7 +120,11 @@ static void test_log_event_to_json(void) { "\"entity.guid\":\"test id 3\"," "\"entity.name\":\"entity name here\"," "\"hostname\":\"host name here\"," - "\"timestamp\":12345" + "\"timestamp\":12345," + "\"attributes\":{" + "\"context.long_attr\":12345," + "\"context.string_attr\":\"string_attr_value\"" + "}" "}", json); nr_free(json); @@ -120,6 +134,8 @@ static void test_log_event_to_json(void) { static void test_log_event_to_json_buffer(void) { nrbuf_t* buf = nr_buffer_create(0, 0); nr_log_event_t* log; + nr_attributes_t* attributes = NULL; + nr_attribute_config_t* config = NULL; /* * Test : Bad parameters. @@ -163,6 +179,14 @@ static void test_log_event_to_json_buffer(void) { nr_log_event_set_guid(log, "test id 3"); nr_log_event_set_entity_name(log, "entity name here"); nr_log_event_set_hostname(log, "host name here"); + config = nr_attribute_config_create(); + attributes = nr_attributes_create(config); + nr_attribute_config_destroy(&config); + nr_attributes_user_add_string(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "string_attr", "string_attr_value"); + nr_attributes_user_add_long(attributes, NR_ATTRIBUTE_DESTINATION_LOG, + "long_attr", 12345); + nr_log_event_set_context_attributes(log, attributes); tlib_pass_if_bool_equal("full log event", true, nr_log_event_to_json_buffer(log, buf)); nr_buffer_add(buf, NR_PSTR("\0")); @@ -175,7 +199,11 @@ static void test_log_event_to_json_buffer(void) { "\"entity.guid\":\"test id 3\"," "\"entity.name\":\"entity name here\"," "\"hostname\":\"host name here\"," - "\"timestamp\":12345" + "\"timestamp\":12345," + "\"attributes\":{" + "\"context.long_attr\":12345," + "\"context.string_attr\":\"string_attr_value\"" + "}" "}", nr_buffer_cptr(buf)); nr_log_event_destroy(&log); @@ -370,6 +398,28 @@ static void test_log_event_priority(void) { nr_log_event_set_priority(NULL, 0xFFFF); } +static void test_log_event_context_attributes(void) { + nr_log_event_t* event = nr_log_event_create(); + nr_attributes_t* attributes = NULL; + + // Test : Get context attributes with a NULL event + tlib_pass_if_null("Initialize event should have NULL context", + event->context_attributes); + + // Test: Setting context data on NULL event ptr should not crash + attributes = nr_attributes_create(NULL); + nr_log_event_set_context_attributes(NULL, attributes); + nr_attributes_destroy(&attributes); + + // Test: Setting a NULL context data ptr should not crash + nr_log_event_set_context_attributes(event, NULL); + + // Test: All NULL parameters should not crash + nr_log_event_set_context_attributes(NULL, NULL); + + nr_log_event_destroy(&event); +} + tlib_parallel_info_t parallel_info = {.suggested_nthreads = 1, .state_size = 0}; void test_main(void* p NRUNUSED) { @@ -385,4 +435,5 @@ void test_main(void* p NRUNUSED) { test_log_event_timestamp(); test_log_event_priority(); test_log_event_span_id(); + test_log_event_context_attributes(); } diff --git a/axiom/tests/test_txn.c b/axiom/tests/test_txn.c index d3571d6b1..37d9a8784 100644 --- a/axiom/tests/test_txn.c +++ b/axiom/tests/test_txn.c @@ -8180,7 +8180,7 @@ static void test_record_log_event(void) { */ txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); - nr_txn_record_log_event(NULL, NULL, NULL, 0, NULL); + nr_txn_record_log_event(NULL, NULL, NULL, 0, NULL, NULL); tlib_pass_if_int_equal("all params null, no crash, event not recorded", 0, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("all params null, no crash, event not recorded", 0, @@ -8188,7 +8188,7 @@ static void test_record_log_event(void) { nr_txn_destroy(&txn); txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); - nr_txn_record_log_event(NULL, LOG_EVENT_PARAMS, NULL); + nr_txn_record_log_event(NULL, LOG_EVENT_PARAMS, NULL, NULL); tlib_pass_if_int_equal("null txn, no crash, event not recorded", 0, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("null txn, no crash, event not recorded", 0, @@ -8200,7 +8200,7 @@ static void test_record_log_event(void) { * don't blow up! */ txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); - nr_txn_record_log_event(txn, NULL, NULL, 0, NULL); + nr_txn_record_log_event(txn, NULL, NULL, 0, NULL, NULL); tlib_pass_if_int_equal("null log params, event not recorded", 0, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("null log params, event not recorded", 0, @@ -8214,7 +8214,7 @@ static void test_record_log_event(void) { nr_txn_destroy(&txn); txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); - nr_txn_record_log_event(txn, NULL, LOG_MESSAGE, 0, NULL); + nr_txn_record_log_event(txn, NULL, LOG_MESSAGE, 0, NULL, NULL); tlib_pass_if_int_equal("null log level, event seen", 1, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("null log level, event saved", 1, @@ -8253,7 +8253,7 @@ static void test_record_log_event(void) { /* Happy path - everything initialized: record! */ txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); tlib_pass_if_int_equal("happy path, event seen", 1, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("happy path, event saved", 1, @@ -8303,11 +8303,11 @@ static void test_record_log_event(void) { /* fill up events pool to force sampling */ for (int i = 0, max_events = nr_log_events_max_events(txn->log_events); i < max_events; i++) { - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); } /* force sampling */ - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); test_txn_metric_is("happy path with sampling, events recorded and dropped", txn->unscoped_metrics, MET_FORCED, "Logging/Forwarding/Dropped", 2, 0, 0, 0, 0, 0); @@ -8321,8 +8321,8 @@ static void test_record_log_event(void) { tlib_pass_if_not_null("empty log events pool created", txn->log_events); tlib_pass_if_int_equal("empty log events pool stores 0 events", 0, nr_log_events_max_events(txn->log_events)); - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); /* Events are seen because log forwarding is enabled and txn->options.log_events_max_samples_stored > 0 */ tlib_pass_if_int_equal("happy path, event seen", 2, @@ -8337,7 +8337,7 @@ static void test_record_log_event(void) { /* High_security */ txn = new_txn_for_record_log_event_test(APP_ENTITY_NAME); txn->high_security = 1; - nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, &appv); + nr_txn_record_log_event(txn, LOG_EVENT_PARAMS, NULL, &appv); tlib_pass_if_int_equal("happy path, hsm, event seen", 0, nr_log_events_number_seen(txn->log_events)); tlib_pass_if_int_equal("happy path, hsm, event saved", 0, @@ -8355,17 +8355,17 @@ static void test_record_log_event(void) { /* default filter log level is LOG_LEVEL_WARNING */ /* these messages should be accepted */ - nr_txn_record_log_event(txn, LL_ALER_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_CRIT_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_WARN_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_EMER_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_UNKN_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, "APPLES", LOG_MESSAGE, 0, NULL); + nr_txn_record_log_event(txn, LL_ALER_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_CRIT_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_WARN_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_EMER_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_UNKN_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, "APPLES", LOG_MESSAGE, 0, NULL, NULL); /* these messages will be dropped */ - nr_txn_record_log_event(txn, LL_INFO_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_DEBU_STR, LOG_MESSAGE, 0, NULL); - nr_txn_record_log_event(txn, LL_NOTI_STR, LOG_MESSAGE, 0, NULL); + nr_txn_record_log_event(txn, LL_INFO_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_DEBU_STR, LOG_MESSAGE, 0, NULL, NULL); + nr_txn_record_log_event(txn, LL_NOTI_STR, LOG_MESSAGE, 0, NULL, NULL); /* events seen and saved are both 6 because the filtering occurs before * log forwarding handles the messages. diff --git a/tests/integration/attributes/test_filtering_1.php b/tests/integration/attributes/test_filtering_1.php new file mode 100644 index 000000000..fb8b01aa0 --- /dev/null +++ b/tests/integration/attributes/test_filtering_1.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "A": "A", + "B": "B", + "C": "C" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); +newrelic_add_custom_parameter("B", "B"); +newrelic_add_custom_parameter("C", "C"); + + diff --git a/tests/integration/attributes/test_filtering_10.php b/tests/integration/attributes/test_filtering_10.php new file mode 100644 index 000000000..3f321732e --- /dev/null +++ b/tests/integration/attributes/test_filtering_10.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "AC": "AC", + "AB": "AB", + "AA": "AA" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + diff --git a/tests/integration/attributes/test_filtering_11.php b/tests/integration/attributes/test_filtering_11.php new file mode 100644 index 000000000..60a149cb1 --- /dev/null +++ b/tests/integration/attributes/test_filtering_11.php @@ -0,0 +1,69 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + diff --git a/tests/integration/attributes/test_filtering_12.php b/tests/integration/attributes/test_filtering_12.php new file mode 100644 index 000000000..440a979a3 --- /dev/null +++ b/tests/integration/attributes/test_filtering_12.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "AC": "AC", + "AB": "AB", + "AA": "AA" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + diff --git a/tests/integration/attributes/test_filtering_2.php b/tests/integration/attributes/test_filtering_2.php new file mode 100644 index 000000000..a424d9129 --- /dev/null +++ b/tests/integration/attributes/test_filtering_2.php @@ -0,0 +1,68 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "C": "C", + "B": "B" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); +newrelic_add_custom_parameter("B", "B"); +newrelic_add_custom_parameter("C", "C"); + + diff --git a/tests/integration/attributes/test_filtering_3.php b/tests/integration/attributes/test_filtering_3.php new file mode 100644 index 000000000..74850761e --- /dev/null +++ b/tests/integration/attributes/test_filtering_3.php @@ -0,0 +1,73 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "A": "A", + "B": "B" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); +newrelic_add_custom_parameter("B", "B"); +newrelic_add_custom_parameter("C", "C"); + + diff --git a/tests/integration/attributes/test_filtering_4.php b/tests/integration/attributes/test_filtering_4.php new file mode 100644 index 000000000..22904355c --- /dev/null +++ b/tests/integration/attributes/test_filtering_4.php @@ -0,0 +1,69 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); + + + diff --git a/tests/integration/attributes/test_filtering_5.php b/tests/integration/attributes/test_filtering_5.php new file mode 100644 index 000000000..a0ccfecfa --- /dev/null +++ b/tests/integration/attributes/test_filtering_5.php @@ -0,0 +1,76 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "A": "A", + "B": "B", + "C": "C", + "D": "D" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); +newrelic_add_custom_parameter("B", "B"); +newrelic_add_custom_parameter("D", "D"); +newrelic_add_custom_parameter("C", "C"); + + diff --git a/tests/integration/attributes/test_filtering_6.php b/tests/integration/attributes/test_filtering_6.php new file mode 100644 index 000000000..8399327e6 --- /dev/null +++ b/tests/integration/attributes/test_filtering_6.php @@ -0,0 +1,73 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "B": "B", + "C": "C" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("A", "A"); +newrelic_add_custom_parameter("B", "B"); +newrelic_add_custom_parameter("C", "C"); + + diff --git a/tests/integration/attributes/test_filtering_7.php b/tests/integration/attributes/test_filtering_7.php new file mode 100644 index 000000000..ea4c484dc --- /dev/null +++ b/tests/integration/attributes/test_filtering_7.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "AC": "AC", + "AA": "AA", + "BB": "BB" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + diff --git a/tests/integration/attributes/test_filtering_8.php b/tests/integration/attributes/test_filtering_8.php new file mode 100644 index 000000000..9f53607b4 --- /dev/null +++ b/tests/integration/attributes/test_filtering_8.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "AB": "AB", + "BB": "BB" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + + diff --git a/tests/integration/attributes/test_filtering_9.php b/tests/integration/attributes/test_filtering_9.php new file mode 100644 index 000000000..b0b821321 --- /dev/null +++ b/tests/integration/attributes/test_filtering_9.php @@ -0,0 +1,74 @@ +", + [ + [ + 0, {}, {}, + "?? trace details", + { + "userAttributes": { + "AC": "AC", + "AA": "AA", + "BB": "BB" + }, + "intrinsics": "??" + } + ], + [ + "OtherTransaction/php__FILE__", + "Custom/force_transaction_trace" + ] + ], + "?? txn guid", + "?? reserved", + "?? force persist", + "?? x-ray sessions", + "?? synthetics resource id" + ] + ] +] +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../include/helpers.php'); + +force_transaction_trace(); + +newrelic_add_custom_parameter("AA", "AA"); +newrelic_add_custom_parameter("AB", "AB"); +newrelic_add_custom_parameter("AC", "AC"); +newrelic_add_custom_parameter("BB", "BB"); + diff --git a/tests/integration/logging/monolog2/test_monolog_basic_clm.php b/tests/integration/logging/monolog2/test_monolog_basic_clm.php new file mode 100644 index 000000000..50f234e57 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_basic_clm.php @@ -0,0 +1,160 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $logger->debug("debug"); + usleep(10000); + +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_basic_clm_off.php b/tests/integration/logging/monolog2/test_monolog_basic_clm_off.php new file mode 100644 index 000000000..c997317e7 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_basic_clm_off.php @@ -0,0 +1,154 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $logger->debug("debug"); + usleep(10000); + +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_default.php b/tests/integration/logging/monolog2/test_monolog_context_default.php new file mode 100644 index 000000000..5fbc32cf0 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_default.php @@ -0,0 +1,103 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_exception.php b/tests/integration/logging/monolog2/test_monolog_context_exception.php new file mode 100644 index 000000000..3cbd4c3bc --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_exception.php @@ -0,0 +1,104 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ['exception' => new \RuntimeException('Foo')]; + $logger->alert("context is nested array", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_extra1.php b/tests/integration/logging/monolog2/test_monolog_context_filter_extra1.php new file mode 100644 index 000000000..fb318cdc3 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_extra1.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_extra2.php b/tests/integration/logging/monolog2/test_monolog_context_filter_extra2.php new file mode 100644 index 000000000..5867f935c --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_extra2.php @@ -0,0 +1,110 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_extra3.php b/tests/integration/logging/monolog2/test_monolog_context_filter_extra3.php new file mode 100644 index 000000000..92812d6bd --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_extra3.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_extra4.php b/tests/integration/logging/monolog2/test_monolog_context_filter_extra4.php new file mode 100644 index 000000000..84c0fdfcc --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_extra4.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_extra5.php b/tests/integration/logging/monolog2/test_monolog_context_filter_extra5.php new file mode 100644 index 000000000..e29b78368 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_extra5.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule1.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule1.php new file mode 100644 index 000000000..bebe8489b --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule1.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule10.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule10.php new file mode 100644 index 000000000..3a817bb7e --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule10.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AB converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule11.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule11.php new file mode 100644 index 000000000..71e268443 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule11.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule2.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule2.php new file mode 100644 index 000000000..87f4f3968 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule2.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule3.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule3.php new file mode 100644 index 000000000..bb65ea6b7 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule3.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A B converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule4.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule4.php new file mode 100644 index 000000000..09f13b10c --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule4.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value", "D" => "D value"); + $logger->debug("A B converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule5.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule5.php new file mode 100644 index 000000000..252180a7b --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule5.php @@ -0,0 +1,112 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value", "D" => "D value"); + $logger->debug("A converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule6.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule6.php new file mode 100644 index 000000000..799336ee5 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule6.php @@ -0,0 +1,110 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule7.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule7.php new file mode 100644 index 000000000..55bc44caa --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule7.php @@ -0,0 +1,112 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule8.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule8.php new file mode 100644 index 000000000..d63c67184 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule8.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_filter_rule9.php b/tests/integration/logging/monolog2/test_monolog_context_filter_rule9.php new file mode 100644 index 000000000..b1be23aec --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_filter_rule9.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value","BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_hsm_disable_forwarding.php b/tests/integration/logging/monolog2/test_monolog_context_hsm_disable_forwarding.php new file mode 100644 index 000000000..bd10ed610 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_hsm_disable_forwarding.php @@ -0,0 +1,136 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); + usleep(10000); + + $context = [1 => "value"]; + $logger->info("key is int not converted", $context); + usleep(10000); + + $context = ["int" => 1]; + $logger->notice("int value converted", $context); + usleep(10000); + + $context = ["dbl" => 3.1415926]; + $logger->warning("dbl value converted", $context); + usleep(10000); + + $context = ["TRUE" => TRUE]; + $logger->error("TRUE value converted", $context); + usleep(10000); + + $context = array("FALSE" => FALSE); + $logger->critical("FALSE value converted", $context); + usleep(10000); + + $context = ["array" => array('foo' => 'bar', 'baz' => 'long')]; + $logger->alert("array value not converted", $context); + usleep(10000); + + $context = ["object" => $logger]; + $logger->emergency("object value not converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_limits_1.php b/tests/integration/logging/monolog2/test_monolog_context_limits_1.php new file mode 100644 index 000000000..72245794f --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_limits_1.php @@ -0,0 +1,105 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $key = str_repeat("A", 300); + $context = array($key => "value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_limits_2.php b/tests/integration/logging/monolog2/test_monolog_context_limits_2.php new file mode 100644 index 000000000..300365203 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_limits_2.php @@ -0,0 +1,108 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $value = str_repeat("A", 300); + $context = array("key" => $value); + $logger->debug("Value truncated", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog2/test_monolog_context_precedence_1.php b/tests/integration/logging/monolog2/test_monolog_context_precedence_1.php new file mode 100644 index 000000000..94b6b9069 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_precedence_1.php @@ -0,0 +1,106 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_precedence_2.php b/tests/integration/logging/monolog2/test_monolog_context_precedence_2.php new file mode 100644 index 000000000..1401e6571 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_precedence_2.php @@ -0,0 +1,106 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog2/test_monolog_context_simple.php b/tests/integration/logging/monolog2/test_monolog_context_simple.php new file mode 100644 index 000000000..f29ab92d8 --- /dev/null +++ b/tests/integration/logging/monolog2/test_monolog_context_simple.php @@ -0,0 +1,236 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); + usleep(10000); + + $context = [1 => "value"]; + $logger->info("key is int not converted", $context); + usleep(10000); + + $context = ["int" => 1]; + $logger->notice("int value converted", $context); + usleep(10000); + + $context = ["dbl" => 3.1415926]; + $logger->warning("dbl value converted", $context); + usleep(10000); + + $context = ["TRUE" => TRUE]; + $logger->error("TRUE value converted", $context); + usleep(10000); + + $context = array("FALSE" => FALSE); + $logger->critical("FALSE value converted", $context); + usleep(10000); + + $context = ["array" => array('foo' => 'bar', 'baz' => 'long')]; + $logger->alert("array value not converted", $context); + usleep(10000); + + $context = ["object" => $logger]; + $logger->emergency("object value not converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_default.php b/tests/integration/logging/monolog3/test_monolog_context_default.php new file mode 100644 index 000000000..f5dde42ad --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_default.php @@ -0,0 +1,103 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_exception.php b/tests/integration/logging/monolog3/test_monolog_context_exception.php new file mode 100644 index 000000000..23d7c28f0 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_exception.php @@ -0,0 +1,104 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ['exception' => new \RuntimeException('Foo')]; + $logger->alert("context is nested array", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_extra1.php b/tests/integration/logging/monolog3/test_monolog_context_filter_extra1.php new file mode 100644 index 000000000..443dc5248 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_extra1.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_extra2.php b/tests/integration/logging/monolog3/test_monolog_context_filter_extra2.php new file mode 100644 index 000000000..ce5d41251 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_extra2.php @@ -0,0 +1,110 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_extra3.php b/tests/integration/logging/monolog3/test_monolog_context_filter_extra3.php new file mode 100644 index 000000000..3abd81c6a --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_extra3.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_extra4.php b/tests/integration/logging/monolog3/test_monolog_context_filter_extra4.php new file mode 100644 index 000000000..2d7dcac60 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_extra4.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AB AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_extra5.php b/tests/integration/logging/monolog3/test_monolog_context_filter_extra5.php new file mode 100644 index 000000000..0496fa444 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_extra5.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule1.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule1.php new file mode 100644 index 000000000..27297c832 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule1.php @@ -0,0 +1,115 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule10.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule10.php new file mode 100644 index 000000000..6c5a0c94b --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule10.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AB converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule11.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule11.php new file mode 100644 index 000000000..eb3b8f4ad --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule11.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value", "BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule2.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule2.php new file mode 100644 index 000000000..648d24aa3 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule2.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule3.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule3.php new file mode 100644 index 000000000..3b3954a17 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule3.php @@ -0,0 +1,113 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A B converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule4.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule4.php new file mode 100644 index 000000000..6e07994f6 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule4.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value", "D" => "D value"); + $logger->debug("A B converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule5.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule5.php new file mode 100644 index 000000000..446120f46 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule5.php @@ -0,0 +1,112 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value", "D" => "D value"); + $logger->debug("A converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule6.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule6.php new file mode 100644 index 000000000..5544883ff --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule6.php @@ -0,0 +1,110 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule7.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule7.php new file mode 100644 index 000000000..d4c4f85a6 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule7.php @@ -0,0 +1,112 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("A converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule8.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule8.php new file mode 100644 index 000000000..abf12928f --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule8.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("A" => "A value", "B" => "B value", "C" => "C value"); + $logger->debug("B C converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_filter_rule9.php b/tests/integration/logging/monolog3/test_monolog_context_filter_rule9.php new file mode 100644 index 000000000..61c8f1a0c --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_filter_rule9.php @@ -0,0 +1,114 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $context = array("AA" => "AA value", "AB" => "AB value", "AC" => "AC value","BB" => "BB value"); + $logger->debug("AA AC converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_hsm_disable_forwarding.php b/tests/integration/logging/monolog3/test_monolog_context_hsm_disable_forwarding.php new file mode 100644 index 000000000..18d211b35 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_hsm_disable_forwarding.php @@ -0,0 +1,136 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); + usleep(10000); + + $context = [1 => "value"]; + $logger->info("key is int not converted", $context); + usleep(10000); + + $context = ["int" => 1]; + $logger->notice("int value converted", $context); + usleep(10000); + + $context = ["dbl" => 3.1415926]; + $logger->warning("dbl value converted", $context); + usleep(10000); + + $context = ["TRUE" => TRUE]; + $logger->error("TRUE value converted", $context); + usleep(10000); + + $context = array("FALSE" => FALSE); + $logger->critical("FALSE value converted", $context); + usleep(10000); + + $context = ["array" => array('foo' => 'bar', 'baz' => 'long')]; + $logger->alert("array value not converted", $context); + usleep(10000); + + $context = ["object" => $logger]; + $logger->emergency("object value not converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_limits_1.php b/tests/integration/logging/monolog3/test_monolog_context_limits_1.php new file mode 100644 index 000000000..0eb98376f --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_limits_1.php @@ -0,0 +1,105 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $key = str_repeat("A", 300); + $context = array($key => "value"); + $logger->debug("None converted", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_limits_2.php b/tests/integration/logging/monolog3/test_monolog_context_limits_2.php new file mode 100644 index 000000000..6efed9ff5 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_limits_2.php @@ -0,0 +1,108 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + $value = str_repeat("A", 300); + $context = array("key" => $value); + $logger->debug("Value truncated", $context); +} + +test_logging(); \ No newline at end of file diff --git a/tests/integration/logging/monolog3/test_monolog_context_precedence_1.php b/tests/integration/logging/monolog3/test_monolog_context_precedence_1.php new file mode 100644 index 000000000..6c66317b6 --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_precedence_1.php @@ -0,0 +1,106 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_precedence_2.php b/tests/integration/logging/monolog3/test_monolog_context_precedence_2.php new file mode 100644 index 000000000..451e5f49b --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_precedence_2.php @@ -0,0 +1,106 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); +} + +test_logging(); diff --git a/tests/integration/logging/monolog3/test_monolog_context_simple.php b/tests/integration/logging/monolog3/test_monolog_context_simple.php new file mode 100644 index 000000000..3c6fd51ef --- /dev/null +++ b/tests/integration/logging/monolog3/test_monolog_context_simple.php @@ -0,0 +1,236 @@ +setFormatter($formatter); + + $logger->pushHandler($stdoutHandler); + + // insert delays between log messages to allow priority sampling + // to resolve that later messages have higher precedence + // since timestamps are only millisecond resolution + // without delays sometimes order in output will reflect + // all having the same timestamp. + $context = ["testkey_string" => "value"]; + $logger->debug("key is string converted", $context); + usleep(10000); + + $context = [1 => "value"]; + $logger->info("key is int not converted", $context); + usleep(10000); + + $context = ["int" => 1]; + $logger->notice("int value converted", $context); + usleep(10000); + + $context = ["dbl" => 3.1415926]; + $logger->warning("dbl value converted", $context); + usleep(10000); + + $context = ["TRUE" => TRUE]; + $logger->error("TRUE value converted", $context); + usleep(10000); + + $context = array("FALSE" => FALSE); + $logger->critical("FALSE value converted", $context); + usleep(10000); + + $context = ["array" => array('foo' => 'bar', 'baz' => 'long')]; + $logger->alert("array value not converted", $context); + usleep(10000); + + $context = ["object" => $logger]; + $logger->emergency("object value not converted", $context); +} + +test_logging();