3838from uuid import UUID
3939
4040from opentelemetry import trace
41- from opentelemetry ._logs import Logger , LogRecord
41+ from opentelemetry ._logs import Logger
4242from opentelemetry .context import Context , get_current
4343from opentelemetry .metrics import Histogram , Meter , get_meter
44+ from opentelemetry .sdk ._logs ._internal import LogRecord as SDKLogRecord
4445from opentelemetry .semconv ._incubating .attributes import (
4546 gen_ai_attributes as GenAI ,
4647)
@@ -85,7 +86,14 @@ def _message_to_log_record(
8586 provider_name : Optional [str ],
8687 framework : Optional [str ],
8788 capture_content : bool ,
88- ) -> Optional [LogRecord ]:
89+ ) -> Optional [SDKLogRecord ]:
90+ """Build an SDK LogRecord for an input message.
91+
92+ Returns an SDK-level LogRecord configured with:
93+ - body: structured payload for the message (when capture_content is True)
94+ - attributes: includes semconv fields and attributes["event.name"]
95+ - event_name: mirrors the event name for SDK consumers
96+ """
8997 content = _get_property_value (message , "content" )
9098 message_type = _get_property_value (message , "type" )
9199
@@ -98,15 +106,17 @@ def _message_to_log_record(
98106 "gen_ai.framework" : framework ,
99107 # TODO: Convert below to constant once opentelemetry.semconv._incubating.attributes.gen_ai_attributes is available
100108 "gen_ai.provider.name" : provider_name ,
109+ # Prefer structured logs; include event name as an attribute.
110+ "event.name" : "gen_ai.client.inference.operation.details" ,
101111 }
102112
103113 if capture_content :
104114 attributes ["gen_ai.input.messages" ] = [message ._to_semconv_dict ()]
105115
106- return LogRecord (
107- event_name = "gen_ai.client.inference.operation.details" ,
108- attributes = attributes ,
116+ return SDKLogRecord (
109117 body = body or None ,
118+ attributes = attributes ,
119+ event_name = "gen_ai.client.inference.operation.details" ,
110120 )
111121
112122
@@ -116,14 +126,21 @@ def _chat_generation_to_log_record(
116126 provider_name : Optional [str ],
117127 framework : Optional [str ],
118128 capture_content : bool ,
119- ) -> Optional [LogRecord ]:
129+ ) -> Optional [SDKLogRecord ]:
130+ """Build an SDK LogRecord for a chat generation (choice) item.
131+
132+ Sets both the SDK event_name and attributes["event.name"] to "gen_ai.choice",
133+ and includes structured fields in body (index, finish_reason, message).
134+ """
120135 if not chat_generation :
121136 return None
122137 attributes = {
123138 # TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
124139 "gen_ai.framework" : framework ,
125140 # TODO: Convert below to constant once opentelemetry.semconv._incubating.attributes.gen_ai_attributes is available
126141 "gen_ai.provider.name" : provider_name ,
142+ # Prefer structured logs; include event name as an attribute.
143+ "event.name" : "gen_ai.choice" ,
127144 }
128145
129146 message = {
@@ -138,10 +155,10 @@ def _chat_generation_to_log_record(
138155 "message" : message ,
139156 }
140157
141- return LogRecord (
142- event_name = "gen_ai.choice" ,
143- attributes = attributes ,
158+ return SDKLogRecord (
144159 body = body or None ,
160+ attributes = attributes ,
161+ event_name = "gen_ai.choice" ,
145162 )
146163
147164
@@ -376,6 +393,7 @@ def start(self, invocation: LLMInvocation):
376393 capture_content = self ._capture_content ,
377394 )
378395 if log and self ._logger :
396+ # _message_to_log_record returns an SDKLogRecord
379397 self ._logger .emit (log )
380398
381399 def finish (self , invocation : LLMInvocation ):
0 commit comments