Skip to content

Commit 8b0ba0f

Browse files
mfulbbduranleau-nr
andauthored
feat(agent): add log level config option (newrelic#525)
* feat(agent, axiom): add log level config option * fix(axiom): add NULL check for pointer arg * fix(axiom): include stdbool for boolean type use in nr_log_level.c * fix(axiom): do not execute level compare block if err == true * fix(axiom): remove parenthesis from Log Level string macro defines * feat(agent,axiom): add log_forwarding_log_level to txn opts * feat(axiom): verify valid log level based on config setting for txns * feat(axiom/tests): update log test framework to use new Log Level macros * chore(agent): Add test_log_level test to gitignore * chore(agent): Rework nr_log_levels * No longer return allocated string from nr_log_level_rfc_to_psr(). * Cleanup API doc. * chore(agent): Reword log level init message * fix(agent): Updates log level filtering * Ensures filtered events included in "Dropped" log forwarding metric * Remove redundant filtering then already occurs in nr_txn_log_forwarding_enabled(). * test(agent): Enhance log level filtering tests * Added test_log_level.c to exercise API. * Extended transaction tests to include examples where log level filtering would reject some messages. * test(agent): Integration test for log level filtering * fix(agent): Fix rebase issue After the rebase it is no longer necessary to destroy the event passed to nr_log_events_add_event(). * test(agent): Update test case after rebase Fixed "log.level" to "level" and move "timestamp" field to proper location in JSON output after rebase with dev. * fix(agent): Fix spelling of log level filtering INI value * fix(agent): If unknown log level filter given use default * test(agent): Fix logging integration tests With the addition of log level filtering is was necessary to update all the monolog integration tests to include this option. Tests for log level filtering were also added. * test(agent): Set log level filter for integration tests * test(agent): Include logging metrics in expected Co-authored-by: Brian Duranleau <[email protected]>
1 parent bb144af commit 8b0ba0f

File tree

49 files changed

+1163
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1163
-12
lines changed

agent/php_newrelic.h

+10-5
Original file line numberDiff line numberDiff line change
@@ -465,14 +465,19 @@ nrinitime_t
465465
* configuration options for handling application logging
466466
*/
467467

468-
nrinibool_t logging_enabled; /* newrelic.application_logging.enabled */
469-
nrinibool_t log_forwarding_enabled; /* newrelic.application_logging.forwarding.enabled
470-
*/
471-
nriniuint_t log_events_max_samples_stored; /* newrelic.application_logging.forwarding.max_samples_stored
472-
*/
468+
nrinibool_t logging_enabled; /* newrelic.application_logging.enabled */
469+
nrinibool_t
470+
log_forwarding_enabled; /* newrelic.application_logging.forwarding.enabled
471+
*/
472+
nriniuint_t
473+
log_events_max_samples_stored; /* newrelic.application_logging.forwarding.max_samples_stored
474+
*/
473475
nrinibool_t
474476
log_metrics_enabled; /* newrelic.application_logging.metrics.enabled */
475477

478+
nriniuint_t log_forwarding_log_level; /* newrelic.application_logging.forwarding.log_level
479+
*/
480+
476481
/*
477482
* pid and user_function_wrappers are used to store user function wrappers.
478483
* Storing this on a request level (as opposed to storing it on transaction

agent/php_nrini.c

+47
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "nr_configstrings.h"
1414
#include "nr_limits.h"
1515
#include "nr_version.h"
16+
#include "nr_log_level.h"
1617
#include "util_buffer.h"
1718
#include "util_json.h"
1819
#include "util_logging.h"
@@ -1802,6 +1803,44 @@ static PHP_INI_MH(nr_log_events_max_samples_stored_mh) {
18021803
return SUCCESS;
18031804
}
18041805

1806+
static PHP_INI_MH(nr_log_forwarding_log_level_mh) {
1807+
nriniuint_t* p;
1808+
int log_level = LOG_LEVEL_DEFAULT;
1809+
1810+
#ifndef ZTS
1811+
char* base = (char*)mh_arg2;
1812+
#else
1813+
char* base = (char*)ts_resource(*((int*)mh_arg2));
1814+
#endif
1815+
1816+
p = (nriniuint_t*)(base + (size_t)mh_arg1);
1817+
1818+
(void)entry;
1819+
(void)mh_arg3;
1820+
NR_UNUSED_TSRMLS;
1821+
1822+
p->where = 0;
1823+
1824+
if (NEW_VALUE_LEN > 0) {
1825+
nrl_debug(NRL_INIT, "Log Level (PSR-3): %s", NEW_VALUE);
1826+
1827+
log_level = nr_log_level_str_to_int(NEW_VALUE);
1828+
if (LOG_LEVEL_UNKNOWN == log_level) {
1829+
log_level = LOG_LEVEL_DEFAULT;
1830+
nrl_warning(NRL_INIT, "Unknown log forwarding level %s, using %s instead.",
1831+
NEW_VALUE, nr_log_level_rfc_to_psr(log_level));
1832+
}
1833+
p->value = log_level;
1834+
p->where = stage;
1835+
1836+
nrl_debug(NRL_INIT, "Log Forwarding Log Level (RFC5424) set to: %d (%s)",
1837+
p->value, nr_log_level_rfc_to_psr(p->value));
1838+
return SUCCESS;
1839+
}
1840+
1841+
return FAILURE;
1842+
}
1843+
18051844
/*
18061845
* Now for the actual INI entry table. Please note there are two types of INI
18071846
* entry specification used.
@@ -2851,6 +2890,14 @@ STD_PHP_INI_ENTRY_EX(
28512890
zend_newrelic_globals,
28522891
newrelic_globals,
28532892
0)
2893+
STD_PHP_INI_ENTRY_EX("newrelic.application_logging.forwarding.log_level",
2894+
"WARNING",
2895+
NR_PHP_REQUEST,
2896+
nr_log_forwarding_log_level_mh,
2897+
log_forwarding_log_level,
2898+
zend_newrelic_globals,
2899+
newrelic_globals,
2900+
0)
28542901
STD_PHP_INI_ENTRY_EX("newrelic.application_logging.metrics.enabled",
28552902
"1",
28562903
NR_PHP_REQUEST,

agent/php_txn.c

+1
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ nr_status_t nr_php_txn_begin(const char* appnames,
737737
opts.span_queue_batch_timeout = NRINI(agent_span_queue_timeout);
738738
opts.logging_enabled = NRINI(logging_enabled);
739739
opts.log_forwarding_enabled = NRINI(log_forwarding_enabled);
740+
opts.log_forwarding_log_level = NRINI(log_forwarding_log_level);
740741
opts.log_events_max_samples_stored = NRINI(log_events_max_samples_stored);
741742
opts.log_metrics_enabled = NRINI(log_metrics_enabled);
742743

agent/scripts/newrelic.ini.template

+23
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,29 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log"
11741174
;
11751175
;newrelic.application_logging.forwarding.max_samples_stored = 10000
11761176

1177+
; Setting: newrelic.application_logging.forwarding.log_level
1178+
; Type : string
1179+
; Scope : per-directory
1180+
; Default: "WARNING"
1181+
; Info : Sets the minimum log level to be collected when log forwarding
1182+
; is enabled.
1183+
;
1184+
; Based on
1185+
; https://www.php-fig.org/psr/psr-3/#5-psrlogloglevel
1186+
; https://datatracker.ietf.org/doc/html/rfc5424
1187+
;
1188+
; Valid settings (ordered highest to lowest):
1189+
; "EMERGENCY"
1190+
; "ALERT"
1191+
; "CRITICAL"
1192+
; "ERROR"
1193+
; "WARNING"
1194+
; "NOTICE"
1195+
; "INFO"
1196+
; "DEBUG"
1197+
;
1198+
;newrelic.application_logging.forwarding.log_level = "WARNING"
1199+
11771200
; Setting: newrelic.application_logging.metrics.enabled
11781201
; Type : boolean
11791202
; Scope : per-directory

axiom/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ OBJS := \
104104
nr_header.o \
105105
nr_log_event.o \
106106
nr_log_events.o \
107+
nr_log_level.o \
107108
nr_mysqli_metadata.o \
108109
nr_postgres.o \
109110
nr_rules.o \

axiom/nr_log_level.c

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright 2022 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "nr_axiom.h"
7+
8+
#include <stddef.h>
9+
10+
#include "nr_log_level.h"
11+
#include "util_strings.h"
12+
#include "util_logging.h"
13+
#include "util_memory.h"
14+
15+
int nr_log_level_str_to_int(const char* str) {
16+
int level;
17+
bool err = false;
18+
19+
if (NULL == str) {
20+
err = true;
21+
}
22+
23+
#define LEVELCMP(x) (0 == nr_stricmp(str, x))
24+
25+
if (!err) {
26+
if (LEVELCMP(LL_EMER_STR)) {
27+
level = LOG_LEVEL_EMERGENCY;
28+
} else if (LEVELCMP(LL_ALER_STR)) {
29+
level = LOG_LEVEL_ALERT;
30+
} else if (LEVELCMP(LL_CRIT_STR)) {
31+
level = LOG_LEVEL_CRITICAL;
32+
} else if (LEVELCMP(LL_ERRO_STR)) {
33+
level = LOG_LEVEL_ERROR;
34+
} else if (LEVELCMP(LL_WARN_STR)) {
35+
level = LOG_LEVEL_WARNING;
36+
} else if (LEVELCMP(LL_NOTI_STR)) {
37+
level = LOG_LEVEL_NOTICE;
38+
} else if (LEVELCMP(LL_INFO_STR)) {
39+
level = LOG_LEVEL_INFO;
40+
} else if (LEVELCMP(LL_DEBU_STR)) {
41+
level = LOG_LEVEL_DEBUG;
42+
} else {
43+
err = true;
44+
}
45+
}
46+
47+
#undef LEVELCMP
48+
49+
if (err) {
50+
nrl_warning(
51+
NRL_INIT,
52+
"Unknown Log Forwarding Log Level Specified; Defaulting to \"%s\"",
53+
nr_log_level_rfc_to_psr(LOG_LEVEL_UNKNOWN));
54+
level = LOG_LEVEL_UNKNOWN;
55+
}
56+
57+
return level;
58+
}
59+
60+
const char* nr_log_level_rfc_to_psr(int level) {
61+
const char* psr = NULL;
62+
63+
switch (level) {
64+
case LOG_LEVEL_EMERGENCY:
65+
psr = LL_EMER_STR;
66+
break;
67+
case LOG_LEVEL_ALERT:
68+
psr = LL_ALER_STR;
69+
break;
70+
case LOG_LEVEL_CRITICAL:
71+
psr = LL_CRIT_STR;
72+
break;
73+
case LOG_LEVEL_ERROR:
74+
psr = LL_ERRO_STR;
75+
break;
76+
case LOG_LEVEL_WARNING:
77+
psr = LL_WARN_STR;
78+
break;
79+
case LOG_LEVEL_NOTICE:
80+
psr = LL_NOTI_STR;
81+
break;
82+
case LOG_LEVEL_INFO:
83+
psr = LL_INFO_STR;
84+
break;
85+
case LOG_LEVEL_DEBUG:
86+
psr = LL_DEBU_STR;
87+
break;
88+
default:
89+
psr = LL_UNKN_STR;
90+
break;
91+
}
92+
93+
return psr;
94+
}

axiom/nr_log_level.h

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2022 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef NR_LOG_LEVEL_HDR
7+
#define NR_LOG_LEVEL_HDR
8+
9+
#include <stdbool.h>
10+
11+
/*
12+
* Implementation Based on:
13+
* https://www.php-fig.org/psr/psr-3/#5-psrlogloglevel
14+
* https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1
15+
*/
16+
17+
#define LOG_LEVEL_EMERGENCY (0) // system is unusable
18+
#define LOG_LEVEL_ALERT (1) // action must be taken immediately
19+
#define LOG_LEVEL_CRITICAL (2) // critical conditions
20+
#define LOG_LEVEL_ERROR (3) // error conditions
21+
#define LOG_LEVEL_WARNING (4) // warning conditions
22+
#define LOG_LEVEL_NOTICE (5) // normal but significant conditions
23+
#define LOG_LEVEL_INFO (6) // informational messages
24+
#define LOG_LEVEL_DEBUG (7) // debug-level messages
25+
#define LOG_LEVEL_UNKNOWN (8) // NON PSR- Unknown/Undefined log level
26+
#define LOG_LEVEL_DEFAULT (LOG_LEVEL_WARNING)
27+
28+
#define LL_EMER_STR "EMERGENCY"
29+
#define LL_ALER_STR "ALERT"
30+
#define LL_CRIT_STR "CRITICAL"
31+
#define LL_ERRO_STR "ERROR"
32+
#define LL_WARN_STR "WARNING"
33+
#define LL_NOTI_STR "NOTICE"
34+
#define LL_INFO_STR "INFO"
35+
#define LL_DEBU_STR "DEBUG"
36+
#define LL_UNKN_STR "UNKNOWN"
37+
38+
/*
39+
* Purpose : Convert PSR-3 string log level to RFC5424 represenation.
40+
*
41+
* Params : 1. str String Log Level
42+
*
43+
* Returns : Integer RFC5424 Log Level numerical code
44+
*/
45+
extern int nr_log_level_str_to_int(const char* str);
46+
47+
/*
48+
* Purpose : Convert RFC5424 log level to PSR-3 string represenation.
49+
*
50+
* Params : 1. level RFC5424 Log Level
51+
*
52+
* Returns : PSR-3 String Log Level as a const char *
53+
*
54+
*/
55+
extern const char* nr_log_level_rfc_to_psr(int level);
56+
57+
#endif /* NR_LOG_LEVEL_HDR */

axiom/nr_txn.c

+34-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "nr_header.h"
1919
#include "nr_limits.h"
2020
#include "nr_log_events.h"
21+
#include "nr_log_level.h"
2122
#include "nr_segment.h"
2223
#include "nr_segment_private.h"
2324
#include "nr_segment_traces.h"
@@ -3269,6 +3270,29 @@ bool nr_txn_log_forwarding_enabled(nrtxn_t* txn) {
32693270
return true;
32703271
}
32713272

3273+
bool nr_txn_log_forwarding_log_level_verify(nrtxn_t* txn,
3274+
const char* log_level_name) {
3275+
int log_level;
3276+
3277+
if (NULL == txn) { /* more like an assert */
3278+
return false;
3279+
}
3280+
3281+
log_level = nr_log_level_str_to_int(log_level_name);
3282+
3283+
// pass through UNKNOWN by default
3284+
if (LOG_LEVEL_UNKNOWN == log_level) {
3285+
return true;
3286+
}
3287+
3288+
// log levels are organized 0 -> 7 in decreasing severity
3289+
if (log_level > txn->options.log_forwarding_log_level) {
3290+
return false;
3291+
}
3292+
3293+
return true;
3294+
}
3295+
32723296
bool nr_txn_log_metrics_enabled(nrtxn_t* txn) {
32733297
if (NULL == txn) { /* more like an assert */
32743298
return false;
@@ -3377,12 +3401,18 @@ static void nr_txn_add_log_event(nrtxn_t* txn,
33773401
return;
33783402
}
33793403

3380-
e = log_event_create(log_level_name, log_message, timestamp, txn, app);
3381-
if (NULL == e) {
3382-
nrl_debug(NRL_TXN, "%s: failed to create log event", __func__);
3404+
/* log events filtered out by log level will go into the Dropped metric */
3405+
if (!nr_txn_log_forwarding_log_level_verify(txn, log_level_name)) {
33833406
event_dropped = true;
33843407
} else {
3385-
event_dropped = nr_log_events_add_event(txn->log_events, e);
3408+
/* event passed log level filter so add it */
3409+
e = log_event_create(log_level_name, log_message, timestamp, txn, app);
3410+
if (NULL == e) {
3411+
nrl_debug(NRL_TXN, "%s: failed to create log event", __func__);
3412+
event_dropped = true;
3413+
} else {
3414+
event_dropped = nr_log_events_add_event(txn->log_events, e);
3415+
}
33863416
}
33873417

33883418
if (event_dropped) {

axiom/nr_txn.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "nr_errors.h"
2424
#include "nr_file_naming.h"
2525
#include "nr_log_events.h"
26+
#include "nr_log_level.h"
2627
#include "nr_segment.h"
2728
#include "nr_slowsqls.h"
2829
#include "nr_span_queue.h"
@@ -117,7 +118,9 @@ typedef struct _nrtxnopt_t {
117118
nrtime_t span_queue_batch_timeout; /* Span queue batch timeout in us. */
118119
bool logging_enabled; /* An overall configuration for enabling/disabling all
119120
application logging features */
120-
bool log_forwarding_enabled; /* Whether log forwarding is enabled */
121+
bool log_forwarding_enabled; /* Whether log forwarding is enabled */
122+
int log_forwarding_log_level; /* minimum log level to forward to the collector
123+
*/
121124
size_t log_events_max_samples_stored; /* The maximum number of log events per
122125
transaction */
123126
bool log_metrics_enabled; /* Whether log metrics are enabled */
@@ -617,6 +620,12 @@ extern void nr_txn_record_custom_event(nrtxn_t* txn,
617620
*/
618621
extern bool nr_txn_log_forwarding_enabled(nrtxn_t* txn);
619622

623+
/*
624+
* Purpose : Check log forwarding log level configuration
625+
*/
626+
extern bool nr_txn_log_forwarding_log_level_verify(nrtxn_t* txn,
627+
const char* log_level_name);
628+
620629
/*
621630
* Purpose : Check logging metrics configuration
622631
*/

axiom/tests/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ test_json
6868
test_labels
6969
test_log_event
7070
test_log_events
71+
test_log_level
7172
test_logging
7273
test_logging_parallel
7374
test_math

0 commit comments

Comments
 (0)