Skip to content

Commit f846c4f

Browse files
divyapathak24ellipsis-dev[bot]nirga
authored
feat(milvus): Add error.type attribute from OpenTelemetry Semantic Conventions (#3009)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Nir Gazit <[email protected]>
1 parent e80094d commit f846c4f

File tree

2 files changed

+120
-11
lines changed

2 files changed

+120
-11
lines changed

packages/opentelemetry-instrumentation-milvus/opentelemetry/instrumentation/milvus/wrapper.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,24 @@
11
from opentelemetry.instrumentation.milvus.utils import dont_throw
22
from opentelemetry.semconv.trace import SpanAttributes
3-
3+
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
44
from opentelemetry import context as context_api
55
from opentelemetry.instrumentation.utils import (
66
_SUPPRESS_INSTRUMENTATION_KEY,
77
)
88
from opentelemetry.semconv_ai import Events, EventAttributes
99
from opentelemetry.semconv_ai import SpanAttributes as AISpanAttributes
1010

11+
from pymilvus.client.types import Status
12+
from pymilvus.exceptions import ErrorCode
13+
14+
code_to_error_type = {}
15+
for name in dir(Status):
16+
value = getattr(Status, name)
17+
if isinstance(value, int):
18+
code_to_error_type[value] = name
19+
20+
code_to_error_type.update({code.value: code.name for code in ErrorCode})
21+
1122

1223
def _with_tracer_wrapper(func):
1324
"""Helper for providing tracer for wrapper functions."""
@@ -56,16 +67,22 @@ def _wrap(tracer, to_wrap, wrapped, instance, args, kwargs):
5667
elif to_wrap.get("method") == "hybrid_search":
5768
_set_hybrid_search_attributes(span, kwargs)
5869

59-
return_value = wrapped(*args, **kwargs)
60-
61-
if to_wrap.get("method") == "query":
62-
_add_query_result_events(span, return_value)
63-
64-
if (
65-
to_wrap.get("method") == "search"
66-
or to_wrap.get("method") == "hybrid_search"
67-
):
68-
_add_search_result_events(span, return_value)
70+
try:
71+
return_value = wrapped(*args, **kwargs)
72+
if to_wrap.get("method") == "query":
73+
_add_query_result_events(span, return_value)
74+
75+
if (
76+
to_wrap.get("method") == "search"
77+
or to_wrap.get("method") == "hybrid_search"
78+
):
79+
_add_search_result_events(span, return_value)
80+
except Exception as e:
81+
error_type = code_to_error_type.get(
82+
getattr(e, "code", None), type(e).__name__
83+
)
84+
span.set_attribute(ERROR_TYPE, error_type)
85+
raise
6986

7087
return return_value
7188

@@ -345,6 +362,7 @@ def set_global_stats():
345362
_set_span_attribute(
346363
span, AISpanAttributes.MILVUS_SEARCH_RESULT_COUNT, total_matches
347364
)
365+
348366
for query_idx, query_results in enumerate(kwargs):
349367

350368
query_distances = []
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import os
2+
import random
3+
4+
import pymilvus
5+
import pytest
6+
from opentelemetry.trace.status import StatusCode
7+
8+
path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "milvus.db")
9+
milvus = pymilvus.MilvusClient(uri=path)
10+
11+
12+
@pytest.fixture
13+
def collection():
14+
collection_name = "Colors"
15+
milvus.create_collection(collection_name=collection_name, dimension=5)
16+
yield collection_name
17+
milvus.drop_collection(collection_name=collection_name)
18+
19+
20+
def insert_data(collection):
21+
colors = [
22+
"green",
23+
"blue",
24+
"yellow",
25+
"red",
26+
"black",
27+
"white",
28+
"purple",
29+
"pink",
30+
"orange",
31+
"grey",
32+
]
33+
data = [
34+
{
35+
"id": i,
36+
"vector": [random.uniform(-1, 1) for _ in range(5)],
37+
"color": random.choice(colors),
38+
"tag": random.randint(1000, 9999),
39+
}
40+
for i in range(1000)
41+
]
42+
data += [
43+
{
44+
"id": 1000,
45+
"vector": [random.uniform(-1, 1) for _ in range(5)],
46+
"color": "brown",
47+
"tag": 1234,
48+
},
49+
{
50+
"id": 1001,
51+
"vector": [random.uniform(-1, 1) for _ in range(5)],
52+
"color": "brown",
53+
"tag": 5678,
54+
},
55+
{
56+
"id": 1002,
57+
"vector": [random.uniform(-1, 1) for _ in range(5)],
58+
"color": "brown",
59+
"tag": 9101,
60+
},
61+
]
62+
for i in data:
63+
i["color_tag"] = "{}_{}".format(i["color"], i["tag"])
64+
milvus.insert(collection_name=collection, data=data)
65+
66+
67+
def test_milvus_single_vector_search(exporter, collection):
68+
insert_data(collection)
69+
70+
query_vector = [random.uniform(-1, 1) for _ in range(5)]
71+
search_params = {"radius": 0.5, "metric_type": "COSINE", "index_type": "IVF_FLAT"}
72+
with pytest.raises(Exception):
73+
milvus.search(
74+
collection_name="random", # non-existent collection
75+
data=[query_vector],
76+
anns_field="vector",
77+
search_params=search_params,
78+
output_fields=["color_tag"],
79+
limit=3,
80+
timeout=10,
81+
)
82+
83+
# Get finished spans
84+
spans = exporter.get_finished_spans()
85+
span = next(span for span in spans if span.name == "milvus.search")
86+
87+
# Check if status code is error
88+
assert span.status.status_code == StatusCode.ERROR
89+
90+
# Check the span attributes related to search
91+
assert span.attributes.get("error.type") == "COLLECTION_NOT_FOUND"

0 commit comments

Comments
 (0)