13
13
from .lambda_context import LambdaContext
14
14
from .lambda_runtime_client import LambdaRuntimeClient
15
15
from .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
+ )
16
23
from .lambda_runtime_marshaller import to_json
17
24
18
25
ERROR_LOG_LINE_TERMINATE = "\r "
19
26
ERROR_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 ()
20
29
21
30
22
31
def _get_handler (handler ):
@@ -73,7 +82,12 @@ def result(*args):
73
82
return result
74
83
75
84
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
+ ):
77
91
result = {
78
92
"errorMessage" : error_message if error_message else "" ,
79
93
"errorType" : error_type if error_type else "" ,
@@ -92,34 +106,52 @@ def replace_line_indentation(line, indent_char, new_indent_char):
92
106
return (new_indent_char * ident_chars_count ) + line [ident_chars_count :]
93
107
94
108
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
+ )
97
123
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
101
126
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" )
104
131
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
107
139
108
- error_message_lines = [error_description ]
140
+ error_message_lines = [error_description ]
109
141
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
+ ]
121
153
122
- log_sink .log_error (error_message_lines )
154
+ log_sink .log_error (error_message_lines )
123
155
124
156
125
157
def handle_event_request (
@@ -152,7 +184,12 @@ def handle_event_request(
152
184
)
153
185
except FaultException as e :
154
186
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
+ )
156
193
157
194
except Exception :
158
195
etype , value , tb = sys .exc_info ()
@@ -221,7 +258,9 @@ def build_fault_result(exc_info, msg):
221
258
break
222
259
223
260
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 ),
225
264
)
226
265
227
266
@@ -257,7 +296,8 @@ def __init__(self, log_sink):
257
296
258
297
def emit (self , record ):
259
298
msg = self .format (record )
260
- self .log_sink .log (msg )
299
+
300
+ self .log_sink .log (msg , frame_type = getattr (record , "_frame_type" , None ))
261
301
262
302
263
303
class LambdaLoggerFilter (logging .Filter ):
@@ -298,7 +338,7 @@ def __enter__(self):
298
338
def __exit__ (self , exc_type , exc_value , exc_tb ):
299
339
pass
300
340
301
- def log (self , msg ):
341
+ def log (self , msg , frame_type = None ):
302
342
sys .stdout .write (msg )
303
343
304
344
def log_error (self , message_lines ):
@@ -324,7 +364,6 @@ class FramedTelemetryLogSink(object):
324
364
325
365
def __init__ (self , fd ):
326
366
self .fd = int (fd )
327
- self .frame_type = 0xA55A0003 .to_bytes (4 , "big" )
328
367
329
368
def __enter__ (self ):
330
369
self .file = os .fdopen (self .fd , "wb" , 0 )
@@ -333,11 +372,12 @@ def __enter__(self):
333
372
def __exit__ (self , exc_type , exc_value , exc_tb ):
334
373
self .file .close ()
335
374
336
- def log (self , msg ):
375
+ def log (self , msg , frame_type = None ):
337
376
encoded_msg = msg .encode ("utf8" )
377
+
338
378
timestamp = int (time .time_ns () / 1000 ) # UNIX timestamp in microseconds
339
379
log_msg = (
340
- self . frame_type
380
+ ( frame_type or _DEFAULT_FRAME_TYPE )
341
381
+ len (encoded_msg ).to_bytes (4 , "big" )
342
382
+ timestamp .to_bytes (8 , "big" )
343
383
+ encoded_msg
@@ -346,7 +386,10 @@ def log(self, msg):
346
386
347
387
def log_error (self , message_lines ):
348
388
error_message = "\n " .join (message_lines )
349
- self .log (error_message )
389
+ self .log (
390
+ error_message ,
391
+ frame_type = _ERROR_FRAME_TYPE ,
392
+ )
350
393
351
394
352
395
def update_xray_env_variable (xray_trace_id ):
@@ -370,6 +413,28 @@ def create_log_sink():
370
413
_GLOBAL_AWS_REQUEST_ID = None
371
414
372
415
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
+
373
438
def run (app_root , handler , lambda_runtime_api_addr ):
374
439
sys .stdout = Unbuffered (sys .stdout )
375
440
sys .stderr = Unbuffered (sys .stderr )
@@ -378,18 +443,7 @@ def run(app_root, handler, lambda_runtime_api_addr):
378
443
lambda_runtime_client = LambdaRuntimeClient (lambda_runtime_api_addr )
379
444
380
445
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 )
393
447
global _GLOBAL_AWS_REQUEST_ID
394
448
395
449
request_handler = _get_handler (handler )
0 commit comments