Skip to content

Commit 8765d53

Browse files
author
Artem Krivonos
committed
move frame_type to JsonFormatter, change make_error, fix tests
1 parent 18a2163 commit 8765d53

File tree

4 files changed

+95
-136
lines changed

4 files changed

+95
-136
lines changed

awslambdaric/bootstrap.py

+56-74
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
JsonFormatter,
1818
_DATETIME_FORMAT,
1919
_JSON_FORMAT,
20-
_TEXT_FORMAT,
20+
_FRAME_TYPES,
21+
_DEFAULT_FRAME_TYPE,
2122
_get_log_format_from_str,
2223
)
2324
from .lambda_runtime_marshaller import to_json
2425

2526
ERROR_LOG_LINE_TERMINATE = "\r"
2627
ERROR_LOG_IDENT = "\u00a0" # NO-BREAK SPACE U+00A0
27-
RUNTIME_ERROR_LOGGER_NAME = "system"
2828
_AWS_LAMBDA_LOG_FORMAT = _get_log_format_from_str(
2929
os.environ.get("AWS_LAMBDA_LOG_FORMAT", "TEXT").upper()
3030
)
@@ -86,14 +86,12 @@ def result(*args):
8686

8787

8888
def make_error(
89-
error_message, error_type, stack_trace, invoke_id=None, level=logging.ERROR
89+
error_message,
90+
error_type,
91+
stack_trace,
92+
invoke_id=None,
9093
):
9194
result = {
92-
"timestamp": time.strftime(
93-
_DATETIME_FORMAT, logging.Formatter.converter(time.time())
94-
),
95-
"log_level": level,
96-
"logger": RUNTIME_ERROR_LOGGER_NAME,
9795
"errorMessage": error_message if error_message else "",
9896
"errorType": error_type if error_type else "",
9997
"requestId": invoke_id if invoke_id is not None else "",
@@ -111,51 +109,48 @@ def replace_line_indentation(line, indent_char, new_indent_char):
111109
return (new_indent_char * ident_chars_count) + line[ident_chars_count:]
112110

113111

114-
def log_error_json(error_result, log_sink):
115-
log_level = error_result.pop("log_level", logging.ERROR)
116-
error_result["level"] = logging.getLevelName(log_level)
117-
log_sink.log_error(
118-
[to_json(error_result, ensure_ascii=False)],
119-
log_level=log_level,
120-
)
112+
if _AWS_LAMBDA_LOG_FORMAT == _JSON_FORMAT:
113+
def log_error(error_result, log_sink):
114+
error_result = {
115+
"timestamp": time.strftime(
116+
_DATETIME_FORMAT, logging.Formatter.converter(time.time())
117+
),
118+
"log_level": "ERROR",
119+
**error_result,
120+
}
121+
log_sink.log_error(
122+
[to_json(error_result)],
123+
)
121124

125+
else:
126+
def log_error(error_result, log_sink):
127+
error_description = "[ERROR]"
122128

123-
def log_error_text(error_result, log_sink):
124-
error_description = "[ERROR]"
129+
error_result_type = error_result.get("errorType")
130+
if error_result_type:
131+
error_description += " " + error_result_type
125132

126-
error_result_type = error_result.get("errorType")
127-
if error_result_type:
128-
error_description += " " + error_result_type
133+
error_result_message = error_result.get("errorMessage")
134+
if error_result_message:
135+
if error_result_type:
136+
error_description += ":"
137+
error_description += " " + error_result_message
129138

130-
error_result_message = error_result.get("errorMessage")
131-
if error_result_message:
132-
if error_result_type:
133-
error_description += ":"
134-
error_description += " " + error_result_message
135-
136-
error_message_lines = [error_description]
137-
138-
stack_trace = error_result.get("stackTrace")
139-
if stack_trace is not None:
140-
error_message_lines += ["Traceback (most recent call last):"]
141-
for trace_element in stack_trace:
142-
if trace_element == "":
143-
error_message_lines += [""]
144-
else:
145-
for trace_line in trace_element.splitlines():
146-
error_message_lines += [
147-
replace_line_indentation(trace_line, " ", ERROR_LOG_IDENT)
148-
]
149-
150-
log_sink.log_error(
151-
error_message_lines, log_level=error_result.get("log_level", logging.ERROR)
152-
)
139+
error_message_lines = [error_description]
153140

141+
stack_trace = error_result.get("stackTrace")
142+
if stack_trace is not None:
143+
error_message_lines += ["Traceback (most recent call last):"]
144+
for trace_element in stack_trace:
145+
if trace_element == "":
146+
error_message_lines += [""]
147+
else:
148+
for trace_line in trace_element.splitlines():
149+
error_message_lines += [
150+
replace_line_indentation(trace_line, " ", ERROR_LOG_IDENT)
151+
]
154152

155-
if _AWS_LAMBDA_LOG_FORMAT == _JSON_FORMAT:
156-
log_error = log_error_json
157-
else:
158-
log_error = log_error_text
153+
log_sink.log_error(error_message_lines)
159154

160155

161156
def handle_event_request(
@@ -189,7 +184,10 @@ def handle_event_request(
189184
except FaultException as e:
190185
xray_fault = make_xray_fault("LambdaValidationError", e.msg, os.getcwd(), [])
191186
error_result = make_error(
192-
e.msg, e.exception_type, e.trace, invoke_id, level=logging.FATAL
187+
e.msg,
188+
e.exception_type,
189+
e.trace,
190+
invoke_id,
193191
)
194192

195193
except Exception:
@@ -262,7 +260,6 @@ def build_fault_result(exc_info, msg):
262260
msg if msg else str(value),
263261
etype.__name__,
264262
traceback.format_list(tb_tuples),
265-
level=logging.FATAL,
266263
)
267264

268265

@@ -300,9 +297,7 @@ def emit(self, record):
300297
msg = self.format(record)
301298

302299
self.log_sink.log(
303-
msg,
304-
log_level=record.levelno,
305-
log_format=getattr(record, "log_format", _TEXT_FORMAT),
300+
msg, frame_type=getattr(record, "_frame_type", _FRAME_TYPES.get(()))
306301
)
307302

308303

@@ -344,29 +339,15 @@ def __enter__(self):
344339
def __exit__(self, exc_type, exc_value, exc_tb):
345340
pass
346341

347-
def log(self, msg, log_level=None, log_format=None):
342+
def log(self, msg, frame_type=None):
348343
sys.stdout.write(msg)
349344

350-
def log_error(self, message_lines, log_level=logging.ERROR):
345+
def log_error(self, message_lines):
351346
error_message = ERROR_LOG_LINE_TERMINATE.join(message_lines) + "\n"
352347
sys.stdout.write(error_message)
353348

354349

355-
FRAME_TYPES = {
356-
(_JSON_FORMAT, logging.NOTSET): 0xA55A0002.to_bytes(4, "big"),
357-
(_JSON_FORMAT, logging.DEBUG): 0xA55A000A.to_bytes(4, "big"),
358-
(_JSON_FORMAT, logging.INFO): 0xA55A000E.to_bytes(4, "big"),
359-
(_JSON_FORMAT, logging.WARNING): 0xA55A0012.to_bytes(4, "big"),
360-
(_JSON_FORMAT, logging.ERROR): 0xA55A0016.to_bytes(4, "big"),
361-
(_JSON_FORMAT, logging.CRITICAL): 0xA55A001A.to_bytes(4, "big"),
362-
(_TEXT_FORMAT, logging.NOTSET): 0xA55A0003.to_bytes(4, "big"),
363-
(_TEXT_FORMAT, logging.DEBUG): 0xA55A000B.to_bytes(4, "big"),
364-
(_TEXT_FORMAT, logging.INFO): 0xA55A000F.to_bytes(4, "big"),
365-
(_TEXT_FORMAT, logging.WARNING): 0xA55A0013.to_bytes(4, "big"),
366-
(_TEXT_FORMAT, logging.ERROR): 0xA55A0017.to_bytes(4, "big"),
367-
(_TEXT_FORMAT, logging.CRITICAL): 0xA55A001B.to_bytes(4, "big"),
368-
}
369-
DEFAULT_FRAME_TYPE = 0xA55A0003.to_bytes(4, "big")
350+
_ERROR_FRAME_TYPE = _FRAME_TYPES[(_AWS_LAMBDA_LOG_FORMAT, logging.ERROR)]
370351

371352

372353
class FramedTelemetryLogSink(object):
@@ -395,8 +376,7 @@ def __enter__(self):
395376
def __exit__(self, exc_type, exc_value, exc_tb):
396377
self.file.close()
397378

398-
def log(self, msg, log_level=logging.NOTSET, log_format: int = _TEXT_FORMAT):
399-
frame_type = FRAME_TYPES.get((log_format, log_level), DEFAULT_FRAME_TYPE)
379+
def log(self, msg, frame_type=_DEFAULT_FRAME_TYPE):
400380
encoded_msg = msg.encode("utf8")
401381

402382
timestamp = int(time.time_ns() / 1000) # UNIX timestamp in microseconds
@@ -408,9 +388,12 @@ def log(self, msg, log_level=logging.NOTSET, log_format: int = _TEXT_FORMAT):
408388
)
409389
self.file.write(log_msg)
410390

411-
def log_error(self, message_lines, log_level=logging.ERROR):
391+
def log_error(self, message_lines):
412392
error_message = "\n".join(message_lines)
413-
self.log(error_message, log_level=log_level)
393+
self.log(
394+
error_message,
395+
frame_type=_ERROR_FRAME_TYPE,
396+
)
414397

415398

416399
def update_xray_env_variable(xray_trace_id):
@@ -435,7 +418,6 @@ def create_log_sink():
435418

436419

437420
def setup_logging(log_format, log_level, log_sink):
438-
logging._levelToName[logging.CRITICAL] = "FATAL"
439421
logging.Formatter.converter = time.gmtime
440422
logger = logging.getLogger()
441423
logger_handler = LambdaLoggerHandler(log_sink)

awslambdaric/lambda_runtime_log_utils.py

+27-7
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
import logging
66
import traceback
7-
8-
from .lambda_runtime_marshaller import to_json
7+
import json
98

109
_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
1110
_RESERVED_FIELDS = {
@@ -30,10 +29,28 @@
3029
"processName",
3130
"process",
3231
"aws_request_id",
33-
"log_format",
32+
"_frame_type",
3433
}
3534
_JSON_FORMAT = 0b0
3635
_TEXT_FORMAT = 0b1
36+
_FRAME_TYPES = {
37+
(_JSON_FORMAT, logging.NOTSET): 0xA55A0002.to_bytes(4, "big"),
38+
(_JSON_FORMAT, logging.DEBUG): 0xA55A000A.to_bytes(4, "big"),
39+
(_JSON_FORMAT, logging.INFO): 0xA55A000E.to_bytes(4, "big"),
40+
(_JSON_FORMAT, logging.WARNING): 0xA55A0012.to_bytes(4, "big"),
41+
(_JSON_FORMAT, logging.ERROR): 0xA55A0016.to_bytes(4, "big"),
42+
(_JSON_FORMAT, logging.CRITICAL): 0xA55A001A.to_bytes(4, "big"),
43+
(_TEXT_FORMAT, logging.NOTSET): 0xA55A0003.to_bytes(4, "big"),
44+
(_TEXT_FORMAT, logging.DEBUG): 0xA55A000B.to_bytes(4, "big"),
45+
(_TEXT_FORMAT, logging.INFO): 0xA55A000F.to_bytes(4, "big"),
46+
(_TEXT_FORMAT, logging.WARNING): 0xA55A0013.to_bytes(4, "big"),
47+
(_TEXT_FORMAT, logging.ERROR): 0xA55A0017.to_bytes(4, "big"),
48+
(_TEXT_FORMAT, logging.CRITICAL): 0xA55A001B.to_bytes(4, "big"),
49+
}
50+
_DEFAULT_FRAME_TYPE = _FRAME_TYPES[(_TEXT_FORMAT, logging.NOTSET)]
51+
52+
53+
_encode_json = json.JSONEncoder(ensure_ascii=False).encode
3754

3855

3956
def _get_log_format_from_str(value: str):
@@ -75,12 +92,15 @@ def format_location(record: logging.LogRecord):
7592

7693
@staticmethod
7794
def format_log_level(record: logging.LogRecord):
78-
level = min(50, max(0, record.levelno)) // 10 * 10
79-
record.levelname = logging.getLevelName(level)
95+
record.levelno = min(50, max(0, record.levelno)) // 10 * 10
96+
record.levelname = logging.getLevelName(record.levelno)
8097

8198
def format(self, record: logging.LogRecord) -> str:
8299
self.format_log_level(record)
83-
record.log_format = _JSON_FORMAT
100+
record._frame_type = _FRAME_TYPES.get(
101+
(_JSON_FORMAT, record.levelno),
102+
_FRAME_TYPES[(_JSON_FORMAT, logging.NOTSET)],
103+
)
84104

85105
result = {
86106
"timestamp": self.formatTime(record, self.datefmt),
@@ -101,4 +121,4 @@ def format(self, record: logging.LogRecord) -> str:
101121

102122
result = {k: v for k, v in result.items() if v is not None}
103123

104-
return to_json(result, ensure_ascii=False) + "\n"
124+
return _encode_json(result) + "\n"

awslambdaric/lambda_runtime_marshaller.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ def default(self, obj):
2424
return super().default(obj)
2525

2626

27-
def to_json(obj, ensure_ascii=True):
28-
return Encoder(ensure_ascii=ensure_ascii).encode(obj)
27+
def to_json(obj):
28+
return Encoder().encode(obj)
2929

3030

3131
class LambdaMarshaller:

0 commit comments

Comments
 (0)