@@ -1396,6 +1396,107 @@ def test_logger_log_accepts_model_dump_metadata(with_memory_logger):
13961396 assert logs [0 ]["metadata" ] == {"foo" : "bar" }
13971397
13981398
1399+ def test_logger_emit_log_without_active_span (with_memory_logger ):
1400+ test_logger = init_test_logger (__name__ )
1401+
1402+ first_id = test_logger .emit_log (
1403+ body = "Payment failed" ,
1404+ level = "error" ,
1405+ metadata = {"payment_id" : "pay_123" },
1406+ )
1407+ second_id = test_logger .emit_log (body = "Retrying payment" , level = "info" )
1408+
1409+ logs = with_memory_logger .pop ()
1410+ assert len (logs ) == 2
1411+ first , second = logs
1412+ assert first_id == first ["id" ]
1413+ assert second_id == second ["id" ]
1414+ assert first ["id" ] != second ["id" ]
1415+ assert first ["span_id" ] != second ["span_id" ]
1416+ assert first ["root_span_id" ] == second ["root_span_id" ]
1417+ assert not first .get ("span_parents" )
1418+ assert first ["output" ] == "Payment failed"
1419+ assert first ["error" ] == "Payment failed"
1420+ assert first ["metadata" ] == {"payment_id" : "pay_123" }
1421+ assert first ["span_attributes" ]["name" ] == "Log"
1422+ assert first ["span_attributes" ]["type" ] == "log"
1423+ assert first ["metrics" ]["start" ] == first ["metrics" ]["end" ]
1424+ assert first ["context" ]["otel" ]["signal" ] == "logs"
1425+ assert first ["context" ]["otel" ]["log" ] == {
1426+ "time_unix_nano" : str (round (first ["metrics" ]["start" ] * 1_000_000_000 )),
1427+ "severity_number" : 17 ,
1428+ "severity_text" : "ERROR" ,
1429+ }
1430+ assert "error" not in second
1431+ assert second ["context" ]["otel" ]["log" ]["severity_number" ] == 9
1432+
1433+
1434+ def test_logger_emit_log_uses_distinct_baseline_trace_per_logger (with_memory_logger ):
1435+ first_logger = init_test_logger (f"{ __name__ } -first" )
1436+ second_logger = init_test_logger (f"{ __name__ } -second" )
1437+
1438+ first_logger .info ("first" )
1439+ second_logger .info ("second" )
1440+
1441+ first , second = with_memory_logger .pop ()
1442+ assert first ["root_span_id" ] != second ["root_span_id" ]
1443+
1444+
1445+ def test_logger_emit_log_uses_active_span (with_memory_logger ):
1446+ test_logger = init_test_logger (__name__ )
1447+
1448+ with test_logger .start_span (name = "owner" ) as owner :
1449+ log_id = test_logger .emit_log (body = "Inside span" , level = "debug" , metadata = {"attempt" : 1 })
1450+
1451+ rows = with_memory_logger .pop ()
1452+ log_row = next (row for row in rows if row ["id" ] == log_id )
1453+ owner_row = next (row for row in rows if row ["span_attributes" ]["name" ] == "owner" )
1454+ assert log_row ["id" ] != owner_row ["id" ]
1455+ assert log_row ["span_id" ] == owner_row ["span_id" ]
1456+ assert log_row ["root_span_id" ] == owner_row ["root_span_id" ]
1457+ assert not log_row .get ("span_parents" )
1458+ assert log_row ["context" ]["otel" ]["log" ]["severity_number" ] == 5
1459+
1460+
1461+ @pytest .mark .parametrize (
1462+ ("level" , "severity_number" ),
1463+ [("trace" , 1 ), ("debug" , 5 ), ("info" , 9 ), ("warn" , 13 ), ("error" , 17 ), ("fatal" , 21 )],
1464+ )
1465+ def test_logger_emit_log_maps_otel_log_levels (with_memory_logger , level , severity_number ):
1466+ test_logger = init_test_logger (__name__ )
1467+
1468+ test_logger .emit_log (body = "message" , level = level )
1469+
1470+ [row ] = with_memory_logger .pop ()
1471+ assert row ["context" ]["otel" ]["log" ]["severity_number" ] == severity_number
1472+ assert row ["context" ]["otel" ]["log" ]["severity_text" ] == level .upper ()
1473+
1474+
1475+ @pytest .mark .parametrize (
1476+ ("method_name" , "severity_number" ),
1477+ [("trace" , 1 ), ("debug" , 5 ), ("info" , 9 ), ("warn" , 13 ), ("error" , 17 ), ("fatal" , 21 )],
1478+ )
1479+ def test_logger_log_level_helpers (with_memory_logger , method_name , severity_number ):
1480+ test_logger = init_test_logger (__name__ )
1481+
1482+ log_id = getattr (test_logger , method_name )("message" , metadata = {"source" : method_name })
1483+
1484+ [row ] = with_memory_logger .pop ()
1485+ assert row ["id" ] == log_id
1486+ assert row ["output" ] == "message"
1487+ assert row ["metadata" ] == {"source" : method_name }
1488+ assert row ["context" ]["otel" ]["log" ]["severity_number" ] == severity_number
1489+
1490+
1491+ def test_logger_emit_log_rejects_invalid_level (with_memory_logger ):
1492+ test_logger = init_test_logger (__name__ )
1493+
1494+ with pytest .raises (ValueError , match = "Invalid log level" ):
1495+ test_logger .emit_log (body = "message" , level = "warning" )
1496+
1497+ assert with_memory_logger .pop () == []
1498+
1499+
13991500def test_experiment_log_accepts_model_dump_metadata (with_memory_logger ):
14001501 experiment = init_test_exp ("test-experiment" , "test-project" )
14011502
0 commit comments