diff --git a/ee/lib/gitlab/duo/chat/react_executor.rb b/ee/lib/gitlab/duo/chat/react_executor.rb
index cdbc70dfade002a73a77f9ef3c9899622a74008c..514bb9f133b4ba56a819e87c4a57c59c00b3e026 100644
--- a/ee/lib/gitlab/duo/chat/react_executor.rb
+++ b/ee/lib/gitlab/duo/chat/react_executor.rb
@@ -33,7 +33,7 @@ def initialize(user_input:, tools:, context:, response_handler:, stream_response
         end
 
         def execute
-          MAX_ITERATIONS.times do
+          MAX_ITERATIONS.times do |i|
             events = step_forward
 
             raise EmptyEventsError if events.empty?
@@ -42,7 +42,11 @@ def execute
               process_tool_action(events) ||
               process_unknown(events)
 
-            return answer if answer
+            next unless answer
+
+            log_info(message: "ReAct turn", react_turn: i, event_name: 'react_turn', ai_component: 'duo_chat')
+
+            return answer
           end
 
           raise ExhaustedLoopError
diff --git a/ee/lib/gitlab/llm/concerns/logger.rb b/ee/lib/gitlab/llm/concerns/logger.rb
index ecfc9e7aa02334dd92cc4999cbab6bd7bc72545d..475b98d92dfdff3b806e61e871de2413986bd75e 100644
--- a/ee/lib/gitlab/llm/concerns/logger.rb
+++ b/ee/lib/gitlab/llm/concerns/logger.rb
@@ -37,6 +37,7 @@ module Logger
             Attribute.new(:picked_tool, String),
             Attribute.new(:allowed, String),
             Attribute.new(:tool_name, String),
+            Attribute.new(:react_turn, Integer),
             Attribute.new(:ai_event, String),
             Attribute.new(:params, String),
             Attribute.new(:status, Integer),
diff --git a/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb b/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
index 399710e509dd7b4264734e1bbb928faf23ca791f..ea60218e6cc6d2d9454aad30ac578f71124f6e18 100644
--- a/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
+++ b/ee/spec/lib/gitlab/duo/chat/react_executor_spec.rb
@@ -107,6 +107,9 @@
       end
 
       it "streams final answer" do
+        expect(agent).to receive(:log_info).with(
+          message: "ReAct turn", react_turn: 0, event_name: 'react_turn', ai_component: 'duo_chat')
+
         expect(stream_response_service_double).to receive(:execute).with(
           response: first_response_double,
           options: { chunk_id: 1 }
@@ -144,6 +147,9 @@
       end
 
       it "returns tool answer" do
+        expect(agent).to receive(:log_info).with(
+          message: "ReAct turn", react_turn: 0, event_name: 'react_turn', ai_component: 'duo_chat')
+
         expect(answer.is_final?).to be_truthy
         expect(answer.content).to include("tool answer")
       end
@@ -220,6 +226,9 @@
       end
 
       it "returns unknown answer as is" do
+        expect(agent).to receive(:log_info).with(
+          message: "ReAct turn", react_turn: 0, event_name: 'react_turn', ai_component: 'duo_chat')
+
         expect(answer.content).to include('foo')
       end
     end