12
12
#include "php_wrapper.h"
13
13
#include "fw_hooks.h"
14
14
#include "fw_support.h"
15
+ #include "lib_monolog_private.h"
15
16
#include "nr_datastore_instance.h"
16
17
#include "nr_segment_datastore.h"
18
+ #include "nr_txn.h"
17
19
#include "util_logging.h"
20
+ #include "util_object.h"
18
21
#include "util_memory.h"
19
22
#include "util_strings.h"
20
23
#include "util_sleep.h"
27
30
#define LOG_DECORATE_PROC_FUNC_NAME \
28
31
"newrelic_phpagent_monolog_decorating_processor"
29
32
30
- // clang-format off
31
- /*
32
- * This macro affects how instrumentation $context argument of
33
- * Monolog\Logger::addRecord works:
34
- *
35
- * 0 - $context argument will not be instrumented: its existance and value
36
- * are ignored
37
- * 1 - the message of the log record forwarded by the agent will have the value
38
- * of $context appended to the value of $message.
39
- */
40
- // clang-format on
41
- #define HAVE_CONTEXT_IN_MESSAGE 0
42
-
43
33
/*
44
34
* Purpose : Convert Monolog\Logger::API to integer
45
35
*
@@ -155,123 +145,78 @@ static char* nr_monolog_get_message(NR_EXECUTE_PROTO TSRMLS_DC) {
155
145
return message ;
156
146
}
157
147
158
- #if HAVE_CONTEXT_IN_MESSAGE
159
148
/*
160
- * Purpose : Format key of $ context array's element as string
149
+ * Purpose : Convert a zval value from context data to a nrobj_t
161
150
*
162
- * Params : zend_hash_key
163
- * *
164
- * Returns : A new string representing zval; caller must free
165
- *
166
- */
167
- static char * nr_monolog_fmt_context_key (const zend_hash_key * hash_key ) {
168
- char * key_str = NULL ;
169
- zval * key = nr_php_zval_alloc ();
170
- if (nr_php_zend_hash_key_is_string (hash_key )) {
171
- nr_php_zval_str (key , nr_php_zend_hash_key_string_value (hash_key ));
172
- key_str = nr_formatf ("%s" , Z_STRVAL_P (key ));
173
- } else if (nr_php_zend_hash_key_is_numeric (hash_key )) {
174
- ZVAL_LONG (key , (zend_long )nr_php_zend_hash_key_integer (hash_key ));
175
- key_str = nr_formatf ("%ld" , (long )Z_LVAL_P (key ));
176
- } else {
177
- /*
178
- * This is a warning because this really, really shouldn't ever happen.
179
- */
180
- nrl_warning (NRL_INSTRUMENT , "%s: unexpected key type" , __func__ );
181
- key_str = nr_formatf ("unsupported-key-type" );
182
- }
183
- nr_php_zval_free (& key );
184
- return key_str ;
185
- }
186
-
187
- /*
188
- * Purpose : Format value of $context array's element as string
151
+ * Params : zval
189
152
*
190
- * Params : zval value
191
- * *
192
- * Returns : A new string representing zval; caller must free
153
+ * Returns : nrobj_t* holding converted value
154
+ * NULL otherwise
193
155
*
156
+ * Notes : Only scalar and string types are supported.
157
+ * Nested arrays are not converted and are ignored.
158
+ * Other zval types are also ignored.
194
159
*/
195
- static char * nr_monolog_fmt_context_value ( zval * zv ) {
196
- char * val_str = NULL ;
197
- zval * zv_str = NULL ;
160
+ nrobj_t * nr_monolog_context_data_zval_to_attribute_obj (
161
+ const zval * z TSRMLS_DC ) {
162
+ nrobj_t * retobj = NULL ;
198
163
199
- if (NULL == zv ) {
200
- return nr_strdup ( "" ) ;
164
+ if (NULL == z ) {
165
+ return NULL ;
201
166
}
202
167
203
- zv_str = nr_php_zval_alloc ();
204
- if (NULL == zv_str ) {
205
- return nr_strdup ("" );
206
- }
168
+ nr_php_zval_unwrap (z );
207
169
208
- ZVAL_DUP ( zv_str , zv );
209
- convert_to_string ( zv_str );
210
- val_str = nr_strdup ( Z_STRVAL_P ( zv_str )) ;
211
- nr_php_zval_free ( & zv_str ) ;
170
+ switch ( Z_TYPE_P ( z )) {
171
+ case IS_NULL :
172
+ retobj = NULL ;
173
+ break ;
212
174
213
- return val_str ;
214
- }
175
+ case IS_LONG :
176
+ retobj = nro_new_long ((long )Z_LVAL_P (z ));
177
+ break ;
215
178
216
- /*
217
- * Purpose : Format an element of $context array as "key => value" string
218
- *
219
- * Params : zval value, pointer to string buffer to store formatted output
220
- * and hash key
221
- *
222
- * Side effect : string buffer is reallocated with each call.
223
- *
224
- * Returns : ZEND_HASH_APPLY_KEEP to keep iteration
225
- *
226
- */
227
- static int nr_monolog_fmt_context_item (zval * value ,
228
- char * * strbuf ,
229
- zend_hash_key * hash_key TSRMLS_DC ) {
230
- NR_UNUSED_TSRMLS ;
231
- char * key = nr_monolog_fmt_context_key (hash_key );
232
- char * val = nr_monolog_fmt_context_value (value );
233
-
234
- char * kv_str = nr_formatf ("%s => %s" , key , val );
235
- nr_free (key );
236
- nr_free (val );
237
-
238
- char * sep = nr_strlen (* strbuf ) > 1 ? ", " : "" ;
239
- * strbuf = nr_str_append (* strbuf , kv_str , sep );
240
- nr_free (kv_str );
241
-
242
- return ZEND_HASH_APPLY_KEEP ;
243
- }
179
+ case IS_DOUBLE :
180
+ retobj = nro_new_double (Z_DVAL_P (z ));
181
+ break ;
244
182
245
- /*
246
- * Purpose : Iterate over $context array and format each element
247
- *
248
- * Params : string buffer to store formatted output and
249
- * Monolog\Logger::addRecord argument list
250
- *
251
- * Returns : A new string with Monolog's log context
252
- */
253
- static char * nr_monolog_fmt_context (char * strbuf ,
254
- HashTable * context TSRMLS_DC ) {
255
- strbuf = nr_str_append (strbuf , "[" , "" );
183
+ case IS_TRUE :
184
+ retobj = nro_new_boolean (true);
185
+ break ;
186
+
187
+ case IS_FALSE :
188
+ retobj = nro_new_boolean (false);
189
+ break ;
256
190
257
- nr_php_zend_hash_zval_apply (context ,
258
- (nr_php_zval_apply_t )nr_monolog_fmt_context_item ,
259
- (void * )& strbuf TSRMLS_CC );
191
+ case IS_STRING :
192
+ if (!nr_php_is_zval_valid_string (z )) {
193
+ retobj = NULL ;
194
+ } else {
195
+ retobj = nro_new_string (Z_STRVAL_P (z ));
196
+ }
197
+ break ;
198
+
199
+ default :
200
+ /* any other type conversion to attribute not supported */
201
+ retobj = NULL ;
202
+ break ;
203
+ }
260
204
261
- return nr_str_append ( strbuf , "]" , "" ) ;
205
+ return retobj ;
262
206
}
263
207
264
208
/*
265
- * Purpose : Convert $context argument of Monolog\Logger::addRecord to a string
209
+ * Purpose : Get $context argument of Monolog\Logger::addRecord as `zval *`.
266
210
*
267
211
* Params : # of Monolog\Logger::addRecord arguments, and
268
212
* Monolog\Logger::addRecord argument list
269
213
*
270
- * Returns : A new string with Monolog's log context
214
+ * Returns : zval* for context array on success (must be freed by caller)
215
+ * NULL otherwise
216
+ *
271
217
*/
272
- static char * nr_monolog_get_context (const size_t argc ,
273
- NR_EXECUTE_PROTO TSRMLS_DC ) {
274
- char * context = nr_strdup ("" );
218
+ static zval * nr_monolog_extract_context_data (const size_t argc ,
219
+ NR_EXECUTE_PROTO TSRMLS_DC ) {
275
220
zval * context_arg = NULL ;
276
221
277
222
if (3 > argc ) {
@@ -298,45 +243,57 @@ static char* nr_monolog_get_context(const size_t argc,
298
243
goto return_context ;
299
244
}
300
245
301
- context = nr_monolog_fmt_context (context , Z_ARRVAL_P (context_arg ) TSRMLS_CC );
302
-
303
246
return_context :
304
- nr_php_arg_release (& context_arg );
305
- return context ;
247
+ return context_arg ;
306
248
}
307
- #endif
308
249
309
250
/*
310
- * Purpose : Combine $message and $context arguments of
311
- * Monolog\Logger::addRecord into a single string to be used as a message
312
- * property of the log event.
251
+ * Purpose : Convert $context array of Monolog\Logger::addRecord to
252
+ * attributes
313
253
*
314
- * Params : # of Monolog\Logger::addRecord arguments, and
315
- * Monolog\Logger::addRecord argument list
254
+ * Params : zval* for context array from Monolog
255
+ *
256
+ * Returns : nr_attributes representation of $context on success
257
+ * NULL otherwise
316
258
*
317
- * Returns : A new string with a log record message; caller must free
318
259
*/
319
- static char * nr_monolog_build_message (const size_t argc ,
320
- NR_EXECUTE_PROTO TSRMLS_DC ) {
321
- #if !HAVE_CONTEXT_IN_MESSAGE
322
- /* Make the compiler happy - argc is not used when $context is ignored */
323
- (void )argc ;
324
- #endif
325
- char * message_and_context = nr_strdup ("" );
326
-
327
- char * message = nr_monolog_get_message (NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
328
- message_and_context = nr_str_append (message_and_context , message , "" );
329
- nr_free (message );
260
+ nr_attributes_t * nr_monolog_convert_context_data_to_attributes (
261
+ zval * context_data TSRMLS_DC ) {
262
+ zend_string * key ;
263
+ zval * val ;
330
264
331
- #if HAVE_CONTEXT_IN_MESSAGE
332
- char * context = nr_monolog_get_context (argc , NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
333
- if (!nr_strempty (context )) {
334
- message_and_context = nr_str_append (message_and_context , context , " " );
265
+ nr_attributes_t * attributes = NULL ;
266
+
267
+ if (NULL == context_data || !nr_php_is_zval_valid_array (context_data )) {
268
+ return NULL ;
269
+ }
270
+
271
+ attributes = nr_attributes_create (NRPRG (txn )-> attribute_config );
272
+ if (NULL == attributes ) {
273
+ return NULL ;
274
+ }
275
+
276
+ ZEND_HASH_FOREACH_STR_KEY_VAL (Z_ARR_P (context_data ), key , val ) {
277
+ if (NULL == key ) {
278
+ continue ;
279
+ }
280
+
281
+ nrobj_t * obj = nr_monolog_context_data_zval_to_attribute_obj (val );
282
+
283
+ if (NULL != obj ) {
284
+ nr_attributes_user_add (attributes , NR_ATTRIBUTE_DESTINATION_LOG ,
285
+ ZSTR_VAL (key ), obj );
286
+ nro_delete (obj );
287
+ } else {
288
+ nrl_verbosedebug (NRL_INSTRUMENT ,
289
+ "%s: log context attribute '%s' dropped due to value "
290
+ "being of unsupported type %d" ,
291
+ __func__ , ZSTR_VAL (key ), Z_TYPE_P (val ));
292
+ }
335
293
}
336
- nr_free (context );
337
- #endif
294
+ ZEND_HASH_FOREACH_END ();
338
295
339
- return message_and_context ;
296
+ return attributes ;
340
297
}
341
298
342
299
/*
@@ -397,21 +354,30 @@ NR_PHP_WRAPPER(nr_monolog_logger_addrecord) {
397
354
int api = 0 ;
398
355
size_t argc = 0 ;
399
356
char * message = NULL ;
357
+ nr_attributes_t * context_attributes = NULL ;
400
358
nrtime_t timestamp = nr_get_time ();
401
359
402
360
/* Values of $message and $timestamp arguments are needed only if log
403
361
* forwarding is enabled so agent will get them conditionally */
404
362
if (nr_txn_log_forwarding_enabled (NRPRG (txn ))) {
405
363
argc = nr_php_get_user_func_arg_count (NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
406
- message = nr_monolog_build_message (argc , NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
364
+ message = nr_monolog_get_message (NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
365
+
366
+ if (nr_txn_log_forwarding_context_data_enabled (NRPRG (txn ))) {
367
+ zval * context_data = nr_monolog_extract_context_data (
368
+ argc , NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
369
+ context_attributes
370
+ = nr_monolog_convert_context_data_to_attributes (context_data );
371
+ nr_php_arg_release (& context_data );
372
+ }
407
373
api = nr_monolog_version (this_var TSRMLS_CC );
408
374
timestamp
409
375
= nr_monolog_get_timestamp (api , argc , NR_EXECUTE_ORIG_ARGS TSRMLS_CC );
410
376
}
411
377
412
378
/* Record the log event */
413
379
nr_txn_record_log_event (NRPRG (txn ), level_name , message , timestamp ,
414
- NRPRG (app ));
380
+ context_attributes , NRPRG (app ));
415
381
416
382
nr_free (level_name );
417
383
nr_free (message );
0 commit comments