This is an automated email from the ASF dual-hosted git repository.

kezhenxu94 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking-python.git


The following commit(s) were added to refs/heads/master by this push:
     new 0308fbf  Add elasticsearch plugin (#64)
0308fbf is described below

commit 0308fbf8cd838dd430656881a4f4bc70fb8381df
Author: huawei <[email protected]>
AuthorDate: Thu Aug 13 00:13:54 2020 +0800

    Add elasticsearch plugin (#64)
    
    * Add elasticsearch plugin
    
    * fix style
    
    * fix style
    
    Co-authored-by: huawei <[email protected]>
---
 docs/EnvVars.md                                    |  1 +
 docs/Plugins.md                                    |  1 +
 requirements.txt                                   |  1 +
 setup.py                                           |  1 +
 skywalking/__init__.py                             |  1 +
 skywalking/config.py                               |  2 +
 skywalking/plugins/sw_elasticsearch.py             | 54 +++++++++++++
 tests/plugin/sw_elasticsearch/__init__.py          | 16 ++++
 tests/plugin/sw_elasticsearch/docker-compose.yml   | 61 ++++++++++++++
 tests/plugin/sw_elasticsearch/expected.data.yml    | 92 ++++++++++++++++++++++
 tests/plugin/sw_elasticsearch/services/__init__.py | 16 ++++
 tests/plugin/sw_elasticsearch/services/consumer.py | 51 ++++++++++++
 .../plugin/sw_elasticsearch/test_elasticsearch.py  | 39 +++++++++
 13 files changed, 336 insertions(+)

diff --git a/docs/EnvVars.md b/docs/EnvVars.md
index 4f59807..395651a 100644
--- a/docs/EnvVars.md
+++ b/docs/EnvVars.md
@@ -21,3 +21,4 @@ Environment Variable | Description | Default
 | `SW_CORRELATION_VALUE_MAX_LENGTH`| Max value length of correlation context 
element.| `128` |
 | `SW_TRACE_IGNORE`| This config item controls that whether the trace should 
be ignore | `false` |
 | `SW_TRACE_IGNORE_PATH`| You can setup multiple URL path patterns, The 
endpoints match these patterns wouldn't be traced. the current matching rules 
follow Ant Path match style , like /path/*, /path/**, /path/?.| `''` |
+| `SW_ELASTICSEARCH_TRACE_DSL`| If true, trace all the DSL(Domain Specific 
Language) in ElasticSearch access, default is false | `false` |
diff --git a/docs/Plugins.md b/docs/Plugins.md
index dded9bb..dbe077a 100644
--- a/docs/Plugins.md
+++ b/docs/Plugins.md
@@ -13,3 +13,4 @@ Library | Plugin Name
 | [tornado](https://www.tornadoweb.org/en/stable/) | `sw_tornado` |
 | [pika](https://pika.readthedocs.io/en/stable/) | `sw_rabbitmq` |
 | [pymongo](https://pymongo.readthedocs.io/en/stable/) | `sw_pymongo` |
+| [elasticsearch](https://github.com/elastic/elasticsearch-py) | 
`sw_elasticsearch` |
diff --git a/requirements.txt b/requirements.txt
index facf167..6bbffba 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -34,3 +34,4 @@ urllib3==1.25.10
 websocket-client==0.57.0
 Werkzeug==1.0.1
 wrapt==1.12.1
+elasticsearch==7.8.0
diff --git a/setup.py b/setup.py
index 293f92c..27c1089 100644
--- a/setup.py
+++ b/setup.py
@@ -53,6 +53,7 @@ setup(
             "tornado",
             "pika",
             "pymongo",
+            "elasticsearch",
         ],
     },
     classifiers=[
diff --git a/skywalking/__init__.py b/skywalking/__init__.py
index 153ff5c..3616a32 100644
--- a/skywalking/__init__.py
+++ b/skywalking/__init__.py
@@ -35,6 +35,7 @@ class Component(Enum):
     KafkaConsumer = 41
     RabbitmqProducer = 52
     RabbitmqConsumer = 53
+    Elasticsearch = 47
 
 
 class Layer(Enum):
diff --git a/skywalking/config.py b/skywalking/config.py
index fb89ba8..b2def35 100644
--- a/skywalking/config.py
+++ b/skywalking/config.py
@@ -44,6 +44,8 @@ correlation_value_max_length = 
int(os.getenv('SW_CORRELATION_VALUE_MAX_LENGTH')
 trace_ignore = True if os.getenv('SW_TRACE_IGNORE') and \
                        os.getenv('SW_TRACE_IGNORE') == 'True' else False   # 
type: bool
 trace_ignore_path = (os.getenv('SW_TRACE_IGNORE_PATH') or '').split(',')  # 
type: List[str]
+elasticsearch_trace_dsl = True if os.getenv('SW_ELASTICSEARCH_TRACE_DSL') and \
+                                   os.getenv('SW_ELASTICSEARCH_TRACE_DSL') == 
'True' else False   # type: bool
 
 
 def init(
diff --git a/skywalking/plugins/sw_elasticsearch.py 
b/skywalking/plugins/sw_elasticsearch.py
new file mode 100644
index 0000000..a1649fa
--- /dev/null
+++ b/skywalking/plugins/sw_elasticsearch.py
@@ -0,0 +1,54 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import logging
+
+from skywalking import Layer, Component, config
+from skywalking.trace import tags
+from skywalking.trace.context import get_context
+from skywalking.trace.tags import Tag
+
+logger = logging.getLogger(__name__)
+
+
+def install():
+    # noinspection PyBroadException
+    try:
+        from elasticsearch import Transport
+        _perform_request = Transport.perform_request
+
+        def _sw_perform_request(this: Transport, method, url, headers=None, 
params=None, body=None):
+            context = get_context()
+            peer = ",".join([host["host"] + ":" + str(host["port"]) for host 
in this.hosts])
+            with context.new_exit_span(op="Elasticsearch/" + method + url, 
peer=peer) as span:
+                span.layer = Layer.Database
+                span.component = Component.Elasticsearch
+                try:
+                    res = _perform_request(this, method, url, headers=headers, 
params=params, body=body)
+
+                    span.tag(Tag(key=tags.DbType, val="Elasticsearch"))
+                    if config.elasticsearch_trace_dsl:
+                        span.tag(Tag(key=tags.DbStatement, val="" if body is 
None else body))
+
+                except BaseException as e:
+                    span.raised()
+                    raise e
+                return res
+
+        Transport.perform_request = _sw_perform_request
+
+    except Exception:
+        logger.warning('failed to install plugin %s', __name__)
diff --git a/tests/plugin/sw_elasticsearch/__init__.py 
b/tests/plugin/sw_elasticsearch/__init__.py
new file mode 100644
index 0000000..b1312a0
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/__init__.py
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/tests/plugin/sw_elasticsearch/docker-compose.yml 
b/tests/plugin/sw_elasticsearch/docker-compose.yml
new file mode 100644
index 0000000..1793521
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/docker-compose.yml
@@ -0,0 +1,61 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+version: '2.1'
+
+services:
+  collector:
+    extends:
+      service: collector
+      file: ../docker/docker-compose.base.yml
+
+  elasticsearch:
+    image: elasticsearch:6.8.11
+    hostname: elasticsearch
+    expose:
+      - 9200
+    environment:
+      - cluster.name=docker-node
+      - xpack.security.enabled=false
+      - bootstrap.memory_lock=true
+      - "ES_JAVA_OPTS=-Xms256m -Xmx256m"
+      - discovery.type=single-node
+    healthcheck:
+      test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/9200"]
+      interval: 5s
+      timeout: 60s
+      retries: 120
+    networks:
+      - beyond
+
+  consumer:
+    extends:
+      service: agent
+      file: ../docker/docker-compose.base.yml
+    ports:
+      - 9090:9090
+    volumes:
+      - ./services/consumer.py:/app/consumer.py
+    command: ['bash', '-c', 'pip install flask &&  pip install elasticsearch 
&& python3 /app/consumer.py']
+    depends_on:
+      collector:
+        condition: service_healthy
+      elasticsearch:
+        condition: service_healthy
+
+networks:
+  beyond:
diff --git a/tests/plugin/sw_elasticsearch/expected.data.yml 
b/tests/plugin/sw_elasticsearch/expected.data.yml
new file mode 100644
index 0000000..32c41f1
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/expected.data.yml
@@ -0,0 +1,92 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+segmentItems:
+  - serviceName: consumer
+    segmentSize: 1
+    segments:
+      - segmentId: not null
+        spans:
+          - operationName: Elasticsearch/PUT/test
+            operationId: 0
+            parentSpanId: 0
+            spanId: 1
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 47
+            isError: false
+            spanType: Exit
+            peer: elasticsearch:9200
+            skipAnalysis: false
+            tags:
+              - key: db.type
+                value: Elasticsearch
+              - key: db.statement
+                value: ''
+          - operationName: Elasticsearch/PUT/test/test/1
+            operationId: 0
+            parentSpanId: 0
+            spanId: 2
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 47
+            isError: false
+            spanType: Exit
+            peer: elasticsearch:9200
+            skipAnalysis: false
+            tags:
+              - key: db.type
+                value: Elasticsearch
+              - key: db.statement
+                value: '{''song'': ''Despacito'', ''artist'': ''Luis Fonsi''}'
+          - operationName: Elasticsearch/GET/test/_doc/1
+            operationId: 0
+            parentSpanId: 0
+            spanId: 3
+            spanLayer: Database
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 47
+            isError: false
+            spanType: Exit
+            peer: elasticsearch:9200
+            skipAnalysis: false
+            tags:
+              - key: db.type
+                value: Elasticsearch
+              - key: db.statement
+                value: ''
+          - operationName: /users
+            operationId: 0
+            parentSpanId: -1
+            spanId: 0
+            spanLayer: Http
+            tags:
+              - key: http.method
+                value: GET
+              - key: url
+                value: http://0.0.0.0:9090/users
+              - key: status.code
+                value: '200'
+            startTime: gt 0
+            endTime: gt 0
+            componentId: 7001
+            spanType: Entry
+            peer: not null
+            skipAnalysis: false
diff --git a/tests/plugin/sw_elasticsearch/services/__init__.py 
b/tests/plugin/sw_elasticsearch/services/__init__.py
new file mode 100644
index 0000000..b1312a0
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/services/__init__.py
@@ -0,0 +1,16 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
diff --git a/tests/plugin/sw_elasticsearch/services/consumer.py 
b/tests/plugin/sw_elasticsearch/services/consumer.py
new file mode 100644
index 0000000..5dc2f52
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/services/consumer.py
@@ -0,0 +1,51 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from elasticsearch import Elasticsearch
+from skywalking import agent, config
+
+if __name__ == '__main__':
+    config.service_name = 'consumer'
+    config.logging_level = 'DEBUG'
+    config.elasticsearch_trace_dsl = True
+    agent.start()
+
+    from flask import Flask, jsonify
+
+    app = Flask(__name__)
+    client = Elasticsearch('http://elasticsearch:9200/')
+    index_name = "test"
+
+    def create_index():
+        client.indices.create(index=index_name, ignore=400)
+
+    def save_index():
+        data = {"song": "Despacito", "artist": "Luis Fonsi"}
+        client.index(index=index_name, doc_type="test", id=1, body=data)
+
+    def search():
+        client.get(index=index_name, id=1)
+
+    @app.route("/users", methods=["POST", "GET"])
+    def application():
+        create_index()
+        save_index()
+        search()
+        return jsonify({"song": "Despacito", "artist": "Luis Fonsi"})
+
+    PORT = 9090
+    app.run(host='0.0.0.0', port=PORT, debug=True)
diff --git a/tests/plugin/sw_elasticsearch/test_elasticsearch.py 
b/tests/plugin/sw_elasticsearch/test_elasticsearch.py
new file mode 100644
index 0000000..c0064cf
--- /dev/null
+++ b/tests/plugin/sw_elasticsearch/test_elasticsearch.py
@@ -0,0 +1,39 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import inspect
+import time
+import unittest
+from os.path import dirname
+from testcontainers.compose import DockerCompose
+from tests.plugin import BasePluginTest
+
+
+class TestPlugin(BasePluginTest):
+    @classmethod
+    def setUpClass(cls):
+        cls.compose = DockerCompose(filepath=dirname(inspect.getfile(cls)))
+        cls.compose.start()
+        cls.compose.wait_for(cls.url(('consumer', '9090'), 'users'))
+
+    def test_plugin(self):
+        time.sleep(10)
+
+        self.validate()
+
+
+if __name__ == '__main__':
+    unittest.main()

Reply via email to