Skip to content

Commit 66e0c26

Browse files
committed
remove events, emit structured logs
1 parent 5127c39 commit 66e0c26

File tree

4 files changed

+94
-84
lines changed

4 files changed

+94
-84
lines changed

util/opentelemetry-util-genai/src/opentelemetry/util/genai/data.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def _to_part_dict(self):
1313
Ref: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/gen-ai.md#gen-ai-input-messages
1414
"""
1515

16-
# Support tool_call and tool_call response
16+
# TODO: Support tool_call and tool_call response
1717
return {
1818
"role": self.type,
1919
"parts": [

util/opentelemetry-util-genai/src/opentelemetry/util/genai/emitters.py

Lines changed: 73 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
from uuid import UUID
1818

1919
from opentelemetry import trace
20-
from opentelemetry._events import Event
21-
from opentelemetry._logs import LogRecord
20+
from opentelemetry._logs import Logger, LogRecord
2221
from opentelemetry.context import Context, get_current
2322
from opentelemetry.metrics import Meter
2423
from opentelemetry.semconv._incubating.attributes import (
@@ -37,7 +36,7 @@
3736
from opentelemetry.trace.status import Status, StatusCode
3837
from opentelemetry.util.types import Attributes
3938

40-
from .data import Error
39+
from .data import ChatGeneration, Error, Message
4140
from .instruments import Instruments
4241
from .types import LLMInvocation
4342

@@ -49,7 +48,6 @@ class _SpanState:
4948
start_time: float
5049
request_model: Optional[str] = None
5150
system: Optional[str] = None
52-
db_system: Optional[str] = None
5351
children: List[UUID] = field(default_factory=list)
5452

5553

@@ -60,93 +58,54 @@ def _get_property_value(obj, property_name) -> object:
6058
return getattr(obj, property_name, None)
6159

6260

63-
def _message_to_event(message, provider_name, framework) -> Optional[Event]:
64-
content = _get_property_value(message, "content")
65-
# TODO: check if content is not None and should_collect_content()
66-
if content:
67-
# update this to event.gen_ai.client.inference.operation.details: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/gen-ai/gen-ai-events.md
68-
message_type = _get_property_value(message, "type")
69-
message_type = "user" if message_type == "human" else message_type
70-
body = {"content": content}
71-
attributes = {
72-
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
73-
"gen_ai.provider.name": provider_name, # Added in 1.37 - https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/gen-ai.md#gen-ai-provider-name
74-
"gen_ai.framework": framework,
75-
GenAI.GEN_AI_SYSTEM: provider_name, # Deprecated: Removed in 1.37
76-
}
77-
78-
return Event(
79-
name=f"gen_ai.{message_type}.message",
80-
attributes=attributes,
81-
body=body or None,
82-
)
83-
84-
8561
def _message_to_log_record(
86-
message, provider_name, framework
62+
message: Message, provider_name, framework, capture_content: bool
8763
) -> Optional[LogRecord]:
8864
content = _get_property_value(message, "content")
89-
# check if content is not None and should_collect_content()
9065
message_type = _get_property_value(message, "type")
91-
body = {"content": content}
66+
67+
body = {}
68+
if content and capture_content:
69+
body = {"type": message_type, "content": content}
9270

9371
attributes = {
9472
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
9573
"gen_ai.framework": framework,
74+
# TODO: Convert below to constant once opentelemetry.semconv._incubating.attributes.gen_ai_attributes is available
9675
"gen_ai.provider.name": provider_name,
97-
GenAI.GEN_AI_SYSTEM: provider_name, # Deprecated: use "gen_ai.provider.name"
9876
}
9977

78+
if capture_content:
79+
attributes["gen_ai.input.messages"] = [message._to_part_dict()]
80+
10081
return LogRecord(
101-
event_name=f"gen_ai.{message_type}.message",
82+
event_name="gen_ai.client.inference.operation.details",
10283
attributes=attributes,
10384
body=body or None,
10485
)
10586

10687

107-
def _chat_generation_to_event(
108-
chat_generation, index, provider_name, framework
109-
) -> Optional[Event]:
110-
if chat_generation.content:
111-
attributes = {
112-
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
113-
"gen_ai.provider.name": provider_name, # added in 1.37 - https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/gen-ai.md#gen-ai-provider-name
114-
"gen_ai.framework": framework,
115-
GenAI.GEN_AI_SYSTEM: provider_name, # Deprecated: removed in 1.37
116-
}
117-
118-
message = {
119-
"content": chat_generation.content,
120-
"type": chat_generation.type,
121-
}
122-
body = {
123-
"index": index,
124-
"finish_reason": chat_generation.finish_reason or "error",
125-
"message": message,
126-
}
127-
128-
return Event(
129-
name="gen_ai.choice",
130-
attributes=attributes,
131-
body=body or None,
132-
)
133-
134-
13588
def _chat_generation_to_log_record(
136-
chat_generation, index, prefix, provider_name, framework
89+
chat_generation: ChatGeneration,
90+
index,
91+
provider_name,
92+
framework,
93+
capture_content: bool,
13794
) -> Optional[LogRecord]:
13895
if chat_generation:
13996
attributes = {
14097
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
14198
"gen_ai.framework": framework,
99+
# TODO: Convert below to constant once opentelemetry.semconv._incubating.attributes.gen_ai_attributes is available
142100
"gen_ai.provider.name": provider_name,
143-
GenAI.GEN_AI_SYSTEM: provider_name, # Deprecated: removed in 1.37
144101
}
145102

146103
message = {
147-
"content": chat_generation.content,
148104
"type": chat_generation.type,
149105
}
106+
if capture_content and chat_generation.content:
107+
message["content"] = chat_generation.content
108+
150109
body = {
151110
"index": index,
152111
"finish_reason": chat_generation.finish_reason or "error",
@@ -204,13 +163,18 @@ class SpanMetricEventEmitter(BaseEmitter):
204163
"""
205164

