diff --git a/CHANGELOG.md b/CHANGELOG.md index c8861b4..80a611c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,13 @@ Release Notes. #### Features - Initialize the ruby agent core. - Implement e2e tests. -- Add docs. #### Plugins * Support [Sinatra](https://github.com/sinatra/sinatra) * Support [redis-rb](https://github.com/redis/redis-rb) * Support [net-http](https://github.com/ruby/net-http) +* Support [memcached](https://github.com/petergoldstein/dalli) +* Support [elasticsearch](https://github.com/elastic/elasticsearch-ruby) #### Documentation * Initialize the documentation. \ No newline at end of file diff --git a/docs/en/agent/plugins.md b/docs/en/agent/plugins.md index a22d930..6c3ca8c 100644 --- a/docs/en/agent/plugins.md +++ b/docs/en/agent/plugins.md @@ -3,8 +3,10 @@ The following plugins provide the distributed tracing capability, and the OAP backend would analyze the topology and metrics based on the tracing data. -| Library | Version | Plugin Name | -|:----------------------------------------------|:---------|:------------| -| [redis](https://github.com/redis/redis-rb) | ~> 5.0 | `redis` | -| [net-http](https://github.com/ruby/net-http) | ~> 0.6.0 | `net_http` | -| [sinatra](https://github.com/sinatra/sinatra) | ~> 4.1 | `sinatra` | \ No newline at end of file +| Library | Version | Plugin Name | +|:---------------------------------------------------------------|:---------|:----------------| +| [redis](https://github.com/redis/redis-rb) | ~> 5.0 | `redis5` | +| [net-http](https://github.com/ruby/net-http) | ~> 0.6.0 | `net_http` | +| [sinatra](https://github.com/sinatra/sinatra) | ~> 4.1 | `sinatra` | +| [memcached](https://github.com/petergoldstein/dalli) | ~> 3.2 | `memcached` | +| [elasticsearch](https://github.com/elastic/elasticsearch-ruby) | ~> 8.0.0 | `elasticsearch` | \ No newline at end of file diff --git a/docs/en/setup/quick-start.md b/docs/en/setup/quick-start.md index 68f4f9e..e367d35 100644 --- a/docs/en/setup/quick-start.md +++ b/docs/en/setup/quick-start.md @@ -97,7 +97,7 @@ The following lists all the configuration options: | log_file_name | SW_AGENT_LOG_FILE_NAME | skywalking | The name of the log file. | | log_file_path | SW_AGENT_LOG_FILE_PATH | Not set | The path to the log file. | | log_level | SW_AGENT_LOG_LEVEL | info | The log level. | -| disable_plugins | SW_AGENT_DISABLE_PLUGINS | Not set | The plugins to disable. | +| disable_plugins | SW_AGENT_DISABLE_PLUGINS | Not set | The plugins to disable, multiple names should be split by comma, e.g. 'redis5,elasticsearch'. | | report_protocol | SW_AGENT_REPORT_PROTOCOL | grpc | The protocol to use for reporting. | | re_ignore_operation | SW_AGENT_RE_IGNORE_OPERATION | Not set | Ignore specific URL paths. | | instance_properties_json | SW_AGENT_INSTANCE_PROPERTIES_JSON | Not set | A custom JSON string to be reported as service instance properties, e.g. `{"key": "value"}`. | diff --git a/lib/skywalking/configuration.rb b/lib/skywalking/configuration.rb index 2d83988..6efe3ae 100644 --- a/lib/skywalking/configuration.rb +++ b/lib/skywalking/configuration.rb @@ -67,7 +67,7 @@ class Configuration :disable_plugins => { type: :string, default: '', - desc: 'The plugins to disable' + desc: "The plugins to disable, multiple names should be split by comma, e.g. 'redis5,elasticsearch'" }, :report_protocol => { type: :string, diff --git a/lib/skywalking/plugins/elasticsearch.rb b/lib/skywalking/plugins/elasticsearch.rb new file mode 100644 index 0000000..1ad6975 --- /dev/null +++ b/lib/skywalking/plugins/elasticsearch.rb @@ -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. + +module Skywalking + module Plugins + class Elasticsearch < PluginsManager::SWPlugin + def plugin_valid? + defined?(::Elasticsearch) + end + + def install + inst_target = if defined?(::Elastic::Transport::Client) + ::Elastic::Transport::Client + elsif defined?(::Elasticsearch::Transport::Client) + ::Elasticsearch::Transport::Client + end + + inst_target.class_eval do + def perform_request_with_skywalking(method, path, *args, &block) + peer_info = transport.hosts.first + db_statement = [{ params: args&.[](0) }] + unless args[1].nil? || args[1].empty? + db_statement << { body: args[1] } + end + + Tracing::ContextManager.new_exit_span( + operation: "Elasticsearch/#{method}/#{path}", + peer: "#{peer_info[:protocol]}://#{peer_info[:host]}:#{peer_info[:port]}", + component: Tracing::Component::Elasticsearch + ) do |span| + span&.tag(Tracing::TagDbType.new("Elasticsearch")) + span&.tag(Tracing::TagDbStatement.new(db_statement)) + span&.layer = Tracing::Layer::Database + + zuper_perform_request(method, path, *args, &block) + rescue + span&.error_occurred = true + end + end + + alias_method :zuper_perform_request, :perform_request + alias_method :perform_request, :perform_request_with_skywalking + end + end + + register :elasticsearch + end + end +end diff --git a/lib/skywalking/plugins/memcached.rb b/lib/skywalking/plugins/memcached.rb new file mode 100644 index 0000000..d2f1f86 --- /dev/null +++ b/lib/skywalking/plugins/memcached.rb @@ -0,0 +1,74 @@ +# 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. + +module Skywalking + module Plugins + module MemcachedIntercept + def self.included(klass) + supported_method = + [:add, :append, :delete, :cas, :incr, :increment, :prepend, :replace, :set, :get, :fetch] + .select do |method_name| + klass.method_defined?(method_name) || klass.private_method_defined?(method_name) + end + + supported_method.each do |method_name| + zuper_method = :"zuper_#{method_name}" + method_with_skywalking = :"#{method_name}_with_skywalking" + + klass.class_eval do + define_method(method_with_skywalking) do |*args, &block| + cache_key = args[0].to_s if args.length && !args[0].is_a?(Array) + Tracing::ContextManager.new_exit_span( + operation: "Memcached/#{method_name}", + peer: @normalized_servers.join(','), + component: Tracing::Component::Memcached + ) do |span| + span&.layer = Tracing::Layer::Cache + span&.tag(Tracing::TagCacheType.new("Memcached")) + span&.tag(Tracing::TagCacheKey.new(cache_key)) + + resp = __send__(zuper_method, *args, &block) + if method_name == :get && args.length && args[0].instance_of?(String) + span&.tag(Tracing::TagCacheMiss.new(resp.nil?)) + end + + resp + rescue + span&.error_occurred = true + end + end + + alias_method zuper_method, method_name + alias_method method_name, method_with_skywalking + end + end + end + end + + class Memcached < PluginsManager::SWPlugin + def plugin_valid? + defined?(::Dalli::Client) + end + + def install + ::Dalli::Client.class_eval do + include Skywalking::Plugins::MemcachedIntercept + end + end + + register :memcached + end + end +end diff --git a/lib/skywalking/plugins/net_http.rb b/lib/skywalking/plugins/net_http.rb index 64cb328..08e6b9c 100644 --- a/lib/skywalking/plugins/net_http.rb +++ b/lib/skywalking/plugins/net_http.rb @@ -35,7 +35,7 @@ def request(req, body = nil, &block) Tracing::ContextManager.new_exit_span( operation: "#{method}:#{req_info}", peer: host, - component: Tracing::Component::Http + component: Tracing::Component::HttpClient ) do |span| span&.tag(Tracing::TagHttpMethod.new(method)) span&.tag(Tracing::TagHttpURL.new(uri)) diff --git a/lib/skywalking/plugins/redis5.rb b/lib/skywalking/plugins/redis5.rb index db5b777..d2e11ed 100644 --- a/lib/skywalking/plugins/redis5.rb +++ b/lib/skywalking/plugins/redis5.rb @@ -30,7 +30,11 @@ def call_v(args, &block) span&.tag(Tracing::TagCacheOp.new(operation)) span&.layer = Tracing::Layer::Cache - super(args, &block) + begin + super(args, &block) + rescue + span&.error_occurred = true + end end end diff --git a/lib/skywalking/plugins/sinatra.rb b/lib/skywalking/plugins/sinatra.rb index a9bf511..c1ea9ba 100644 --- a/lib/skywalking/plugins/sinatra.rb +++ b/lib/skywalking/plugins/sinatra.rb @@ -21,13 +21,12 @@ def dispatch!(*args, &block) req_method = @request.request_method if @request.respond_to?(:request_method) carrier = Tracing::Carrier.new carrier.each do |item| - item.val = request.env[item.key.capitalize] if request.env[item.key.capitalize] + item.value = request.env["HTTP_#{item.key.upcase}"] end Tracing::ContextManager.new_entry_span( operation: "#{req_method}:#{request.env['REQUEST_URI']}", - carrier: carrier, - inherit: Tracing::Component::General + carrier: carrier ) do |span| span&.tag(Tracing::TagHttpMethod.new(req_method)) span&.tag(Tracing::TagHttpURL.new(request.env['REQUEST_URI'])) @@ -36,6 +35,8 @@ def dispatch!(*args, &block) span&.component = Tracing::Component::Sinatra super(*args, &block) + rescue + span&.error_occurred = true end end end diff --git a/lib/skywalking/plugins_manager.rb b/lib/skywalking/plugins_manager.rb index 2a46155..d9387bc 100644 --- a/lib/skywalking/plugins_manager.rb +++ b/lib/skywalking/plugins_manager.rb @@ -34,7 +34,7 @@ def init_plugins Dir[File.join(__dir__, 'plugins', '*.rb')].each { |file| require file } installed_plugins = self.class.installed.keys - @enabled_plugins ||= installed_plugins - @config[:disable_plugins].split(',') + @enabled_plugins ||= installed_plugins - @config[:disable_plugins].split(',').map(&:to_sym) @enabled_plugins.each do |plugin_name| self.class.installed[plugin_name].try_install(plugin_name) end diff --git a/lib/skywalking/tracing/carrier.rb b/lib/skywalking/tracing/carrier.rb index d53472e..2acdd68 100644 --- a/lib/skywalking/tracing/carrier.rb +++ b/lib/skywalking/tracing/carrier.rb @@ -21,7 +21,7 @@ module Tracing class Carrier < CarrierItem attr_reader :trace_id, :correlation_carrier, :service_instance, :endpoint, :segment_id, :span_id, - :peer, :items, :iter_index + :peer, :items, :iter_index, :service def initialize( trace_id: '', diff --git a/lib/skywalking/tracing/constants.rb b/lib/skywalking/tracing/constants.rb index 6c3422c..ebffd1c 100644 --- a/lib/skywalking/tracing/constants.rb +++ b/lib/skywalking/tracing/constants.rb @@ -17,8 +17,10 @@ module Skywalking module Tracing module Component Unknown = 0 - Http = 2 + HttpClient = 2 Redis = 7 + Memcached = 20 + Elasticsearch = 47 General = 12000 Sinatra = 12001 end diff --git a/lib/skywalking/tracing/segment.rb b/lib/skywalking/tracing/segment.rb index 258892a..101fe00 100644 --- a/lib/skywalking/tracing/segment.rb +++ b/lib/skywalking/tracing/segment.rb @@ -32,7 +32,7 @@ def archive(span) end def relate(trace_id) - @related_traces.shift if @related_traces.first.is_a?(Utils::IDGen.new) + @related_traces.pop if @related_traces[0].is_a?(Utils::IDGen) @related_traces << trace_id end end diff --git a/lib/skywalking/tracing/span.rb b/lib/skywalking/tracing/span.rb index 1f18760..4eb1108 100644 --- a/lib/skywalking/tracing/span.rb +++ b/lib/skywalking/tracing/span.rb @@ -104,6 +104,7 @@ def extract(carrier) ref = SegmentRef.new(carrier) @refs << ref unless @refs.include?(ref) + self end end diff --git a/lib/skywalking/tracing/span_context.rb b/lib/skywalking/tracing/span_context.rb index 6dff1a5..fa882cc 100644 --- a/lib/skywalking/tracing/span_context.rb +++ b/lib/skywalking/tracing/span_context.rb @@ -201,9 +201,9 @@ def current_context end end - def new_exit_span(operation:, peer: nil, component: nil, &block) + def new_exit_span(operation:, peer: nil, component: nil, inherit: nil, &block) context = current_context - span = context.new_exit_span(operation, peer, component: component) + span = context.new_exit_span(operation, peer, component: component, inherit: inherit) span&.start begin diff --git a/lib/skywalking/tracing/tag.rb b/lib/skywalking/tracing/tag.rb index 2cf348d..2bf45c7 100644 --- a/lib/skywalking/tracing/tag.rb +++ b/lib/skywalking/tracing/tag.rb @@ -53,6 +53,13 @@ def initialize(val) end end + class TagDbStatement < Tag + def initialize(val) + super + @key = 'db.statement' + end + end + class TagCacheType < Tag def initialize(val) super @@ -80,5 +87,12 @@ def initialize(val) @key = 'cache.key' end end + + class TagCacheMiss < Tag + def initialize(val) + super + @key = 'cache.miss' + end + end end end diff --git a/lib/skywalking/version.rb b/lib/skywalking/version.rb index feec054..e4d1e85 100644 --- a/lib/skywalking/version.rb +++ b/lib/skywalking/version.rb @@ -14,5 +14,5 @@ # limitations under the License. module Skywalking - VERSION = "0.0.0.beta1".freeze + VERSION = "0.0.0.beta2".freeze end diff --git a/skywalking.gemspec b/skywalking.gemspec index cf46aa3..fb0c5b7 100644 --- a/skywalking.gemspec +++ b/skywalking.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] # Communication with OAP - spec.add_dependency 'grpc', '1.68.1' + spec.add_dependency 'grpc', '~> 1.68.0' # Base dev dependency spec.add_development_dependency 'bundler', '~> 2.0' @@ -53,9 +53,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bigdecimal', '3.1.5' # E2E test dependency - spec.add_development_dependency 'redis', '~> 5.0' - spec.add_development_dependency 'sinatra', '~> 4.1' spec.add_development_dependency 'testcontainers-compose', '~> 0.2.0' spec.add_development_dependency 'faraday', '~> 2.12' - spec.add_development_dependency 'rspec-wait', '~> 1.0' end diff --git a/spec/scenarios/common/compose_context.rb b/spec/scenarios/common/compose_context.rb index 741291d..d771d95 100644 --- a/spec/scenarios/common/compose_context.rb +++ b/spec/scenarios/common/compose_context.rb @@ -30,7 +30,7 @@ before(:each) do compose.start - compose.wait_for_http(url: client_url, timeout: 600) + compose.wait_for_http(url: client_url, timeout: 800) end after(:each) do diff --git a/spec/scenarios/elasticsearch/docker-compose.yml b/spec/scenarios/elasticsearch/docker-compose.yml new file mode 100644 index 0000000..880ec79 --- /dev/null +++ b/spec/scenarios/elasticsearch/docker-compose.yml @@ -0,0 +1,72 @@ +# +# 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: + oap: + extends: + file: ../common/base-compose.yml + service: oap + networks: + - gem + + elasticsearch: + image: elasticsearch:8.0.0 + hostname: elasticsearch + ports: + - "9200:9200" + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - xpack.security.enabled=false + networks: + - gem + healthcheck: + test: [ "CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1" ] + interval: 5s + timeout: 60s + retries: 120 + + service: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - "8080:8080" + volumes: + - .:/app/spec/scenarios/elasticsearch + environment: + SW_AGENT_SERVICE_NAME: "elasticsearch" + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8080" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + - "sh" + - "-c" + - "gem install sinatra rackup puma elasticsearch && ruby /app/spec/scenarios/elasticsearch/elasticsearch.rb" + depends_on: + oap: + condition: service_healthy + networks: + - gem + +networks: + gem: \ No newline at end of file diff --git a/spec/scenarios/elasticsearch/elasticsearch.rb b/spec/scenarios/elasticsearch/elasticsearch.rb new file mode 100644 index 0000000..eb788d9 --- /dev/null +++ b/spec/scenarios/elasticsearch/elasticsearch.rb @@ -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. + +require_relative '../../../lib/skywalking' +require 'sinatra' +require 'elasticsearch' + +Skywalking.start(disable_plugins: 'net_http') + +def create_index(client) + client.indices.create(index: 'test_index', body: { mappings: { properties: { name: { type: 'text' } } } }) + puts "Index created." +end + +def add_document(client, id, name) + client.index(index: 'test_index', id: id, body: { name: name }) + puts "Document added: #{id} => #{name}" +end + +def get_document(client, id) + response = client.get(index: 'test_index', id: id) + puts "Document retrieved: #{response['_source']}" +rescue Elasticsearch::Transport::Transport::Errors::NotFound + puts "Document not found." +end + +def delete_document(client, id) + client.delete(index: 'test_index', id: id) + puts "Document deleted: #{id}" +end + +get "/execute" do + client = Elasticsearch::Client.new(hosts: ['http://elasticsearch:9200'], log: true, + transport_options: { request: { timeout: 5 } }) + create_index(client) + add_document(client, '1', 'Document 1') + get_document(client, '1') + delete_document(client, '1') +end + +set :bind, '0.0.0.0' +set :port, 8080 \ No newline at end of file diff --git a/spec/scenarios/elasticsearch/elasticsearch_spec.rb b/spec/scenarios/elasticsearch/elasticsearch_spec.rb new file mode 100644 index 0000000..7059ed9 --- /dev/null +++ b/spec/scenarios/elasticsearch/elasticsearch_spec.rb @@ -0,0 +1,29 @@ +# 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. + +require_relative '../common/common_spec_helper' +require_relative '../common/compose_context' + +RSpec.describe "Elasticsearch" do + include CommonSpecHelper + include_context 'compose' + include_context 'scenario value' + + let(:root_dir) { File.expand_path(__dir__) } + + it 'test elasticsearch plugin' do + test_plugin('elasticsearch') + end +end \ No newline at end of file diff --git a/spec/scenarios/elasticsearch/expected.yml b/spec/scenarios/elasticsearch/expected.yml new file mode 100644 index 0000000..4dffeba --- /dev/null +++ b/spec/scenarios/elasticsearch/expected.yml @@ -0,0 +1,94 @@ +# 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: elasticsearch + segmentSize: ge 1 + segments: + - segmentId: not null + spans: + - operationName: Elasticsearch/PUT/test_index + parentSpanId: 0 + spanId: 1 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 47 + isError: false + spanType: Exit + peer: http://elasticsearch:9200 + skipAnalysis: false + tags: + - { key: db.type, value: Elasticsearch } + - { key: db.statement, value: '[{:params=>{}}, {:body=>{:mappings=>{:properties=>{:name=>{:type=>"text"}}}}}]' } + - operationName: Elasticsearch/PUT/test_index/_doc/1 + parentSpanId: 0 + spanId: 2 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 47 + isError: false + spanType: Exit + peer: http://elasticsearch:9200 + skipAnalysis: false + tags: + - { key: db.type, value: Elasticsearch } + - { key: db.statement, value: '[{:params=>{}}, {:body=>{:name=>"Document 1"}}]' } + - operationName: Elasticsearch/GET/test_index/_doc/1 + parentSpanId: 0 + spanId: 3 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 47 + isError: false + spanType: Exit + peer: http://elasticsearch:9200 + skipAnalysis: false + tags: + - { key: db.type, value: Elasticsearch } + - { key: db.statement, value: '[{:params=>{}}]' } + - operationName: Elasticsearch/DELETE/test_index/_doc/1 + parentSpanId: 0 + spanId: 4 + spanLayer: Database + startTime: gt 0 + endTime: gt 0 + componentId: 47 + isError: false + spanType: Exit + peer: http://elasticsearch:9200 + skipAnalysis: false + tags: + - { key: db.type, value: Elasticsearch } + - { key: db.statement, value: '[{:params=>{}}]' } + - operationName: GET:/execute + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 12001 + isError: false + spanType: Entry + peer: localhost:8080 + skipAnalysis: false + tags: + - { key: http.method, value: GET } + - { key: http.url, value: /execute } +meterItems: [ ] +logItems: [ ] \ No newline at end of file diff --git a/spec/scenarios/memcached/docker-compose.yml b/spec/scenarios/memcached/docker-compose.yml new file mode 100644 index 0000000..18dacdb --- /dev/null +++ b/spec/scenarios/memcached/docker-compose.yml @@ -0,0 +1,67 @@ +# +# 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: + oap: + extends: + file: ../common/base-compose.yml + service: oap + networks: + - gem + + memcached: + image: memcached:1.6 + hostname: memcached + ports: + - "11211:11211" + networks: + - gem + healthcheck: + test: [ "CMD", "nc", "-z", "localhost", "11211" ] + interval: 5s + timeout: 60s + retries: 120 + + service: + extends: + file: ../common/base-compose.yml + service: agent + ports: + - "8080:8080" + volumes: + - .:/app/spec/scenarios/memcached + environment: + SW_AGENT_SERVICE_NAME: "memcached" + healthcheck: + test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/8080" ] + interval: 5s + timeout: 60s + retries: 120 + entrypoint: + - "sh" + - "-c" + - "gem install sinatra rackup puma dalli && ruby /app/spec/scenarios/memcached/memcached.rb" + depends_on: + oap: + condition: service_healthy + networks: + - gem + +networks: + gem: \ No newline at end of file diff --git a/spec/scenarios/memcached/expected.yml b/spec/scenarios/memcached/expected.yml new file mode 100644 index 0000000..37c4111 --- /dev/null +++ b/spec/scenarios/memcached/expected.yml @@ -0,0 +1,81 @@ +# 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: memcached + segmentSize: ge 1 + segments: + - segmentId: not null + spans: + - operationName: Memcached/set + parentSpanId: 0 + spanId: 1 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 20 + isError: false + spanType: Exit + peer: memcached:11211 + skipAnalysis: false + tags: + - {key: cache.type, value: Memcached} + - {key: cache.key, value: sw_key} + - operationName: Memcached/get + parentSpanId: 0 + spanId: 2 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 20 + isError: false + spanType: Exit + peer: memcached:11211 + skipAnalysis: false + tags: + - {key: cache.type, value: Memcached} + - {key: cache.key, value: sw_key} + - {key: cache.miss, value: 'false'} + - operationName: Memcached/delete + parentSpanId: 0 + spanId: 3 + spanLayer: Cache + startTime: gt 0 + endTime: gt 0 + componentId: 20 + isError: false + spanType: Exit + peer: memcached:11211 + skipAnalysis: false + tags: + - {key: cache.type, value: Memcached} + - {key: cache.key, value: sw_key} + - operationName: GET:/execute + parentSpanId: -1 + spanId: 0 + spanLayer: Http + startTime: gt 0 + endTime: gt 0 + componentId: 12001 + isError: false + spanType: Entry + peer: localhost:8080 + skipAnalysis: false + tags: + - {key: http.method, value: GET} + - {key: http.url, value: /execute} +meterItems: [] +logItems: [] \ No newline at end of file diff --git a/spec/scenarios/memcached/memcached.rb b/spec/scenarios/memcached/memcached.rb new file mode 100644 index 0000000..fbe493a --- /dev/null +++ b/spec/scenarios/memcached/memcached.rb @@ -0,0 +1,31 @@ +# 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. + +require_relative '../../../lib/skywalking' +require 'sinatra' +require 'dalli' + +Skywalking.start + +get "/execute" do + client = Dalli::Client.new('memcached:11211', { namespace: "sw", compress: true }) + client.set('sw_key', 'sw_value') + value = client.get('sw_key') + p "The value for 'sw_key' is: #{value}" + client.delete('sw_key') +end + +set :bind, '0.0.0.0' +set :port, 8080 \ No newline at end of file diff --git a/spec/scenarios/memcached/memcached_spec.rb b/spec/scenarios/memcached/memcached_spec.rb new file mode 100644 index 0000000..1f00d92 --- /dev/null +++ b/spec/scenarios/memcached/memcached_spec.rb @@ -0,0 +1,29 @@ +# 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. + +require_relative '../common/common_spec_helper' +require_relative '../common/compose_context' + +RSpec.describe "Memcached" do + include CommonSpecHelper + include_context 'compose' + include_context 'scenario value' + + let(:root_dir) { File.expand_path(__dir__) } + + it 'test memcached plugin' do + test_plugin('memcached') + end +end \ No newline at end of file