11
11
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
+
14
15
import json
15
16
import os
16
17
import threading
27
28
from opentelemetry .instrumentation .elasticsearch import (
28
29
ElasticsearchInstrumentor ,
29
30
)
31
+ from opentelemetry .instrumentation .elasticsearch .utils import sanitize_body
30
32
from opentelemetry .semconv .trace import SpanAttributes
31
33
from opentelemetry .test .test_base import TestBase
32
34
from opentelemetry .trace import StatusCode
33
35
36
+ from . import sanitization_queries # pylint: disable=no-name-in-module
37
+
34
38
major_version = elasticsearch .VERSION [0 ]
35
39
36
40
if major_version == 7 :
42
46
else :
43
47
from . import helpers_es2 as helpers # pylint: disable=no-name-in-module
44
48
45
-
46
49
Article = helpers .Article
47
50
48
51
49
52
@mock .patch (
50
53
"elasticsearch.connection.http_urllib3.Urllib3HttpConnection.perform_request"
51
54
)
52
55
class TestElasticsearchIntegration (TestBase ):
56
+ search_attributes = {
57
+ SpanAttributes .DB_SYSTEM : "elasticsearch" ,
58
+ "elasticsearch.url" : "/test-index/_search" ,
59
+ "elasticsearch.method" : helpers .dsl_search_method ,
60
+ "elasticsearch.target" : "test-index" ,
61
+ SpanAttributes .DB_STATEMENT : str (
62
+ {"query" : {"bool" : {"filter" : [{"term" : {"author" : "testing" }}]}}}
63
+ ),
64
+ }
65
+
66
+ create_attributes = {
67
+ SpanAttributes .DB_SYSTEM : "elasticsearch" ,
68
+ "elasticsearch.url" : "/test-index" ,
69
+ "elasticsearch.method" : "HEAD" ,
70
+ }
71
+
53
72
def setUp (self ):
54
73
super ().setUp ()
55
74
self .tracer = self .tracer_provider .get_tracer (__name__ )
@@ -241,21 +260,36 @@ def test_dsl_search(self, request_mock):
241
260
self .assertIsNotNone (span .end_time )
242
261
self .assertEqual (
243
262
span .attributes ,
263
+ self .search_attributes ,
264
+ )
265
+
266
+ def test_dsl_search_sanitized (self , request_mock ):
267
+ # Reset instrumentation to use sanitized query (default)
268
+ ElasticsearchInstrumentor ().uninstrument ()
269
+ ElasticsearchInstrumentor ().instrument (sanitize_query = True )
270
+
271
+ # update expected attributes to match sanitized query
272
+ sanitized_search_attributes = self .search_attributes .copy ()
273
+ sanitized_search_attributes .update (
244
274
{
245
- SpanAttributes .DB_SYSTEM : "elasticsearch" ,
246
- "elasticsearch.url" : "/test-index/_search" ,
247
- "elasticsearch.method" : helpers .dsl_search_method ,
248
- "elasticsearch.target" : "test-index" ,
249
- SpanAttributes .DB_STATEMENT : str (
250
- {
251
- "query" : {
252
- "bool" : {
253
- "filter" : [{"term" : {"author" : "testing" }}]
254
- }
255
- }
256
- }
257
- ),
258
- },
275
+ SpanAttributes .DB_STATEMENT : "{'query': {'bool': {'filter': '?'}}}"
276
+ }
277
+ )
278
+
279
+ request_mock .return_value = (1 , {}, '{"hits": {"hits": []}}' )
280
+ client = Elasticsearch ()
281
+ search = Search (using = client , index = "test-index" ).filter (
282
+ "term" , author = "testing"
283
+ )
284
+ search .execute ()
285
+ spans = self .get_finished_spans ()
286
+ span = spans [0 ]
287
+ self .assertEqual (1 , len (spans ))
288
+ self .assertEqual (span .name , "Elasticsearch/<target>/_search" )
289
+ self .assertIsNotNone (span .end_time )
290
+ self .assertEqual (
291
+ span .attributes ,
292
+ sanitized_search_attributes ,
259
293
)
260
294
261
295
def test_dsl_create (self , request_mock ):
@@ -264,17 +298,14 @@ def test_dsl_create(self, request_mock):
264
298
Article .init (using = client )
265
299
266
300
spans = self .get_finished_spans ()
301
+ assert spans
267
302
self .assertEqual (2 , len (spans ))
268
303
span1 = spans .by_attr (key = "elasticsearch.method" , value = "HEAD" )
269
304
span2 = spans .by_attr (key = "elasticsearch.method" , value = "PUT" )
270
305
271
306
self .assertEqual (
272
307
span1 .attributes ,
273
- {
274
- SpanAttributes .DB_SYSTEM : "elasticsearch" ,
275
- "elasticsearch.url" : "/test-index" ,
276
- "elasticsearch.method" : "HEAD" ,
277
- },
308
+ self .create_attributes ,
278
309
)
279
310
280
311
attributes = {
@@ -288,6 +319,25 @@ def test_dsl_create(self, request_mock):
288
319
helpers .dsl_create_statement ,
289
320
)
290
321
322
+ def test_dsl_create_sanitized (self , request_mock ):
323
+ # Reset instrumentation to explicitly use sanitized query
324
+ ElasticsearchInstrumentor ().uninstrument ()
325
+ ElasticsearchInstrumentor ().instrument (sanitize_query = True )
326
+ request_mock .return_value = (1 , {}, {})
327
+ client = Elasticsearch ()
328
+ Article .init (using = client )
329
+
330
+ spans = self .get_finished_spans ()
331
+ assert spans
332
+
333
+ self .assertEqual (2 , len (spans ))
334
+ span = spans .by_attr (key = "elasticsearch.method" , value = "HEAD" )
335
+
336
+ self .assertEqual (
337
+ span .attributes ,
338
+ self .create_attributes ,
339
+ )
340
+
291
341
def test_dsl_index (self , request_mock ):
292
342
request_mock .return_value = helpers .dsl_index_result
293
343
@@ -412,3 +462,17 @@ def response_hook(span, response):
412
462
json .dumps (response_payload ),
413
463
spans [0 ].attributes [response_attribute_name ],
414
464
)
465
+
466
+ def test_body_sanitization (self , _ ):
467
+ self .assertEqual (
468
+ sanitize_body (sanitization_queries .interval_query ),
469
+ str (sanitization_queries .interval_query_sanitized ),
470
+ )
471
+ self .assertEqual (
472
+ sanitize_body (sanitization_queries .match_query ),
473
+ str (sanitization_queries .match_query_sanitized ),
474
+ )
475
+ self .assertEqual (
476
+ sanitize_body (sanitization_queries .filter_query ),
477
+ str (sanitization_queries .filter_query_sanitized ),
478
+ )
0 commit comments