1313from .lambda_context import LambdaContext
1414from .lambda_runtime_client import LambdaRuntimeClient
1515from .lambda_runtime_exception import FaultException
16+ from .lambda_runtime_log_utils import (
17+ _DATETIME_FORMAT ,
18+ _DEFAULT_FRAME_TYPE ,
19+ _JSON_FRAME_TYPES ,
20+ JsonFormatter ,
21+ LogFormat ,
22+ )
1623from .lambda_runtime_marshaller import to_json
1724
1825ERROR_LOG_LINE_TERMINATE = "\r "
1926ERROR_LOG_IDENT = "\u00a0 " # NO-BREAK SPACE U+00A0
27+ _AWS_LAMBDA_LOG_FORMAT = LogFormat .from_str (os .environ .get ("AWS_LAMBDA_LOG_FORMAT" ))
28+ _AWS_LAMBDA_LOG_LEVEL = os .environ .get ("AWS_LAMBDA_LOG_LEVEL" , "" ).upper ()
2029
2130
2231def _get_handler (handler ):
@@ -73,7 +82,12 @@ def result(*args):
7382 return result
7483
7584
76- def make_error (error_message , error_type , stack_trace , invoke_id = None ):
85+ def make_error (
86+ error_message ,
87+ error_type ,
88+ stack_trace ,
89+ invoke_id = None ,
90+ ):
7791 result = {
7892 "errorMessage" : error_message if error_message else "" ,
7993 "errorType" : error_type if error_type else "" ,
@@ -92,34 +106,52 @@ def replace_line_indentation(line, indent_char, new_indent_char):
92106 return (new_indent_char * ident_chars_count ) + line [ident_chars_count :]
93107
94108
95- def log_error (error_result , log_sink ):
96- error_description = "[ERROR]"
109+ if _AWS_LAMBDA_LOG_FORMAT == LogFormat .JSON :
110+ _ERROR_FRAME_TYPE = _JSON_FRAME_TYPES [logging .ERROR ]
111+
112+ def log_error (error_result , log_sink ):
113+ error_result = {
114+ "timestamp" : time .strftime (
115+ _DATETIME_FORMAT , logging .Formatter .converter (time .time ())
116+ ),
117+ "log_level" : "ERROR" ,
118+ ** error_result ,
119+ }
120+ log_sink .log_error (
121+ [to_json (error_result )],
122+ )
97123
98- error_result_type = error_result .get ("errorType" )
99- if error_result_type :
100- error_description += " " + error_result_type
124+ else :
125+ _ERROR_FRAME_TYPE = _DEFAULT_FRAME_TYPE
101126
102- error_result_message = error_result .get ("errorMessage" )
103- if error_result_message :
127+ def log_error (error_result , log_sink ):
128+ error_description = "[ERROR]"
129+
130+ error_result_type = error_result .get ("errorType" )
104131 if error_result_type :
105- error_description += ":"
106- error_description += " " + error_result_message
132+ error_description += " " + error_result_type
133+
134+ error_result_message = error_result .get ("errorMessage" )
135+ if error_result_message :
136+ if error_result_type :
137+ error_description += ":"
138+ error_description += " " + error_result_message
107139
108- error_message_lines = [error_description ]
140+ error_message_lines = [error_description ]
109141
110- stack_trace = error_result .get ("stackTrace" )
111- if stack_trace is not None :
112- error_message_lines += ["Traceback (most recent call last):" ]
113- for trace_element in stack_trace :
114- if trace_element == "" :
115- error_message_lines += ["" ]
116- else :
117- for trace_line in trace_element .splitlines ():
118- error_message_lines += [
119- replace_line_indentation (trace_line , " " , ERROR_LOG_IDENT )
120- ]
142+ stack_trace = error_result .get ("stackTrace" )
143+ if stack_trace is not None :
144+ error_message_lines += ["Traceback (most recent call last):" ]
145+ for trace_element in stack_trace :
146+ if trace_element == "" :
147+ error_message_lines += ["" ]
148+ else :
149+ for trace_line in trace_element .splitlines ():
150+ error_message_lines += [
151+ replace_line_indentation (trace_line , " " , ERROR_LOG_IDENT )
152+ ]
121153
122- log_sink .log_error (error_message_lines )
154+ log_sink .log_error (error_message_lines )
123155
124156
125157def handle_event_request (
@@ -152,7 +184,12 @@ def handle_event_request(
152184 )
153185 except FaultException as e :
154186 xray_fault = make_xray_fault ("LambdaValidationError" , e .msg , os .getcwd (), [])
155- error_result = make_error (e .msg , e .exception_type , e .trace , invoke_id )
187+ error_result = make_error (
188+ e .msg ,
189+ e .exception_type ,
190+ e .trace ,
191+ invoke_id ,
192+ )
156193
157194 except Exception :
158195 etype , value , tb = sys .exc_info ()
@@ -221,7 +258,9 @@ def build_fault_result(exc_info, msg):
221258 break
222259
223260 return make_error (
224- msg if msg else str (value ), etype .__name__ , traceback .format_list (tb_tuples )
261+ msg if msg else str (value ),
262+ etype .__name__ ,
263+ traceback .format_list (tb_tuples ),
225264 )
226265
227266
@@ -257,7 +296,8 @@ def __init__(self, log_sink):
257296
258297 def emit (self , record ):
259298 msg = self .format (record )
260- self .log_sink .log (msg )
299+
300+ self .log_sink .log (msg , frame_type = getattr (record , "_frame_type" , None ))
261301
262302
263303class LambdaLoggerFilter (logging .Filter ):
@@ -298,7 +338,7 @@ def __enter__(self):
298338 def __exit__ (self , exc_type , exc_value , exc_tb ):
299339 pass
300340
301- def log (self , msg ):
341+ def log (self , msg , frame_type = None ):
302342 sys .stdout .write (msg )
303343
304344 def log_error (self , message_lines ):
@@ -324,7 +364,6 @@ class FramedTelemetryLogSink(object):
324364
325365 def __init__ (self , fd ):
326366 self .fd = int (fd )
327- self .frame_type = 0xA55A0003 .to_bytes (4 , "big" )
328367
329368 def __enter__ (self ):
330369 self .file = os .fdopen (self .fd , "wb" , 0 )
@@ -333,11 +372,12 @@ def __enter__(self):
333372 def __exit__ (self , exc_type , exc_value , exc_tb ):
334373 self .file .close ()
335374
336- def log (self , msg ):
375+ def log (self , msg , frame_type = None ):
337376 encoded_msg = msg .encode ("utf8" )
377+
338378 timestamp = int (time .time_ns () / 1000 ) # UNIX timestamp in microseconds
339379 log_msg = (
340- self . frame_type
380+ ( frame_type or _DEFAULT_FRAME_TYPE )
341381 + len (encoded_msg ).to_bytes (4 , "big" )
342382 + timestamp .to_bytes (8 , "big" )
343383 + encoded_msg
@@ -346,7 +386,10 @@ def log(self, msg):
346386
347387 def log_error (self , message_lines ):
348388 error_message = "\n " .join (message_lines )
349- self .log (error_message )
389+ self .log (
390+ error_message ,
391+ frame_type = _ERROR_FRAME_TYPE ,
392+ )
350393
351394
352395def update_xray_env_variable (xray_trace_id ):
@@ -370,6 +413,28 @@ def create_log_sink():
370413_GLOBAL_AWS_REQUEST_ID = None
371414
372415
416+ def _setup_logging (log_format , log_level , log_sink ):
417+ logging .Formatter .converter = time .gmtime
418+ logger = logging .getLogger ()
419+ logger_handler = LambdaLoggerHandler (log_sink )
420+ if log_format == LogFormat .JSON :
421+ logger_handler .setFormatter (JsonFormatter ())
422+
423+ logging .addLevelName (logging .DEBUG , "TRACE" )
424+ if log_level in logging ._nameToLevel :
425+ logger .setLevel (log_level )
426+ else :
427+ logger_handler .setFormatter (
428+ logging .Formatter (
429+ "[%(levelname)s]\t %(asctime)s.%(msecs)03dZ\t %(aws_request_id)s\t %(message)s\n " ,
430+ "%Y-%m-%dT%H:%M:%S" ,
431+ )
432+ )
433+
434+ logger_handler .addFilter (LambdaLoggerFilter ())
435+ logger .addHandler (logger_handler )
436+
437+
373438def run (app_root , handler , lambda_runtime_api_addr ):
374439 sys .stdout = Unbuffered (sys .stdout )
375440 sys .stderr = Unbuffered (sys .stderr )
@@ -378,18 +443,7 @@ def run(app_root, handler, lambda_runtime_api_addr):
378443 lambda_runtime_client = LambdaRuntimeClient (lambda_runtime_api_addr )
379444
380445 try :
381- logging .Formatter .converter = time .gmtime
382- logger = logging .getLogger ()
383- logger_handler = LambdaLoggerHandler (log_sink )
384- logger_handler .setFormatter (
385- logging .Formatter (
386- "[%(levelname)s]\t %(asctime)s.%(msecs)03dZ\t %(aws_request_id)s\t %(message)s\n " ,
387- "%Y-%m-%dT%H:%M:%S" ,
388- )
389- )
390- logger_handler .addFilter (LambdaLoggerFilter ())
391- logger .addHandler (logger_handler )
392-
446+ _setup_logging (_AWS_LAMBDA_LOG_FORMAT , _AWS_LAMBDA_LOG_LEVEL , log_sink )
393447 global _GLOBAL_AWS_REQUEST_ID
394448
395449 request_handler = _get_handler (handler )
0 commit comments