Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit 2c837aa
Author: Tim Pansino <[email protected]>
Date:   Wed Dec 20 14:13:24 2023 -0800

    Apply suggestions from code review

commit 81d1f44
Author: Tim Pansino <[email protected]>
Date:   Fri Dec 15 11:01:49 2023 -0800

    Linting

commit 8e1f04d
Author: Tim Pansino <[email protected]>
Date:   Fri Dec 15 10:28:17 2023 -0800

    Add attribute filtering for log events

commit bd6c973
Author: Tim Pansino <[email protected]>
Date:   Thu Dec 14 13:50:27 2023 -0800

    Add settings for context data filtering

commit 9441a8d
Author: Tim Pansino <[email protected]>
Date:   Thu Dec 14 13:50:11 2023 -0800

    Expand validator for log events

commit 364fd7b
Merge: 1d43944 1575a24
Author: Tim Pansino <[email protected]>
Date:   Fri Dec 15 13:07:55 2023 -0800

    Merge branch 'main' into develop-logging-attributes

commit 1575a24
Author: Lalleh Rafeei <[email protected]>
Date:   Wed Dec 13 10:56:16 2023 -0800

    Update flaskrestx testing (#1004)

    * Update flaskrestx testing

    * Update tastypie testing

    * Reformat tox

    * Fix tox typo

    ---------

    Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>

commit cc4c4e1
Author: Lalleh Rafeei <[email protected]>
Date:   Wed Dec 13 09:26:02 2023 -0800

    Patch sentinel bug (#997)

    Co-authored-by: Timothy Pansino <[email protected]>
    Co-authored-by: Hannah Stepanek <[email protected]>
    Co-authored-by: Uma Annamalai <[email protected]>

commit 1d43944
Author: Timothy Pansino <[email protected]>
Date:   Thu Nov 17 12:04:32 2022 -0800

    Log Forwarding User Attributes (#682)

    * Add context data setting

    Co-authored-by: Uma Annamalai <[email protected]>
    Co-authored-by: Hannah Stepanek <[email protected]>
    Co-authored-by: Lalleh Rafeei <[email protected]>

    * Update record_log_event signature with attributes

    * Logging attributes initial implementation

    * Fix settings attribute error

    * Update logging instrumentation with attributes

    * Update log handler API

    * Add loguru support for extra attrs

    * Add more explicit messaging to validator

    * Expanding testing for record_log_event

    * Expand record log event testing

    * Fix settings typo

    * Remove missing loguru attributes from test

    * Adjust safe log attr encoding

    * Correct py2 issues

    * Fix missing record attrs in logging.

    Co-authored-by: Uma Annamalai <[email protected]>
    Co-authored-by: Hannah Stepanek <[email protected]>
    Co-authored-by: Lalleh Rafeei <[email protected]>
    Co-authored-by: Hannah Stepanek <[email protected]>
  • Loading branch information
TimPansino committed Dec 20, 2023
1 parent b22500c commit 203d21e
Show file tree
Hide file tree
Showing 28 changed files with 910 additions and 284 deletions.
7 changes: 4 additions & 3 deletions newrelic/api/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@


class Application(object):

_lock = threading.Lock()
_instances = {}

Expand Down Expand Up @@ -162,9 +161,11 @@ def record_transaction(self, data):
if self.active:
self._agent.record_transaction(self._name, data)

def record_log_event(self, message, level=None, timestamp=None, priority=None):
def record_log_event(self, message, level=None, timestamp=None, attributes=None, priority=None):
if self.active:
self._agent.record_log_event(self._name, message, level, timestamp, priority=priority)
self._agent.record_log_event(
self._name, message, level, timestamp, attributes=attributes, priority=priority
)

def normalize_name(self, name, rule_type="url"):
if self.active:
Expand Down
31 changes: 19 additions & 12 deletions newrelic/api/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
from newrelic.api.time_trace import get_linking_metadata
from newrelic.api.transaction import current_transaction, record_log_event
from newrelic.common import agent_http
from newrelic.common.encoding_utils import safe_json_encode
from newrelic.common.object_names import parse_exc_info
from newrelic.core.attribute import truncate
from newrelic.core.config import global_settings, is_expected_error
from newrelic.packages import six


def format_exc_info(exc_info):
Expand All @@ -43,7 +45,7 @@ def format_exc_info(exc_info):


class NewRelicContextFormatter(Formatter):
DEFAULT_LOG_RECORD_KEYS = frozenset(vars(LogRecord("", 0, "", 0, "", (), None)))
DEFAULT_LOG_RECORD_KEYS = frozenset(set(vars(LogRecord("", 0, "", 0, "", (), None))) | {"message"})

def __init__(self, *args, **kwargs):
super(NewRelicContextFormatter, self).__init__()
Expand Down Expand Up @@ -76,17 +78,12 @@ def log_record_to_dict(cls, record):
return output

def format(self, record):
def safe_str(object, *args, **kwargs):
"""Convert object to str, catching any errors raised."""
try:
return str(object, *args, **kwargs)
except:
return "<unprintable %s object>" % type(object).__name__

return json.dumps(self.log_record_to_dict(record), default=safe_str, separators=(",", ":"))
return json.dumps(self.log_record_to_dict(record), default=safe_json_encode, separators=(",", ":"))


class NewRelicLogForwardingHandler(logging.Handler):
DEFAULT_LOG_RECORD_KEYS = frozenset(set(vars(LogRecord("", 0, "", 0, "", (), None))) | {"message"})

def emit(self, record):
try:
# Avoid getting local log decorated message
Expand All @@ -95,10 +92,20 @@ def emit(self, record):
else:
message = record.getMessage()

record_log_event(message, record.levelname, int(record.created * 1000))
attrs = self.filter_record_attributes(record)
record_log_event(message, record.levelname, int(record.created * 1000), attributes=attrs)
except Exception:
self.handleError(record)

@classmethod
def filter_record_attributes(cls, record):
record_attrs = vars(record)
DEFAULT_LOG_RECORD_KEYS = cls.DEFAULT_LOG_RECORD_KEYS
if len(record_attrs) > len(DEFAULT_LOG_RECORD_KEYS):
return {k: v for k, v in six.iteritems(vars(record)) if k not in DEFAULT_LOG_RECORD_KEYS}
else:
return None


class NewRelicLogHandler(logging.Handler):
"""
Expand Down Expand Up @@ -126,8 +133,8 @@ def __init__(
"The contributed NewRelicLogHandler has been superseded by automatic instrumentation for "
"logging in the standard lib. If for some reason you need to manually configure a handler, "
"please use newrelic.api.log.NewRelicLogForwardingHandler to take advantage of all the "
"features included in application log forwarding such as proper batching.",
DeprecationWarning
"features included in application log forwarding such as proper batching.",
DeprecationWarning,
)
super(NewRelicLogHandler, self).__init__(level=level)
self.license_key = license_key or self.settings.license_key
Expand Down
22 changes: 15 additions & 7 deletions newrelic/api/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
json_decode,
json_encode,
obfuscate,
safe_json_encode,
snake_case,
)
from newrelic.core.attribute import (
Expand All @@ -54,6 +55,7 @@
create_attributes,
create_user_attributes,
process_user_attribute,
resolve_logging_context_attributes,
truncate,
)
from newrelic.core.attribute_filter import (
Expand Down Expand Up @@ -309,7 +311,7 @@ def __init__(self, application, enabled=None, source=None):
self.synthetics_job_id = None
self.synthetics_monitor_id = None
self.synthetics_header = None

# Synthetics Info Header
self.synthetics_type = None
self.synthetics_initiator = None
Expand Down Expand Up @@ -1524,7 +1526,7 @@ def set_transaction_name(self, name, group=None, priority=None):
self._group = group
self._name = name

def record_log_event(self, message, level=None, timestamp=None, priority=None):
def record_log_event(self, message, level=None, timestamp=None, attributes=None, priority=None):
settings = self.settings
if not (
settings
Expand Down Expand Up @@ -1554,11 +1556,17 @@ def record_log_event(self, message, level=None, timestamp=None, priority=None):
else:
message = message

collected_attributes = get_linking_metadata()
if attributes and (settings and settings.application_logging.forwarding.context_data.enabled):
context_attributes = resolve_logging_context_attributes(attributes, settings.attribute_filter, "context.")
if context_attributes:
collected_attributes.update(context_attributes)

event = LogEventNode(
timestamp=timestamp,
level=level,
message=message,
attributes=get_linking_metadata(),
attributes=collected_attributes,
)

self._log_events.add(event, priority=priority)
Expand Down Expand Up @@ -2071,7 +2079,7 @@ def record_ml_event(event_type, params, application=None):
application.record_ml_event(event_type, params)


def record_log_event(message, level=None, timestamp=None, application=None, priority=None):
def record_log_event(message, level=None, timestamp=None, attributes=None, application=None, priority=None):
"""Record a log event.
Args:
Expand All @@ -2082,12 +2090,12 @@ def record_log_event(message, level=None, timestamp=None, application=None, prio
if application is None:
transaction = current_transaction()
if transaction:
transaction.record_log_event(message, level, timestamp)
transaction.record_log_event(message, level, timestamp, attributes=attributes)
else:
application = application_instance(activate=False)

if application and application.enabled:
application.record_log_event(message, level, timestamp, priority=priority)
application.record_log_event(message, level, timestamp, attributes=attributes, priority=priority)
else:
_logger.debug(
"record_log_event has been called but no transaction or application was running. As a result, "
Expand All @@ -2098,7 +2106,7 @@ def record_log_event(message, level=None, timestamp=None, application=None, prio
timestamp,
)
elif application.enabled:
application.record_log_event(message, level, timestamp, priority=priority)
application.record_log_event(message, level, timestamp, attributes=attributes, priority=priority)


def accept_distributed_trace_payload(payload, transport_type="HTTP"):
Expand Down
Loading

0 comments on commit 203d21e

Please sign in to comment.