206165
def __init__(
207-
self, event_logger, tracer: Tracer = None, meter: Meter = None
166+
self,
167+
logger: Logger = None,
168+
tracer: Tracer = None,
169+
meter: Meter = None,
170+
capture_content: bool = False,
208171
):
209172
self._tracer = tracer or trace.get_tracer(__name__)
210173
instruments = Instruments(meter)
211174
self._duration_histogram = instruments.operation_duration_histogram
212175
self._token_histogram = instruments.token_usage_histogram
213-
self._event_logger = event_logger
176+
self._logger = logger
177+
self._capture_content = capture_content
214178

215179
# Map from run_id -> _SpanState, to keep track of spans and parent/child relationships
216180
self.spans: Dict[UUID, _SpanState] = {}
@@ -250,13 +214,25 @@ def init(self, invocation: LLMInvocation):
250214

251215
for message in invocation.messages:
252216
system = invocation.attributes.get("system")
253-
self._event_logger.emit(
254-
_message_to_event(
255-
message=message,
256-
provider_name=system,
257-
framework=invocation.attributes.get("framework"),
258-
)
217+
# Event API is deprecated, use structured logs instead
218+
# event = _message_to_event(
219+
# message=message,
220+
# provider_name=system,
221+
# framework=invocation.attributes.get("framework"),
222+
# )
223+
# if event and self._event_logger:
224+
# self._event_logger.emit(
225+
# event
226+
# )
227+
228+
log = _message_to_log_record(
229+
message=message,
230+
provider_name=system,
231+
framework=invocation.attributes.get("framework"),
232+
capture_content=self._capture_content,
259233
)
234+
if log and self._logger:
235+
self._logger.emit(log)
260236

261237
def emit(self, invocation: LLMInvocation):
262238
system = invocation.attributes.get("system")
@@ -304,11 +280,24 @@ def emit(self, invocation: LLMInvocation):
304280
for index, chat_generation in enumerate(
305281
invocation.chat_generations
306282
):
307-
self._event_logger.emit(
308-
_chat_generation_to_event(
309-
chat_generation, index, system, framework
310-
)
283+
# Event API is deprecated. Use structured logs instead
284+
# event = _chat_generation_to_event(
285+
# chat_generation, index, system, framework
286+
# )
287+
# if event and self._event_logger:
288+
# self._event_logger.emit(
289+
# event
290+
# )
291+
292+
log = _chat_generation_to_log_record(
293+
chat_generation,
294+
index,
295+
system,
296+
framework,
297+
capture_content=self._capture_content,
311298
)
299+
if log and self._logger:
300+
self._logger.emit(log)
312301
finish_reasons.append(chat_generation.finish_reason)
313302

314303
if finish_reasons is not None and len(finish_reasons) > 0:
@@ -426,11 +415,17 @@ class SpanMetricEmitter(BaseEmitter):
426415
Emits only spans and metrics (no events).
427416
"""
428417

429-
def __init__(self, tracer: Tracer = None, meter: Meter = None):
418+
def __init__(
419+
self,
420+
tracer: Tracer = None,
421+
meter: Meter = None,
422+
capture_content: bool = False,
423+
):
430424
self._tracer = tracer or trace.get_tracer(__name__)
431425
instruments = Instruments(meter)
432426
self._duration_histogram = instruments.operation_duration_histogram
433427
self._token_histogram = instruments.token_usage_histogram
428+
self._capture_content = capture_content
434429

435430
# Map from run_id -> _SpanState, to keep track of spans and parent/child relationships
436431
self.spans: Dict[UUID, _SpanState] = {}
@@ -502,21 +497,19 @@ def emit(self, invocation: LLMInvocation):
502497
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
503498
framework = invocation.attributes.get("framework")
504499
if framework is not None:
505-
span.set_attribute(
506-
"gen_ai.framework", invocation.attributes.get("framework")
507-
)
500+
span.set_attribute("gen_ai.framework", framework)
508501
span.set_attribute(
509502
GenAI.GEN_AI_SYSTEM, system
510503
) # Deprecated: use "gen_ai.provider.name"
511504
# TODO: add below to opentelemetry.semconv._incubating.attributes.gen_ai_attributes
512505
span.set_attribute("gen_ai.provider.name", system)
513506

514-
finish_reasons = []
507+
finish_reasons: list[str] = []
515508
for index, chat_generation in enumerate(
516509
invocation.chat_generations
517510
):
518511
finish_reasons.append(chat_generation.finish_reason)
519-
if finish_reasons is not None and len(finish_reasons) > 0:
512+
if finish_reasons and len(finish_reasons) > 0:
520513
span.set_attribute(
521514
GenAI.GEN_AI_RESPONSE_FINISH_REASONS, finish_reasons
522515
)

util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from uuid import UUID
1919

2020
from opentelemetry._events import get_event_logger
21+
from opentelemetry._logs import get_logger
2122
from opentelemetry.metrics import get_meter
2223
from opentelemetry.semconv.schemas import Schemas
2324
from opentelemetry.trace import get_tracer
@@ -61,19 +62,35 @@ def __init__(self, emitter_type_full: bool = True, **kwargs: Any):
6162
schema_url=Schemas.V1_36_0.value,
6263
)
6364

65+
logger_provider = kwargs.get("logger_provider")
66+
self._logger = get_logger(
67+
__name__,
68+
__version__,
69+
logger_provider=logger_provider,
70+
schema_url=Schemas.V1_36_0.value,
71+
)
72+
6473
self._emitter = (
6574
SpanMetricEventEmitter(
6675
tracer=self._tracer,
6776
meter=self._meter,
68-
event_logger=self._event_logger,
77+
logger=self._logger,
78+
capture_content=self._should_collect_content(),
6979
)
7080
if emitter_type_full
71-
else SpanMetricEmitter(tracer=self._tracer, meter=self._meter)
81+
else SpanMetricEmitter(
82+
tracer=self._tracer,
83+
meter=self._meter,
84+
capture_content=self._should_collect_content(),
85+
)
7286
)
7387

7488
self._llm_registry: dict[UUID, LLMInvocation] = {}
7589
self._lock = Lock()
7690

91+
def _should_collect_content(self) -> bool:
92+
return True # Placeholder for future config
93+
7794
def start_llm(
7895
self,
7996
prompts: List[Message],

util/opentelemetry-util-genai/tests/test_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
99
InMemorySpanExporter,
1010
)
11-
from opentelemetry.util.genai.client import (
11+
from opentelemetry.util.genai.handler import (
1212
llm_start,
1313
llm_stop,
1414
)

0 commit comments

Comments
 (0)