Skip to content

Commit 6dcfea1

Browse files
author
Artem Krivonos
committed
add AWS_LAMBDA_LOG_LEVEL, return stack trace as a list, allow any value in extras
1 parent ca438f8 commit 6dcfea1

File tree

3 files changed

+45
-10
lines changed

3 files changed

+45
-10
lines changed

awslambdaric/bootstrap.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,16 +369,18 @@ def create_log_sink():
369369

370370

371371
AWS_LAMBDA_LOG_FORMAT = os.environ.get("AWS_LAMBDA_LOG_FORMAT", "TEXT")
372+
AWS_LAMBDA_LOG_LEVEL = os.environ.get("AWS_LAMBDA_LOG_LEVEL", "INFO")
372373
_GLOBAL_AWS_REQUEST_ID = None
373374

374375

375-
def setup_logging(log_format, log_sink):
376+
def setup_logging(log_format, log_level, log_sink):
376377
logging.Formatter.converter = time.gmtime
377378
logger = logging.getLogger()
378379
logger_handler = LambdaLoggerHandler(log_sink)
379380
if log_format == "JSON":
380381
logger_handler.setFormatter(JsonFormatter())
381382
logging._levelToName[logging.CRITICAL] = "FATAL"
383+
logger.setLevel(log_level)
382384
else:
383385
logger_handler.setFormatter(
384386
logging.Formatter(
@@ -399,7 +401,7 @@ def run(app_root, handler, lambda_runtime_api_addr):
399401
lambda_runtime_client = LambdaRuntimeClient(lambda_runtime_api_addr)
400402

401403
try:
402-
setup_logging(AWS_LAMBDA_LOG_FORMAT, log_sink)
404+
setup_logging(AWS_LAMBDA_LOG_FORMAT, AWS_LAMBDA_LOG_LEVEL, log_sink)
403405
global _GLOBAL_AWS_REQUEST_ID
404406

405407
request_handler = _get_handler(handler)

awslambdaric/lambda_runtime_log_utils.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import logging
6+
import traceback
67

78
from .lambda_runtime_marshaller import to_json
89

@@ -35,10 +36,11 @@ class JsonFormatter(logging.Formatter):
3536
def __init__(self):
3637
super().__init__(datefmt="%Y-%m-%dT%H:%M:%SZ", validate=False)
3738

38-
def format_stacktrace(self, exc_info):
39+
@staticmethod
40+
def format_stacktrace(exc_info):
3941
if not exc_info:
4042
return None
41-
return self.formatException(exc_info)
43+
return traceback.format_tb(exc_info[2])
4244

4345
@staticmethod
4446
def format_exception_name(exc_info):
@@ -90,9 +92,9 @@ def format(self, record: logging.LogRecord) -> str:
9092
INVOCATION_LOGGING_CONTEXT = dict()
9193

9294

93-
def lambda_instance_logs_update(key: str, value: str):
94-
INSTANCE_LOGGING_CONTEXT[str(key)] = str(value)
95+
def lambda_instance_logs_update(key: str, value):
96+
INSTANCE_LOGGING_CONTEXT[str(key)] = value
9597

9698

97-
def lambda_invocation_logs_update(key: str, value: str):
98-
INVOCATION_LOGGING_CONTEXT[str(key)] = str(value)
99+
def lambda_invocation_logs_update(key: str, value):
100+
INVOCATION_LOGGING_CONTEXT[str(key)] = value

tests/test_bootstrap.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1184,7 +1184,7 @@ def test_multiple_frame(self):
11841184
class TestLogging(unittest.TestCase):
11851185
@classmethod
11861186
def setUpClass(cls) -> None:
1187-
bootstrap.setup_logging("JSON", bootstrap.StandardLogSink())
1187+
bootstrap.setup_logging("JSON", "INFO", bootstrap.StandardLogSink())
11881188

11891189
def tearDown(self) -> None:
11901190
INVOCATION_LOGGING_CONTEXT.clear()
@@ -1252,7 +1252,9 @@ def test_exception(self, mock_stderr, mock_stdout):
12521252
self.assertIn("stackTrace", exception_log)
12531253
exception_log.pop("timestamp")
12541254
exception_log.pop("location")
1255-
exception_log.pop("stackTrace")
1255+
stack_trace = exception_log.pop("stackTrace")
1256+
1257+
self.assertEqual(len(stack_trace), 1)
12561258

12571259
self.assertEqual(
12581260
exception_log,
@@ -1273,6 +1275,9 @@ def test_exception(self, mock_stderr, mock_stdout):
12731275
def test_log_with_extra_params(self, mock_stderr, mock_stdout):
12741276
lambda_instance_logs_update("instance_key", "instance_value")
12751277
lambda_invocation_logs_update("invocation_key", "invocation_value")
1278+
lambda_invocation_logs_update("int_param", 42)
1279+
lambda_invocation_logs_update("list_param", ["1", 2, {}])
1280+
lambda_invocation_logs_update("dict_param", {"a": "b"})
12761281

12771282
logging.getLogger("test.logger").error("test extra params")
12781283

@@ -1284,6 +1289,9 @@ def test_log_with_extra_params(self, mock_stderr, mock_stdout):
12841289
{
12851290
"instance_key": "instance_value",
12861291
"invocation_key": "invocation_value",
1292+
"int_param": 42,
1293+
"list_param": ["1", 2, {}],
1294+
"dict_param": {"a": "b"},
12871295
"level": "ERROR",
12881296
"logger": "test.logger",
12891297
"message": "test extra params",
@@ -1292,6 +1300,29 @@ def test_log_with_extra_params(self, mock_stderr, mock_stdout):
12921300
)
12931301
self.assertEqual(mock_stderr.getvalue(), "")
12941302

1303+
@patch("sys.stdout", new_callable=StringIO)
1304+
@patch("sys.stderr", new_callable=StringIO)
1305+
def test_log_level(self, mock_stderr, mock_stdout):
1306+
logger = logging.getLogger("test.logger")
1307+
1308+
logger.debug("debug message")
1309+
logger.info("info message")
1310+
1311+
# print("some ", mock_stdout.getvalue())
1312+
data = json.loads(mock_stdout.getvalue())
1313+
data.pop("timestamp")
1314+
1315+
self.assertEqual(
1316+
data,
1317+
{
1318+
"level": "INFO",
1319+
"logger": "test.logger",
1320+
"message": "info message",
1321+
"requestId": "",
1322+
},
1323+
)
1324+
self.assertEqual(mock_stderr.getvalue(), "")
1325+
12951326

12961327
class TestBootstrapModule(unittest.TestCase):
12971328
@patch("awslambdaric.bootstrap.handle_event_request")

0 commit comments

Comments
 (0)