@@ -1008,91 +1008,63 @@ async def some_activity():
10081008 )
10091009
10101010
1011+ @pytest .mark .parametrize ("temporal_extra_mode" , ["dict" , "flatten" ])
10111012async def test_activity_logging (
10121013 client : Client ,
10131014 worker : ExternalWorker ,
10141015 shared_state_manager : SharedStateManager ,
1016+ temporal_extra_mode : str ,
10151017):
1016- @activity .defn
1017- async def say_hello (name : str ) -> str :
1018- activity .logger .info (f"Called with arg: { name } " )
1019- return f"Hello, { name } !"
1020-
1021- # Create a queue, add handler to logger, call normal activity, then check
1022- handler = logging .handlers .QueueHandler (queue .Queue ())
1023- with LogHandler .apply (activity .logger .base_logger , handler ):
1024- activity .logger .base_logger .setLevel (logging .INFO )
1025- result = await _execute_workflow_with_activity (
1026- client ,
1027- worker ,
1028- say_hello ,
1029- "Temporal" ,
1030- shared_state_manager = shared_state_manager ,
1031- )
1032- assert result .result == "Hello, Temporal!"
1033- records : list [logging .LogRecord ] = list (handler .queue .queue ) # type: ignore
1034- assert len (records ) > 0
1035- assert records [- 1 ].message .startswith (
1036- "Called with arg: Temporal ({'activity_id': '"
1037- )
1038- assert records [- 1 ].__dict__ ["temporal_activity" ]["activity_type" ] == "say_hello"
1039-
1040-
1041- async def test_activity_logging_flatten_mode (
1042- client : Client ,
1043- worker : ExternalWorker ,
1044- shared_state_manager : SharedStateManager ,
1045- ):
1046- """Test that activity logger flatten mode produces OTel-safe scalar attributes."""
1018+ """Test that activity logger produces correct log records for each extra mode."""
10471019
10481020 @activity .defn
1049- async def say_hello_flatten (name : str ) -> str :
1021+ async def say_hello (name : str ) -> str :
10501022 activity .logger .info (f"Called with arg: { name } " )
10511023 return f"Hello, { name } !"
10521024
10531025 original_mode = activity .logger .temporal_extra_mode
1054- activity .logger .temporal_extra_mode = "flatten"
1026+ activity .logger .temporal_extra_mode = temporal_extra_mode
10551027
10561028 handler = logging .handlers .QueueHandler (queue .Queue ())
1057- activity .logger .base_logger .addHandler (handler )
1058- prev_level = activity .logger .base_logger .level
1059- activity .logger .base_logger .setLevel (logging .INFO )
10601029 try :
1061- result = await _execute_workflow_with_activity (
1062- client ,
1063- worker ,
1064- say_hello_flatten ,
1065- "Temporal" ,
1066- shared_state_manager = shared_state_manager ,
1067- )
1030+ with LogHandler .apply (activity .logger .base_logger , handler ):
1031+ activity .logger .base_logger .setLevel (logging .INFO )
1032+ result = await _execute_workflow_with_activity (
1033+ client ,
1034+ worker ,
1035+ say_hello ,
1036+ "Temporal" ,
1037+ shared_state_manager = shared_state_manager ,
1038+ )
10681039 finally :
1069- activity .logger .base_logger .removeHandler (handler )
1070- activity .logger .base_logger .setLevel (prev_level )
10711040 activity .logger .temporal_extra_mode = original_mode
10721041
10731042 assert result .result == "Hello, Temporal!"
10741043 records : list [logging .LogRecord ] = list (handler .queue .queue ) # type: ignore
10751044 assert len (records ) > 0
10761045 record = records [- 1 ]
10771046
1078- # Should NOT have nested dict
1079- assert "temporal_activity" not in record .__dict__
1080-
1081- # Should have flattened keys with temporal.activity prefix
1082- assert record .__dict__ ["temporal.activity.activity_type" ] == "say_hello_flatten"
1083- assert "temporal.activity.activity_id" in record .__dict__
1084- assert "temporal.activity.workflow_id" in record .__dict__
1085- assert "temporal.activity.workflow_run_id" in record .__dict__
1086- assert "temporal.activity.namespace" in record .__dict__
1087- assert "temporal.activity.task_queue" in record .__dict__
1088- assert record .__dict__ ["temporal.activity.attempt" ] == 1
1089-
1090- # Verify all temporal.activity.* values are primitives (OTel-safe)
1091- for key , value in record .__dict__ .items ():
1092- if key .startswith ("temporal.activity." ):
1093- assert isinstance (
1094- value , (str , int , float , bool , type (None ))
1095- ), f"Key { key } has non-primitive value: { type (value )} "
1047+ if temporal_extra_mode == "dict" :
1048+ # Dict mode appends context to message and uses nested dict
1049+ assert record .message .startswith ("Called with arg: Temporal ({'activity_id': '" )
1050+ assert record .__dict__ ["temporal_activity" ]["activity_type" ] == "say_hello"
1051+ else :
1052+ # Flatten mode uses OTel-safe scalar attributes
1053+ assert "temporal_activity" not in record .__dict__
1054+ assert record .__dict__ ["temporal.activity.activity_type" ] == "say_hello"
1055+ assert "temporal.activity.activity_id" in record .__dict__
1056+ assert "temporal.activity.workflow_id" in record .__dict__
1057+ assert "temporal.activity.workflow_run_id" in record .__dict__
1058+ assert "temporal.activity.namespace" in record .__dict__
1059+ assert "temporal.activity.task_queue" in record .__dict__
1060+ assert record .__dict__ ["temporal.activity.attempt" ] == 1
1061+
1062+ # Verify all temporal.activity.* values are primitives (OTel-safe)
1063+ for key , value in record .__dict__ .items ():
1064+ if key .startswith ("temporal.activity." ):
1065+ assert isinstance (
1066+ value , (str , int , float , bool , type (None ))
1067+ ), f"Key { key } has non-primitive value: { type (value )} "
10961068
10971069
10981070async def test_activity_worker_shutdown (
0 commit comments