Skip to content

Commit 2656a3b

Browse files
TimPansinolrafeeiumaannamalai
authored
Log Handler Cleanup (#500)
* Log handler cleaned up Co-authored-by: Lalleh Rafeei <[email protected]> Co-authored-by: Uma Annamalai <[email protected]> * Last clean pass Co-authored-by: Lalleh Rafeei <[email protected]> Co-authored-by: Uma Annamalai <[email protected]>
1 parent e6354b5 commit 2656a3b

File tree

1 file changed

+66
-69
lines changed

1 file changed

+66
-69
lines changed

newrelic/api/log.py

Lines changed: 66 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import re
1516
import json
1617
import logging
17-
import time
1818

19-
from urllib.request import urlopen, Request
2019
from logging import Formatter, LogRecord
2120

21+
from newrelic.core.attribute import truncate
2222
from newrelic.api.time_trace import get_linking_metadata
23+
from newrelic.api.transaction import current_transaction
2324
from newrelic.common.object_names import parse_exc_info
24-
from newrelic.core.config import is_expected_error
25+
from newrelic.common import agent_http
26+
from newrelic.core.config import global_settings, is_expected_error
2527

2628

2729
def format_exc_info(exc_info):
@@ -85,79 +87,74 @@ def safe_str(object, *args, **kwargs):
8587

8688

8789
class NewRelicLogHandler(logging.Handler):
88-
"""
89-
Implementation was derived from: https://pypi.org/project/new-relic-logger-for-python/0.2.0/
90-
file: newrelic_logger.handlers.py
91-
A class which sends records to a New Relic via its API.
92-
"""
93-
94-
def __init__(self, level=logging.INFO, app_id=0, app_name=None, license_key=None, region="US", ):
95-
"""
96-
Initialize the instance with the region and license_key
97-
"""
90+
"""This is an experimental log handler provided by the community. Use with caution."""
91+
92+
PATH="/log/v1"
93+
94+
def __init__(
95+
self,
96+
level=logging.INFO,
97+
license_key=None,
98+
host=None,
99+
port=443,
100+
proxy_scheme=None,
101+
proxy_host=None,
102+
proxy_user=None,
103+
proxy_pass=None,
104+
timeout=None,
105+
ca_bundle_path=None,
106+
disable_certificate_validation=False,
107+
):
98108
super(NewRelicLogHandler, self).__init__(level=level)
99-
self.app_id = app_id
100-
self.app_name = app_name
101-
self.host_us = "log-api.newrelic.com"
102-
self.host_eu = "log-api.eu.newrelic.com"
103-
self.url = "/log/v1"
104-
self.region = region.upper()
105-
self.license_key = license_key
106-
self.setFormatter(NewRelicContextFormatter())
109+
self.license_key = license_key or self.settings.license_key
110+
self.host = host or self.settings.host or self.default_host(self.license_key)
111+
112+
self.client = agent_http.HttpClient(
113+
host=host,
114+
port=port,
115+
proxy_scheme=proxy_scheme,
116+
proxy_host=proxy_host,
117+
proxy_user=proxy_user,
118+
proxy_pass=proxy_pass,
119+
timeout=timeout,
120+
ca_bundle_path=ca_bundle_path,
121+
disable_certificate_validation=disable_certificate_validation,
122+
)
107123

108-
def prepare(self, record):
109-
self.format(record)
124+
self.setFormatter(NewRelicContextFormatter())
110125

111-
record.msg = record.message
112-
record.args = get_linking_metadata()
113-
record.exc_info = None
114-
return record
126+
@property
127+
def settings(self):
128+
transaction = current_transaction()
129+
if transaction:
130+
return transaction.settings
131+
return global_settings()
115132

116133
def emit(self, record):
117-
"""
118-
Emit a record.
119-
Send the record to the New Relic API
120-
"""
121134
try:
122-
record = self.prepare(record)
123-
print(record.getMessage())
124-
data_formatted_dict = json.loads(self.format(record))
125-
126-
data = {
127-
**data_formatted_dict,
128-
"appId": self.app_id,
129-
"labels": {"app": self.app_name},
130-
**record.args,
131-
}
132-
self.send_log(data=data)
135+
headers = {"Api-Key": self.license_key or "", "Content-Type": "application/json"}
136+
payload = self.format(record).encode("utf-8")
137+
with self.client:
138+
status_code, response = self.client.send_request(path=self.PATH, headers=headers, payload=payload)
139+
if status_code < 200 or status_code >= 300:
140+
raise RuntimeError(
141+
"An unexpected HTTP response of %r was received for request made to https://%s:%d%s."
142+
"The response payload for the request was %r. If this issue persists then please "
143+
"report this problem to New Relic support for further investigation."
144+
% (status_code, self.client._host, self.client._port, self.PATH, truncate(response.decode("utf-8"), maxlen=1024))
145+
)
133146

134147
except Exception:
135148
self.handleError(record)
136149

137-
def send_log(self, data: {}):
138-
host = self.host_us if self.region == "US" else self.host_eu
139-
req = Request(
140-
url="https://" + host + self.url,
141-
data=json.dumps(data).encode(),
142-
headers={
143-
'X-License-Key': self.license_key,
144-
'Content-Type': "application/json",
145-
},
146-
method="POST"
147-
)
148-
# this line helps to forward logs to newrelic logs api. I made sure to use a python standard lib
149-
# see https://docs.newrelic.com/docs/logs/log-api/introduction-log-api
150-
resp = urlopen(req) # nosec
151-
152-
if resp.status // 100 != 2:
153-
if resp.status == 429:
154-
print("New Relic API Response: Retry-After")
155-
time.sleep(1)
156-
self.send_log(data=data)
157-
return
158-
print("Error sending log to new relic")
159-
print("Status Code: {}".format(resp.status))
160-
print("Reason: {}".format(resp.reason))
161-
print("url: {}".format(resp.url))
162-
print(resp.read().decode())
163-
print("data: {}".format(data))
150+
def default_host(self, license_key):
151+
if not license_key:
152+
return "log-api.newrelic.com"
153+
154+
region_aware_match = re.match("^(.+?)x", license_key)
155+
if not region_aware_match:
156+
return "log-api.newrelic.com"
157+
158+
region = region_aware_match.group(1)
159+
host = "log-api." + region + ".newrelic.com"
160+
return host

0 commit comments

Comments
 (0)