├── .ci ├── .codecov.yml ├── .exclude.yml ├── .framework.yml ├── .main_framework.yml ├── .ruby.yml ├── docker │ └── jruby │ │ ├── 11-jdk │ │ └── Dockerfile │ │ ├── 12-jdk │ │ └── Dockerfile │ │ ├── 13-jdk │ │ └── Dockerfile │ │ ├── 7-jdk │ │ └── Dockerfile │ │ ├── 8-jdk │ │ └── Dockerfile │ │ ├── README.md │ │ ├── run.sh │ │ └── test.sh ├── scripts │ ├── bench.sh │ └── install-build-system.sh └── updatecli │ └── values.d │ ├── apm-data-spec.yml │ ├── apm-gherkin.yml │ ├── apm-json-specs.yml │ ├── scm.yml │ └── update-compose.yml ├── .github ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml ├── labeler-config.yml └── workflows │ ├── README.md │ ├── addToProject.yml │ ├── ci-docs.yml │ ├── ci.yml │ ├── docs-build.yml │ ├── docs-cleanup.yml │ ├── github-commands-comment.yml │ ├── labeler.yml │ ├── microbenchmark.yml │ ├── release.yml │ ├── run-matrix.yml │ ├── test-reporter.yml │ └── updatecli.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .rspec ├── .rubocop.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── SECURITY.md ├── bench ├── .gitignore ├── app.rb ├── benchmark.rb ├── report.rb ├── rubyprof.rb ├── sql.rb ├── stackprof.rb └── tmp │ └── .gitkeep ├── bin ├── build_docs ├── console ├── dev ├── run-bdd ├── run-tests ├── setup └── with_framework ├── docker-compose.yml ├── docs ├── docset.yml ├── reference │ ├── advanced-topics.md │ ├── api-reference.md │ ├── configuration.md │ ├── context.md │ ├── custom-instrumentation.md │ ├── getting-started-rack.md │ ├── getting-started-rails.md │ ├── graphql.md │ ├── images │ │ └── dynamic-config.svg │ ├── index.md │ ├── logs.md │ ├── metrics.md │ ├── opentracing-api.md │ ├── performance-tuning.md │ ├── set-up-apm-ruby-agent.md │ ├── supported-technologies.md │ ├── toc.yml │ └── upgrading.md └── release-notes │ ├── index.md │ ├── known-issues.md │ └── toc.yml ├── elastic-apm.gemspec ├── features ├── api_key.feature ├── azure_app_service_metadata.feature ├── azure_functions_metadata.feature ├── otel_bridge.feature ├── outcome.feature ├── step_definitions │ ├── api_key_steps.rb │ ├── common_steps.rb │ └── outcome_steps.rb ├── support │ └── env.rb └── user_agent.feature ├── lib ├── elastic-apm.rb ├── elastic_apm.rb └── elastic_apm │ ├── agent.rb │ ├── central_config.rb │ ├── central_config │ └── cache_control.rb │ ├── child_durations.rb │ ├── config.rb │ ├── config │ ├── bytes.rb │ ├── duration.rb │ ├── log_level_map.rb │ ├── options.rb │ ├── regexp_list.rb │ ├── round_float.rb │ ├── round_float_hash_value.rb │ ├── server_info.rb │ └── wildcard_pattern_list.rb │ ├── context.rb │ ├── context │ ├── request.rb │ ├── request │ │ ├── socket.rb │ │ └── url.rb │ ├── response.rb │ └── user.rb │ ├── context_builder.rb │ ├── deprecations.rb │ ├── error.rb │ ├── error │ ├── exception.rb │ └── log.rb │ ├── error_builder.rb │ ├── fields.rb │ ├── grape.rb │ ├── graphql.rb │ ├── grpc.rb │ ├── instrumenter.rb │ ├── internal_error.rb │ ├── logging.rb │ ├── metadata.rb │ ├── metadata │ ├── cloud_info.rb │ ├── process_info.rb │ ├── service_info.rb │ ├── system_info.rb │ └── system_info │ │ └── container_info.rb │ ├── metrics.rb │ ├── metrics │ ├── breakdown_set.rb │ ├── cpu_mem_set.rb │ ├── jvm_set.rb │ ├── metric.rb │ ├── set.rb │ ├── span_scoped_set.rb │ ├── transaction_set.rb │ └── vm_set.rb │ ├── metricset.rb │ ├── middleware.rb │ ├── naively_hashable.rb │ ├── normalizers.rb │ ├── normalizers │ ├── grape.rb │ ├── grape │ │ └── endpoint_run.rb │ ├── rails.rb │ └── rails │ │ ├── action_controller.rb │ │ ├── action_mailer.rb │ │ ├── action_view.rb │ │ └── active_record.rb │ ├── opentracing.rb │ ├── rails.rb │ ├── railtie.rb │ ├── resque.rb │ ├── sinatra.rb │ ├── span.rb │ ├── span │ ├── context.rb │ └── context │ │ ├── db.rb │ │ ├── destination.rb │ │ ├── http.rb │ │ ├── links.rb │ │ ├── message.rb │ │ └── service.rb │ ├── span_helpers.rb │ ├── spies.rb │ ├── spies │ ├── action_dispatch.rb │ ├── azure_storage_table.rb │ ├── delayed_job.rb │ ├── dynamo_db.rb │ ├── elasticsearch.rb │ ├── faraday.rb │ ├── http.rb │ ├── json.rb │ ├── mongo.rb │ ├── net_http.rb │ ├── racecar.rb │ ├── rake.rb │ ├── redis.rb │ ├── resque.rb │ ├── s3.rb │ ├── sequel.rb │ ├── shoryuken.rb │ ├── sidekiq.rb │ ├── sinatra.rb │ ├── sneakers.rb │ ├── sns.rb │ ├── sqs.rb │ ├── sucker_punch.rb │ └── tilt.rb │ ├── sql │ ├── signature.rb │ ├── tokenizer.rb │ └── tokens.rb │ ├── stacktrace.rb │ ├── stacktrace │ └── frame.rb │ ├── stacktrace_builder.rb │ ├── subscriber.rb │ ├── trace_context.rb │ ├── trace_context │ ├── traceparent.rb │ └── tracestate.rb │ ├── transaction.rb │ ├── transport │ ├── base.rb │ ├── connection.rb │ ├── connection │ │ ├── http.rb │ │ └── proxy_pipe.rb │ ├── filters.rb │ ├── filters │ │ ├── hash_sanitizer.rb │ │ └── secrets_filter.rb │ ├── headers.rb │ ├── serializers.rb │ ├── serializers │ │ ├── context_serializer.rb │ │ ├── error_serializer.rb │ │ ├── metadata_serializer.rb │ │ ├── metricset_serializer.rb │ │ ├── span_serializer.rb │ │ └── transaction_serializer.rb │ ├── user_agent.rb │ └── worker.rb │ ├── util.rb │ ├── util │ ├── deep_dup.rb │ ├── inflector.rb │ ├── lru_cache.rb │ ├── precision_validator.rb │ └── throttle.rb │ └── version.rb ├── spec ├── Dockerfile ├── build │ ├── entrypoint.sh │ ├── run-bdd.sh │ ├── run-bench.sh │ ├── run-tests.sh │ └── update.sh ├── elastic_apm │ ├── agent_spec.rb │ ├── apm_spec.rb │ ├── central_config │ │ └── cache_control_spec.rb │ ├── central_config_spec.rb │ ├── config │ │ ├── log_level_map_spec.rb │ │ ├── server_info_spec.rb │ │ └── wildcard_pattern_list_spec.rb │ ├── config_spec.rb │ ├── context │ │ ├── request │ │ │ ├── socket_spec.rb │ │ │ └── url_spec.rb │ │ ├── response_spec.rb │ │ └── user_spec.rb │ ├── context_builder_spec.rb │ ├── context_spec.rb │ ├── error │ │ ├── exception_spec.rb │ │ └── log_spec.rb │ ├── error_builder_spec.rb │ ├── error_spec.rb │ ├── fields_spec.rb │ ├── grape_spec.rb │ ├── grpc_spec.rb │ ├── instrumenter_spec.rb │ ├── logging_spec.rb │ ├── metadata │ │ ├── cloud_info_spec.rb │ │ ├── process_info_spec.rb │ │ ├── service_info_spec.rb │ │ ├── system_info │ │ │ └── container_info_spec.rb │ │ └── system_info_spec.rb │ ├── metadata_spec.rb │ ├── metrics │ │ ├── cpu_mem_set_spec.rb │ │ ├── jvm_set_spec.rb │ │ ├── metric_spec.rb │ │ ├── set_spec.rb │ │ ├── span_scoped_set_spec.rb │ │ └── vm_set_spec.rb │ ├── metrics_spec.rb │ ├── metricset_spec.rb │ ├── middleware_spec.rb │ ├── naively_hashable_spec.rb │ ├── normalizers │ │ ├── action_controller_spec.rb │ │ ├── action_view_spec.rb │ │ └── active_record_spec.rb │ ├── normalizers_spec.rb │ ├── rails_spec.rb │ ├── sinatra_spec.rb │ ├── span │ │ ├── context │ │ │ ├── destination_spec.rb │ │ │ └── service_spec.rb │ │ └── context_spec.rb │ ├── span_helpers_spec.rb │ ├── span_spec.rb │ ├── spies │ │ ├── azure_storage_table_spec.rb │ │ ├── delayed_job_spec.rb │ │ ├── dynamo_db_spec.rb │ │ ├── elasticsearch_spec.rb │ │ ├── faraday_spec.rb │ │ ├── http_spec.rb │ │ ├── mongo_spec.rb │ │ ├── net_http_spec.rb │ │ ├── racecar_spec.rb │ │ ├── rake_spec.rb │ │ ├── redis_spec.rb │ │ ├── resque_spec.rb │ │ ├── s3_spec.rb │ │ ├── sequel_spec.rb │ │ ├── shoryuken_spec.rb │ │ ├── sidekiq_spec.rb │ │ ├── sinatra_spec.rb │ │ ├── sneakers_spec.rb │ │ ├── sns_spec.rb │ │ ├── sqs_spec.rb │ │ ├── sucker_punch_spec.rb │ │ └── tilt_spec.rb │ ├── sql │ │ ├── signature_spec.rb │ │ └── tokenizer_spec.rb │ ├── stacktrace_builder_spec.rb │ ├── subscriber_spec.rb │ ├── trace_context │ │ ├── traceparent_spec.rb │ │ └── tracestate_spec.rb │ ├── trace_context_spec.rb │ ├── transaction_spec.rb │ ├── transport │ │ ├── base_spec.rb │ │ ├── connection │ │ │ ├── http_spec.rb │ │ │ └── proxy_pipe_spec.rb │ │ ├── connection_spec.rb │ │ ├── filters │ │ │ ├── hash_sanitizer_spec.rb │ │ │ └── secrets_filter_spec.rb │ │ ├── filters_spec.rb │ │ ├── headers_spec.rb │ │ ├── serializers │ │ │ ├── context_serializer_spec.rb │ │ │ ├── error_serializer_spec.rb │ │ │ ├── metadata_serializer_spec.rb │ │ │ ├── metricset_serializer_spec.rb │ │ │ ├── span_serializer_spec.rb │ │ │ └── transaction_serializer_spec.rb │ │ ├── serializers_spec.rb │ │ ├── user_agent_spec.rb │ │ └── worker_spec.rb │ ├── util │ │ ├── deep_dup_spec.rb │ │ ├── lru_cache_spec.rb │ │ ├── precision_validator_spec.rb │ │ └── throttle_spec.rb │ └── util_spec.rb ├── elastic_apm_spec.rb ├── entrypoint.sh ├── fixtures │ ├── container_metadata_discovery.json │ ├── elastic_apm.yml │ ├── elastic_apm_erb.yml │ ├── error.json │ ├── log.json │ ├── metadata.json │ ├── metricset.json │ ├── proc_meminfo │ ├── proc_meminfo_wheezy │ ├── proc_self_stat │ ├── proc_stat_debian │ ├── proc_stat_rhel │ ├── service_resource_inference.json │ ├── span.json │ ├── span_types.json │ ├── sql_signature_examples.json │ ├── sql_token_examples.json │ ├── transaction.json │ ├── unknown_option.yml │ ├── w3c_distributed_tracing.json │ └── wildcard_matcher_tests.json ├── integration │ ├── grape_spec.rb │ ├── graphql_spec.rb │ ├── json_spy_spec.rb │ ├── mongo_spec.rb │ ├── opentracing_spec.rb │ ├── rails_console_spec.rb │ ├── rails_grape_spec.rb │ ├── rails_logger_ecs_spec.rb │ ├── rails_logger_spec.rb │ ├── rails_metrics_stress_spec.rb │ ├── rails_paths_spec.rb │ ├── rails_spec.rb │ ├── sinatra_spec.rb │ └── skip_require_patch_spec.rb ├── integration_helper.rb ├── scripts │ ├── benchmarks.sh │ ├── coverage_converge.sh │ ├── coverage_entrypoint.sh │ ├── features.sh │ └── spec.sh ├── spec_helper.rb └── support │ ├── delegate_matcher.rb │ ├── event_collector.rb │ ├── exception_helpers.rb │ ├── helloworld_pb.rb │ ├── helloworld_services_pb.rb │ ├── intercept.rb │ ├── match_json_schema_matcher.rb │ ├── mock_intake.rb │ ├── mock_time.rb │ ├── platform_helpers.rb │ ├── rails_test_helpers.rb │ ├── with_agent.rb │ └── with_env.rb └── updatecli-compose.yaml /.ci/.codecov.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Tuple of ruby version and frameworks which are in charge to send data to codecov. 3 | # # is the separator between the ruby version and the framework. 4 | ENABLED: 5 | - ruby:2.7#rails-6.1 6 | -------------------------------------------------------------------------------- /.ci/.framework.yml: -------------------------------------------------------------------------------- 1 | FRAMEWORK: 2 | - rails-7.2 3 | - rails-6.1 4 | - rails-5.2 5 | - rails-4.2 6 | 7 | - sinatra-2.2 8 | - sinatra-1.4 9 | 10 | - grape-1.6 11 | -------------------------------------------------------------------------------- /.ci/.main_framework.yml: -------------------------------------------------------------------------------- 1 | FRAMEWORK: 2 | #- rails-main 3 | - sinatra-main 4 | - grape-master 5 | -------------------------------------------------------------------------------- /.ci/.ruby.yml: -------------------------------------------------------------------------------- 1 | VERSION: 2 | - ruby:3.4 3 | - ruby:3.1 4 | - ruby:2.7 5 | - ruby:2.6 6 | - ruby:2.4 7 | - jruby:9.2 8 | - elasticobservability/jruby:9.2-13-jdk 9 | - elasticobservability/jruby:9.2-11-jdk 10 | - elasticobservability/jruby:9.2-8-jdk 11 | -------------------------------------------------------------------------------- /.ci/docker/jruby/11-jdk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:11-jdk 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y libc6-dev --no-install-recommends \ 5 | && apt-get install -y gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | ENV JRUBY_VERSION 9.2.16.0 9 | 10 | RUN mkdir -p /opt/jruby \ 11 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz.sha256 -o /tmp/jruby.sha256 \ 12 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ 13 | && echo "$(cat /tmp/jruby.sha256) */tmp/jruby.tar.gz" | sha256sum -c - \ 14 | && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ 15 | && update-alternatives --install /usr/local/bin/ruby ruby /opt/jruby/bin/jruby 1 16 | 17 | # set the jruby binaries in the path 18 | ENV PATH /opt/jruby/bin:$PATH 19 | 20 | # skip installing gem documentation 21 | RUN mkdir -p /opt/jruby/etc \ 22 | && { \ 23 | echo 'install: --no-document'; \ 24 | echo 'update: --no-document'; \ 25 | } >> /opt/jruby/etc/gemrc 26 | 27 | # install bundler, gem requires bash to work 28 | # https://github.com/rubygems/rubygems/issues/2534#issuecomment-448843746 29 | RUN gem update --system --conservative || (gem i "rubygems-update:~>2.7" --no-document && update_rubygems) \ 30 | && gem install bundler:2.3.26 rake net-telnet xmlrpc tzinfo-data 31 | 32 | # install things globally, for great justice 33 | # and don't create ".bundle" in all our apps 34 | ENV GEM_HOME /usr/local/bundle 35 | ENV BUNDLE_PATH="$GEM_HOME" \ 36 | BUNDLE_BIN="$GEM_HOME/bin" \ 37 | BUNDLE_SILENCE_ROOT_WARNING=1 \ 38 | BUNDLE_APP_CONFIG="$GEM_HOME" 39 | ENV PATH $BUNDLE_BIN:$PATH 40 | RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \ 41 | && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" 42 | 43 | RUN useradd -rm -d /home/jruby -u 1001 jruby 44 | USER jruby 45 | WORKDIR /home/jruby 46 | CMD [ "irb" ] 47 | -------------------------------------------------------------------------------- /.ci/docker/jruby/12-jdk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk:12-jdk-openj9 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y libc6-dev git tcc netbase --no-install-recommends \ 5 | && apt-get install -y gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | ENV JRUBY_VERSION 9.3.1.0 9 | 10 | RUN mkdir -p /opt/jruby \ 11 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz.sha256 -o /tmp/jruby.sha256 \ 12 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ 13 | && echo "$(cat /tmp/jruby.sha256) */tmp/jruby.tar.gz" | sha256sum -c - \ 14 | && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ 15 | && update-alternatives --install /usr/local/bin/ruby ruby /opt/jruby/bin/jruby 1 16 | 17 | # set the jruby binaries in the path 18 | ENV PATH /opt/jruby/bin:$PATH 19 | 20 | # skip installing gem documentation 21 | RUN mkdir -p /opt/jruby/etc \ 22 | && { \ 23 | echo 'install: --no-document'; \ 24 | echo 'update: --no-document'; \ 25 | } >> /opt/jruby/etc/gemrc 26 | 27 | # install bundler, gem requires bash to work 28 | RUN gem install bundler rake net-telnet xmlrpc tzinfo-data 29 | 30 | # install things globally, for great justice 31 | # and don't create ".bundle" in all our apps 32 | ENV GEM_HOME /usr/local/bundle 33 | ENV BUNDLE_PATH="$GEM_HOME" \ 34 | BUNDLE_BIN="$GEM_HOME/bin" \ 35 | BUNDLE_SILENCE_ROOT_WARNING=1 \ 36 | BUNDLE_APP_CONFIG="$GEM_HOME" 37 | ENV PATH $BUNDLE_BIN:$PATH 38 | RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \ 39 | && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" 40 | 41 | RUN useradd -rm -d /home/jruby -u 1001 jruby 42 | USER jruby 43 | WORKDIR /home/jruby 44 | CMD [ "irb" ] 45 | -------------------------------------------------------------------------------- /.ci/docker/jruby/13-jdk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:13-jdk-slim 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y libc6-dev git tcc curl netbase --no-install-recommends \ 5 | && apt-get install -y gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | ENV JRUBY_VERSION 9.3.1.0 9 | 10 | RUN mkdir -p /opt/jruby \ 11 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz.sha256 -o /tmp/jruby.sha256 \ 12 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ 13 | && echo "$(cat /tmp/jruby.sha256) */tmp/jruby.tar.gz" | sha256sum -c - \ 14 | && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ 15 | && update-alternatives --install /usr/local/bin/ruby ruby /opt/jruby/bin/jruby 1 16 | 17 | # set the jruby binaries in the path 18 | ENV PATH /opt/jruby/bin:$PATH 19 | 20 | # skip installing gem documentation 21 | RUN mkdir -p /opt/jruby/etc \ 22 | && { \ 23 | echo 'install: --no-document'; \ 24 | echo 'update: --no-document'; \ 25 | } >> /opt/jruby/etc/gemrc 26 | 27 | # install bundler, gem requires bash to work 28 | RUN gem install bundler rake net-telnet xmlrpc tzinfo-data 29 | 30 | # install things globally, for great justice 31 | # and don't create ".bundle" in all our apps 32 | ENV GEM_HOME /usr/local/bundle 33 | ENV BUNDLE_PATH="$GEM_HOME" \ 34 | BUNDLE_BIN="$GEM_HOME/bin" \ 35 | BUNDLE_SILENCE_ROOT_WARNING=1 \ 36 | BUNDLE_APP_CONFIG="$GEM_HOME" 37 | ENV PATH $BUNDLE_BIN:$PATH 38 | RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \ 39 | && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" 40 | 41 | RUN useradd -rm -d /home/jruby -u 1001 jruby 42 | USER jruby 43 | WORKDIR /home/jruby 44 | CMD [ "irb" ] 45 | -------------------------------------------------------------------------------- /.ci/docker/jruby/7-jdk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:7-jdk 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y libc6-dev --no-install-recommends \ 5 | && apt-get install --force-yes -y gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | ENV JRUBY_VERSION 9.3.1.0 9 | 10 | RUN mkdir -p /opt/jruby \ 11 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz.sha256 -o /tmp/jruby.sha256 \ 12 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ 13 | && echo "$(cat /tmp/jruby.sha256) */tmp/jruby.tar.gz" | sha256sum -c - \ 14 | && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ 15 | && update-alternatives --install /usr/local/bin/ruby ruby /opt/jruby/bin/jruby 1 16 | 17 | # set the jruby binaries in the path 18 | ENV PATH /opt/jruby/bin:$PATH 19 | 20 | # skip installing gem documentation 21 | RUN mkdir -p /opt/jruby/etc \ 22 | && { \ 23 | echo 'install: --no-document'; \ 24 | echo 'update: --no-document'; \ 25 | } >> /opt/jruby/etc/gemrc 26 | 27 | # install bundler, gem requires bash to work 28 | RUN gem install bundler rake net-telnet xmlrpc tzinfo-data 29 | 30 | # install things globally, for great justice 31 | # and don't create ".bundle" in all our apps 32 | ENV GEM_HOME /usr/local/bundle 33 | ENV BUNDLE_PATH="$GEM_HOME" \ 34 | BUNDLE_BIN="$GEM_HOME/bin" \ 35 | BUNDLE_SILENCE_ROOT_WARNING=1 \ 36 | BUNDLE_APP_CONFIG="$GEM_HOME" 37 | ENV PATH $BUNDLE_BIN:$PATH 38 | RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \ 39 | && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" 40 | 41 | RUN useradd -rm -d /home/jruby -u 1001 jruby 42 | USER jruby 43 | WORKDIR /home/jruby 44 | CMD [ "irb" ] 45 | -------------------------------------------------------------------------------- /.ci/docker/jruby/8-jdk/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8-jdk 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y libc6-dev --no-install-recommends \ 5 | && apt-get install -y gcc \ 6 | && rm -rf /var/lib/apt/lists/* 7 | 8 | ENV JRUBY_VERSION 9.2.14.0 9 | 10 | RUN mkdir -p /opt/jruby \ 11 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz.sha256 -o /tmp/jruby.sha256 \ 12 | && curl -fSL https://repo1.maven.org/maven2/org/jruby/jruby-dist/${JRUBY_VERSION}/jruby-dist-${JRUBY_VERSION}-bin.tar.gz -o /tmp/jruby.tar.gz \ 13 | && echo "$(cat /tmp/jruby.sha256) */tmp/jruby.tar.gz" | sha256sum -c - \ 14 | && tar -zx --strip-components=1 -f /tmp/jruby.tar.gz -C /opt/jruby \ 15 | && update-alternatives --install /usr/local/bin/ruby ruby /opt/jruby/bin/jruby 1 16 | 17 | # set the jruby binaries in the path 18 | ENV PATH /opt/jruby/bin:$PATH 19 | 20 | # skip installing gem documentation 21 | RUN mkdir -p /opt/jruby/etc \ 22 | && { \ 23 | echo 'install: --no-document'; \ 24 | echo 'update: --no-document'; \ 25 | } >> /opt/jruby/etc/gemrc 26 | 27 | # install bundler, gem requires bash to work 28 | # https://github.com/rubygems/rubygems/issues/2534#issuecomment-448843746 29 | RUN gem update --system --conservative || (gem i "rubygems-update:~>2.7" --no-document && update_rubygems) \ 30 | && gem install bundler:2.3.26 rake net-telnet xmlrpc tzinfo-data 31 | 32 | # install things globally, for great justice 33 | # and don't create ".bundle" in all our apps 34 | ENV GEM_HOME /usr/local/bundle 35 | ENV BUNDLE_PATH="$GEM_HOME" \ 36 | BUNDLE_BIN="$GEM_HOME/bin" \ 37 | BUNDLE_SILENCE_ROOT_WARNING=1 \ 38 | BUNDLE_APP_CONFIG="$GEM_HOME" 39 | ENV PATH $BUNDLE_BIN:$PATH 40 | RUN mkdir -p "$GEM_HOME" "$BUNDLE_BIN" \ 41 | && chmod 777 "$GEM_HOME" "$BUNDLE_BIN" 42 | 43 | RUN useradd -rm -d /home/jruby -u 1001 jruby 44 | USER jruby 45 | WORKDIR /home/jruby 46 | CMD [ "irb" ] 47 | -------------------------------------------------------------------------------- /.ci/docker/jruby/README.md: -------------------------------------------------------------------------------- 1 | # JRuby docker images 2 | 3 | Builds jruby docker images. 4 | 5 | ## Build 6 | 7 | To build the images run 8 | 9 | ```bash 10 | ./run.sh --action build 11 | ``` 12 | 13 | You can set your own docker registry with the flag `--registry x.y.z/org` 14 | 15 | You can exclude what images can be built with the flag `--exclude jdk-7`. Multiple usages of `--exclude` are accepted. 16 | 17 | ## Test 18 | 19 | To test the images run 20 | 21 | ```bash 22 | ./run.sh --action test 23 | ``` 24 | 25 | ## Push 26 | 27 | To push the images run 28 | 29 | ```bash 30 | ./run.sh --action push 31 | ``` 32 | -------------------------------------------------------------------------------- /.ci/docker/jruby/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | image=${1?image is required} 3 | version=${2} 4 | 5 | printf '\tTest %-30s %s\n' ${image} 6 | 7 | if [ -n "$version" ] ; then 8 | test_name="Test java -version '$version'" 9 | docker run -t --rm $image java -version | grep -q "openjdk version \"$version\|1.$version" 10 | TEST_JAVA_VERSION_RESULT=$? 11 | if [[ $TEST_JAVA_VERSION_RESULT -eq 0 ]]; then 12 | printf '\t\t%-40s %s\n' "${test_name}" "PASSED" 13 | else 14 | printf '\t\t%-40s %s\n' "${test_name}" "FAILED" 15 | fi 16 | fi 17 | 18 | test_name="Test Hello World" 19 | # random operation to verify that ruby evaluates the passed string correctly 20 | docker run -t --rm $image jruby -e "foo = 3 * 4; puts foo" | grep -q '12' 21 | TEST_HELLO_WORLD_RESULT=$? 22 | if [[ $TEST_HELLO_WORLD_RESULT -eq 0 ]]; then 23 | printf '\t\t%-40s %s\n' "${test_name}" "PASSED" 24 | else 25 | printf '\t\t%-40s %s\n' "${test_name}" "FAILED" 26 | fi 27 | 28 | ! (( $TEST_JAVA_VERSION_RESULT || $TEST_HELLO_WORLD_RESULT )) 29 | -------------------------------------------------------------------------------- /.ci/scripts/bench.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -eo pipefail 5 | 6 | # Found current script directory 7 | RELATIVE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 8 | 9 | # Found project directory 10 | BASE_PROJECT="$(dirname "$(dirname "${RELATIVE_DIR}")")" 11 | 12 | ## Buildkite specific configuration 13 | if [ "$CI" == "true" ] ; then 14 | # If HOME is not set then use the Buildkite workspace 15 | # that's normally happening when running in the CI 16 | # owned by Elastic. 17 | if [ -z "$HOME" ] ; then 18 | HOME=$BUILDKITE_BUILD_CHECKOUT_PATH 19 | export HOME 20 | fi 21 | 22 | # required when running the benchmark 23 | PATH=$PATH:$HOME/.local/bin 24 | export PATH 25 | 26 | echo 'Docker login is done in the Buildkite hooks' 27 | fi 28 | 29 | # It does not fail so it runs for every single version and then we report the error at the end. 30 | set +e 31 | status=0 32 | 33 | for VERSION in "ruby:3.1" "ruby:3.0" "ruby:2.7" "ruby:2.6" "jruby:9.2" ; do 34 | ## Transform the versions like: 35 | ## jruby:9.1 to jruby-9.1 36 | echo "--- Benchmark for :ruby: ${VERSION}" 37 | OUTPUT_NAME=benchmark-$(echo "${VERSION//:/-}") 38 | 39 | # TBC, maybe a timeout could help so it can run the other versions? 40 | ${BASE_PROJECT}/spec/scripts/benchmarks.sh "${VERSION}" "${OUTPUT_NAME}" 41 | 42 | # Gather error if any 43 | if [ $? -gt 0 ] ; then 44 | status=1 45 | fi 46 | 47 | # Then we ship the data using the helper 48 | sendBenchmark "${ES_USER_SECRET}" "${ES_PASS_SECRET}" "${ES_URL_SECRET}" "${BASE_PROJECT}/spec/${OUTPUT_NAME}.bulk" 49 | done 50 | 51 | # Report status 52 | exit $status 53 | -------------------------------------------------------------------------------- /.ci/scripts/install-build-system.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euxo pipefail 4 | 5 | gem install rake yard rspec 6 | -------------------------------------------------------------------------------- /.ci/updatecli/values.d/apm-data-spec.yml: -------------------------------------------------------------------------------- 1 | apm_schema_specs_path: spec/fixtures 2 | -------------------------------------------------------------------------------- /.ci/updatecli/values.d/apm-gherkin.yml: -------------------------------------------------------------------------------- 1 | apm_gherkin_specs_path: features -------------------------------------------------------------------------------- /.ci/updatecli/values.d/apm-json-specs.yml: -------------------------------------------------------------------------------- 1 | apm_json_specs_path: spec/fixtures 2 | -------------------------------------------------------------------------------- /.ci/updatecli/values.d/scm.yml: -------------------------------------------------------------------------------- 1 | scm: 2 | enabled: true 3 | owner: elastic 4 | repository: apm-agent-ruby 5 | branch: main 6 | commitusingapi: true 7 | # begin update-compose policy values 8 | user: obltmachine 9 | email: obltmachine@users.noreply.github.com 10 | # end update-compose policy values 11 | -------------------------------------------------------------------------------- /.ci/updatecli/values.d/update-compose.yml: -------------------------------------------------------------------------------- 1 | spec: 2 | files: 3 | - "updatecli-compose.yaml" -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## Describe the bug 8 | 9 | ## Steps to reproduce 10 | 16 | 17 | ## Expected behavior 18 | 19 | ## Environment 20 | 21 | - **OS**: 22 | - **Ruby version**: 23 | - **Framework and version**: 24 | - **APM Server version**: 25 | - **Agent version**: 26 | 27 | 28 | ## Additional context 29 | 30 | Add any other context about the problem here. 31 | 32 | - Agent config options 33 |
34 | Click to expand 35 | 36 | ``` 37 | replace this line with your agent config options 38 | remember to mask any sensitive fields like tokens 39 | ``` 40 |
41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | ## Is your feature request related to a problem? Please describe. 8 | 9 | 10 | ## Describe the solution you'd like 11 | 12 | 13 | ## Describe alternatives you've considered 14 | 15 | 16 | ## Additional context 17 | 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | 5 | - package-ecosystem: bundler 6 | directory: "/" 7 | schedule: 8 | interval: "weekly" 9 | day: "sunday" 10 | time: "22:00" 11 | open-pull-requests-limit: 5 12 | ignore: 13 | - dependency-name: sucker_punch 14 | versions: 15 | - "> 2.0" 16 | - dependency-name: i18n 17 | versions: 18 | - 1.8.10 19 | - 1.8.8 20 | - 1.8.9 21 | 22 | # Maintain dependencies for GitHub Actions (/.github/workflows) 23 | - package-ecosystem: "github-actions" 24 | directory: "/" 25 | schedule: 26 | interval: "weekly" 27 | day: "sunday" 28 | time: "22:00" 29 | open-pull-requests-limit: 5 30 | reviewers: 31 | - "elastic/observablt-ci" 32 | labels: 33 | - dependencies 34 | groups: 35 | github-actions: 36 | patterns: 37 | - "*" 38 | -------------------------------------------------------------------------------- /.github/labeler-config.yml: -------------------------------------------------------------------------------- 1 | # add 'agent-ruby' label to all new issues 2 | agent-ruby: 3 | - '.*' 4 | -------------------------------------------------------------------------------- /.github/workflows/addToProject.yml: -------------------------------------------------------------------------------- 1 | name: Auto Assign to Project(s) 2 | 3 | on: 4 | issues: 5 | types: [opened, edited, milestoned] 6 | pull_request_target: 7 | types: [opened, edited, milestoned] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | assign_one_project: 14 | runs-on: ubuntu-latest 15 | name: Assign milestoned to Project 16 | steps: 17 | - name: Get token 18 | id: get_token 19 | uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0 20 | with: 21 | app_id: ${{ secrets.OBS_AUTOMATION_APP_ID }} 22 | private_key: ${{ secrets.OBS_AUTOMATION_APP_PEM }} 23 | permissions: >- 24 | { 25 | "issues": "read", 26 | "organization_projects": "write", 27 | "pull_requests": "read" 28 | } 29 | - name: Assign issues with milestones to project 30 | uses: elastic/assign-one-project-github-action@1.2.2 31 | if: github.event.issue && github.event.issue.milestone 32 | with: 33 | project: 'https://github.com/orgs/elastic/projects/454' 34 | project_id: '5882982' 35 | column_name: 'Planned' 36 | env: 37 | MY_GITHUB_TOKEN: ${{ steps.get_token.outputs.token }} 38 | - name: Assign new pull requests to project 39 | uses: elastic/assign-one-project-github-action@1.2.2 40 | if: github.event.action == 'opened' && github.event.pull_request 41 | with: 42 | project: 'https://github.com/orgs/elastic/projects/454' 43 | project_id: '5882982' 44 | column_name: 'In Progress' 45 | env: 46 | MY_GITHUB_TOKEN: ${{ steps.get_token.outputs.token }} 47 | -------------------------------------------------------------------------------- /.github/workflows/ci-docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow sets the test / all status check to success in case it's a docs only PR and ci.yml is not triggered 3 | # https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/defining-the-mergeability-of-pull-requests/troubleshooting-required-status-checks#handling-skipped-but-required-checks 4 | name: ci # The name must be the same as in ci.yml 5 | 6 | on: 7 | pull_request: 8 | paths-ignore: # This expression needs to match the paths ignored on ci.yml. 9 | - '**' 10 | - '!**/*.md' 11 | - '!**/*.asciidoc' 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | all: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - run: 'echo "No build required"' 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - "**/*.asciidoc" 7 | - "**/*.md" 8 | push: 9 | branches: 10 | - main 11 | paths-ignore: 12 | - "**/*.asciidoc" 13 | - "**/*.md" 14 | 15 | permissions: 16 | contents: read 17 | 18 | jobs: 19 | sanity-checks: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: elastic/oblt-actions/pre-commit@v1 23 | 24 | # Invokes the actual matrix workflow with the provided files. 25 | matrix: 26 | uses: ./.github/workflows/run-matrix.yml 27 | with: 28 | versionsFile: .ci/.ruby.yml 29 | frameworksFile: .ci/.framework.yml 30 | excludedFile: .ci/.exclude.yml 31 | 32 | # Invokes the actual matrix workflow with the provided files. 33 | # In this case it's for the main frameworks. 34 | matrix-main-frameworks: 35 | uses: ./.github/workflows/run-matrix.yml 36 | with: 37 | versionsFile: .ci/.ruby.yml 38 | frameworksFile: .ci/.main_framework.yml 39 | excludedFile: .ci/.exclude.yml 40 | 41 | package: 42 | runs-on: ubuntu-latest 43 | steps: 44 | - uses: actions/checkout@v4 45 | - uses: ruby/setup-ruby@v1 46 | with: 47 | ruby-version: 2.6 48 | - name: Install build system 49 | run: .ci/scripts/install-build-system.sh 50 | - name: Create gem 51 | run: rake build 52 | - uses: actions/upload-artifact@v4 53 | with: 54 | name: package 55 | path: ./pkg/**/* 56 | 57 | all: 58 | if: always() 59 | runs-on: ubuntu-latest 60 | needs: 61 | - sanity-checks 62 | - matrix 63 | - matrix-main-frameworks 64 | - package 65 | steps: 66 | - id: check 67 | uses: elastic/oblt-actions/check-dependent-jobs@v1 68 | with: 69 | jobs: ${{ toJSON(needs) }} 70 | - run: ${{ steps.check.outputs.is-success }} 71 | -------------------------------------------------------------------------------- /.github/workflows/docs-build.yml: -------------------------------------------------------------------------------- 1 | name: docs-build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request_target: ~ 8 | merge_group: ~ 9 | 10 | jobs: 11 | docs-preview: 12 | uses: elastic/docs-builder/.github/workflows/preview-build.yml@main 13 | with: 14 | path-pattern: docs/** 15 | permissions: 16 | deployments: write 17 | id-token: write 18 | contents: read 19 | pull-requests: read 20 | -------------------------------------------------------------------------------- /.github/workflows/docs-cleanup.yml: -------------------------------------------------------------------------------- 1 | name: docs-cleanup 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - closed 7 | 8 | jobs: 9 | docs-preview: 10 | uses: elastic/docs-builder/.github/workflows/preview-cleanup.yml@main 11 | permissions: 12 | contents: none 13 | id-token: write 14 | deployments: write 15 | -------------------------------------------------------------------------------- /.github/workflows/github-commands-comment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: github-commands-comment 3 | 4 | on: 5 | pull_request_target: 6 | types: 7 | - opened 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | comment: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | pull-requests: write 17 | steps: 18 | - uses: elastic/oblt-actions/elastic/github-commands@v1 19 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | name: "Issue Labeler" 2 | on: 3 | issues: 4 | types: [opened] 5 | pull_request_target: 6 | types: [opened] 7 | 8 | permissions: 9 | contents: read 10 | issues: write 11 | pull-requests: write 12 | 13 | jobs: 14 | triage: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: AlexanderWert/issue-labeler@v2.3 18 | with: 19 | repo-token: "${{ secrets.GITHUB_TOKEN }}" 20 | configuration-path: .github/labeler-config.yml 21 | enable-versioned-regex: 0 22 | -------------------------------------------------------------------------------- /.github/workflows/microbenchmark.yml: -------------------------------------------------------------------------------- 1 | name: microbenchmark 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | paths-ignore: 9 | - '**.md' 10 | - '**.asciidoc' 11 | 12 | # limit the access of the generated GITHUB_TOKEN 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | microbenchmark: 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 5 20 | steps: 21 | - name: Run microbenchmark 22 | uses: elastic/oblt-actions/buildkite/run@v1 23 | with: 24 | pipeline: "apm-agent-microbenchmark" 25 | token: ${{ secrets.BUILDKITE_TOKEN }} 26 | wait-for: false 27 | env-vars: | 28 | script=.ci/scripts/bench.sh 29 | repo=apm-agent-ruby 30 | sha=${{ github.sha }} 31 | BRANCH_NAME=${{ github.ref_name }} 32 | -------------------------------------------------------------------------------- /.github/workflows/test-reporter.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## Workflow to process the JUnit test results and add a report to the checks. 3 | name: test-reporter 4 | on: 5 | workflow_run: 6 | workflows: 7 | - ci 8 | types: 9 | - completed 10 | 11 | permissions: 12 | contents: read 13 | actions: read 14 | checks: write 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: elastic/oblt-actions/test-report@v1 21 | with: 22 | artifact: /test-results(.*)/ 23 | name: 'Test Report $1' 24 | path: "**/*ruby-agent-junit.xml" 25 | reporter: java-junit 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | Gemfile.lock 14 | /html_docs/ 15 | spec/elastic_apm.log 16 | spec/junit-reports 17 | .gem 18 | *.bulk 19 | vendor/ 20 | 21 | .idea/ 22 | log/ 23 | 24 | benchmark-*.error 25 | benchmark-*.raw 26 | 27 | .ci/docker/jruby/output.log 28 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v2.2.3 4 | hooks: 5 | - id: check-case-conflict 6 | - id: check-executables-have-shebangs 7 | - id: check-json 8 | - id: check-merge-conflict 9 | - id: check-yaml 10 | - id: check-xml 11 | 12 | - repo: https://github.com/elastic/apm-pipeline-library 13 | rev: current 14 | hooks: 15 | - id: check-bash-syntax 16 | - id: check-unicode-non-breaking-spaces 17 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --exclude-pattern spec/integration/**/*_spec.rb 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG RUBY_IMAGE 2 | FROM ${RUBY_IMAGE} 3 | 4 | ARG USER_ID_GROUP 5 | ARG FRAMEWORKS 6 | ARG VENDOR_PATH 7 | ARG BUNDLER_VERSION 8 | 9 | # For tzdata 10 | # ENV DEBIAN_FRONTEND=noninteractive 11 | 12 | RUN apt-get update -qq \ 13 | && apt-get install -qq -y --no-install-recommends \ 14 | build-essential libpq-dev git \ 15 | && rm -rf /var/lib/apt/lists/* 16 | 17 | # Configure bundler and PATH 18 | ENV LANG=C.UTF-8 19 | 20 | ENV GEM_HOME=$VENDOR_PATH 21 | ENV BUNDLE_PATH=$GEM_HOME \ 22 | BUNDLE_JOBS=4 BUNDLE_RETRY=3 23 | ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH \ 24 | BUNDLE_BIN=$BUNDLE_PATH/bin 25 | ENV PATH=/app/bin:$BUNDLE_BIN:$PATH 26 | 27 | ENV FRAMEWORKS $FRAMEWORKS 28 | 29 | RUN mkdir -p $VENDOR_PATH \ 30 | && chown -R $USER_ID_GROUP $VENDOR_PATH 31 | 32 | USER $USER_ID_GROUP 33 | 34 | # Upgrade RubyGems and install required Bundler version 35 | RUN gem update --system && \ 36 | gem install bundler:$BUNDLER_VERSION 37 | 38 | # Use unpatched, system version for more speed over less security 39 | RUN gem install nokogiri -- --use-system-libraries 40 | # Rake is required to build http-parser on some jruby images 41 | RUN gem install rake 42 | 43 | WORKDIR /app 44 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'bundler/gem_tasks' 21 | 22 | desc 'Post release action:'\ 23 | 'Update `3.x` branch to be at released commit and push it to GitHub.' 24 | 25 | namespace :release do 26 | task :update_branch do 27 | `git checkout 4.x && 28 | git rebase main && 29 | git push origin 4.x && 30 | git checkout main` 31 | end 32 | end 33 | 34 | require 'rspec/core/rake_task' 35 | RSpec::Core::RakeTask.new(:spec) 36 | 37 | require 'yard' 38 | YARD::Rake::YardocTask.new 39 | task docs: :yard 40 | 41 | task default: :spec 42 | 43 | namespace :coverage do 44 | desc "Collates all result sets generated by the different test runners" 45 | task :report do 46 | require 'simplecov' 47 | require 'simplecov-cobertura' 48 | SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter 49 | SimpleCov.collate Dir["coverage/matrix_results/**/.resultset.json"] 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | Thanks for your interest in the security of our products. 4 | Our security policy can be found at [https://www.elastic.co/community/security](https://www.elastic.co/community/security). 5 | 6 | ## Reporting a Vulnerability 7 | Please send security vulnerability reports to security@elastic.co. 8 | -------------------------------------------------------------------------------- /bench/.gitignore: -------------------------------------------------------------------------------- 1 | tmp/* 2 | db/* 3 | -------------------------------------------------------------------------------- /bench/app.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | $LOAD_PATH.unshift(File.expand_path('../lib', __dir__)) 5 | require 'active_record' 6 | require 'action_controller/railtie' 7 | require 'elastic-apm' 8 | require 'elastic_apm/railtie' 9 | 10 | $log = Logger.new('/tmp/bench.log') 11 | 12 | ActiveRecord::Base.logger = $log 13 | 14 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: '/tmp/bench.sqlite3') 15 | 16 | ActiveRecord::Schema.define do 17 | create_table :posts, force: true do |t| 18 | t.string :title 19 | t.timestamps 20 | end 21 | end 22 | 23 | class Post < ActiveRecord::Base 24 | end 25 | 26 | 10.times { |i| Post.create! title: "Post #{i}" } 27 | 28 | class ApplicationController < ActionController::Base 29 | def index 30 | render inline: '<%= Post.pluck(:title).join(", ") %>' 31 | end 32 | 33 | def favicon 34 | render nothing: true 35 | end 36 | end 37 | 38 | class App < Rails::Application 39 | config.secret_key_base = '__secret' 40 | config.logger = $log 41 | config.eager_load = false 42 | 43 | config.elastic_apm.disable_send = true 44 | config.elastic_apm.logger = $log 45 | config.elastic_apm.log_level = Logger::DEBUG 46 | end 47 | 48 | App.initialize! 49 | 50 | App.routes.draw do 51 | get '/favicon.ico', to: 'application#favicon' 52 | root to: 'application#index' 53 | end 54 | -------------------------------------------------------------------------------- /bench/benchmark.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | ENV['RAILS_ENV'] = 'production' 5 | 6 | require 'bundler' 7 | require 'bundler/setup' 8 | 9 | require 'benchmark' 10 | include Benchmark 11 | require 'rack/test' 12 | 13 | require './bench/app' 14 | 15 | def app 16 | App 17 | end 18 | 19 | include Rack::Test::Methods 20 | 21 | def perform 22 | 10_000.times do 23 | get '/' 24 | end 25 | end 26 | 27 | bench = Benchmark.benchmark(CAPTION, 15, FORMAT) do |b| 28 | perform # warm up 29 | 30 | b.report('with agent:') { perform } 31 | 32 | ElasticAPM.stop 33 | perform # warm up 34 | 35 | b.report('without agent:') { perform } 36 | end 37 | -------------------------------------------------------------------------------- /bench/report.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'time' 5 | require 'json' 6 | 7 | input = STDIN.read.split("\n") 8 | 9 | git_sha, git_msg = `git log -n 1 --pretty="format:%H|||%s"`.split('|||') 10 | git_date = `git log -n 1 --pretty="format:%ai"` 11 | platform = Gem::Platform.local 12 | 13 | def doc(payload) 14 | puts({ index: { _index: "benchmark-ruby" } }.to_json) 15 | puts(payload.to_json) 16 | end 17 | 18 | meta = { 19 | executed_at: Time.new.iso8601, 20 | 'git.commit' => git_sha, 21 | 'git.date' => (String(git_date).strip != '' && Time.parse(git_date).iso8601) || Time.now.iso8601, 22 | 'git.subject' => git_msg, 23 | hostname: `hostname`.chomp, 24 | engine: RUBY_ENGINE, 25 | arch: platform.cpu, 26 | os: platform.os, 27 | ruby_version: "#{RUBY_ENGINE == 'jruby' ? 'j' : ''}#{RUBY_VERSION}" 28 | } 29 | 30 | results = 31 | input 32 | .grep(/^with/) 33 | .map do |line| 34 | title = line.match(/^(.*):/) { |m| m[1] } 35 | user, system, total, real = line.scan(/[0-9\.]+/).map(&:to_f) 36 | meta.merge( 37 | title: title, 38 | user: user, 39 | system: system, 40 | total: total, 41 | real: real, 42 | ) 43 | end 44 | 45 | results.each { |result| doc result } 46 | 47 | overhead = 48 | (results[0][:total] - results[1][:total]) * 49 | 1000 / # milliseconds 50 | 10_000 # transactions 51 | 52 | doc meta.merge( 53 | title: 'overhead', 54 | overhead: overhead 55 | ) 56 | -------------------------------------------------------------------------------- /bench/rubyprof.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'ruby-prof' 5 | require 'rack/test' 6 | 7 | $LOAD_PATH.unshift(File.expand_path('../lib', __dir__)) 8 | require 'elastic-apm' 9 | 10 | require './bench/app' 11 | include App::Helpers 12 | 13 | def perform(app, count: 1000) 14 | app.start 15 | 16 | transactions = count.times.map do |i| 17 | ElasticAPM.transaction "Transaction##{i}", 18 | context: ElasticAPM.build_context(app.mock_env) do 19 | ElasticAPM.span('Number one') { 'ok 1' } 20 | ElasticAPM.span('Number two') { 'ok 2' } 21 | ElasticAPM.span('Number three') { 'ok 3' } 22 | end 23 | end 24 | 25 | app.stop 26 | end 27 | 28 | def do_bench(transaction_count: 1000, **config) 29 | with_app(config) do |app| 30 | profile = RubyProf::Profile.new 31 | profile.exclude_common_methods! 32 | profile.start 33 | perform(app, count: transaction_count) 34 | profile.stop 35 | printer = RubyProf::GraphHtmlPrinter.new(profile) 36 | printer.print(File.open('bench/tmp/out.html', 'w')) 37 | end 38 | end 39 | 40 | do_bench(transaction_count: 10_000) 41 | -------------------------------------------------------------------------------- /bench/sql.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | $LOAD_PATH.unshift(File.expand_path('../lib', __dir__)) 5 | 6 | require 'json' 7 | require 'benchmark' 8 | include Benchmark # rubocop:disable Style/MixinUsage 9 | 10 | require 'elastic_apm/sql_summarizer' 11 | require 'elastic_apm/sql/signature' 12 | 13 | examples = 14 | JSON 15 | .parse(File.read('../apm/tests/random_sql_query_set.json')) 16 | .map { |ex| ex['input'] } 17 | 18 | puts "#{'=' * 14} Parsing #{examples.length} examples #{'=' * 14}" 19 | 20 | summarizer = ElasticAPM::SqlSummarizer.new 21 | 22 | result_old = nil 23 | result_new = nil 24 | 25 | benchmark(CAPTION, 7, FORMAT, 'avg/ex:') do |bm| 26 | old = bm.report('old:') do 27 | result_old = examples.map { |i| summarizer.summarize(i) } 28 | end 29 | new = bm.report('new:') do 30 | result_new = examples.map { |i| ElasticAPM::Sql::Signature.parse(i) } 31 | end 32 | 33 | [(new - old) / examples.length] 34 | end 35 | 36 | pp(result_old.count { |res| res == 'SQL' }) 37 | pp(result_new.count { |res| res =~ /(--|\/\*\*)/ }) 38 | 39 | ## Stackprof 40 | 41 | require 'stackprof' 42 | 43 | puts 'Running stackprof' 44 | profile = StackProf.run(mode: :wall, interval: 1) do 45 | examples.each { |i| ElasticAPM::Sql::Signature.parse(i) } 46 | end 47 | puts '' 48 | 49 | StackProf::Report.new(profile).print_text 50 | -------------------------------------------------------------------------------- /bench/stackprof.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'stackprof' 5 | require 'rack/test' 6 | 7 | require './bench/app' 8 | 9 | def app 10 | App 11 | end 12 | 13 | include Rack::Test::Methods 14 | 15 | puts 'Running ' 16 | profile = StackProf.run(mode: :cpu) do 17 | 10_000.times do 18 | get '/' 19 | end 20 | end 21 | puts '' 22 | 23 | StackProf::Report.new(profile).print_text 24 | -------------------------------------------------------------------------------- /bench/tmp/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/apm-agent-ruby/a87a9321b9e1e7f0a3e6a96e413c593af517369f/bench/tmp/.gitkeep -------------------------------------------------------------------------------- /bin/build_docs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ex 3 | 4 | # Requires elastic/docs to be checked out at ../docs 5 | ../docs/build_docs --doc docs/index.asciidoc --chunk 1 6 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'elastic_apm' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require 'irb' 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/dev: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'optparse' 5 | 6 | options = {} 7 | OptionParser.new do |opts| 8 | opts.banner = 'Usage: bin/dev [options] [command]' 9 | 10 | opts.on( 11 | '-iIMAGE', '--image=IMAGE', 12 | 'Specify Docker image (eg. ruby:latest)' 13 | ) { |val| options[:image] = val } 14 | 15 | opts.on( 16 | '-fFRAMEWORKS', '--frameworks=FRAMEWORKS', 17 | 'Specify frameworks to test (eg. rails:master,sinatra)' 18 | ) { |val| options[:frameworks] = val } 19 | 20 | opts.on( 21 | '-s', '--skip-build', 22 | 'Skip building image' 23 | ) { |val| options[:skip_build] = val } 24 | end.parse! 25 | 26 | USER_ID_GROUP = %w[u g].map { |f| `id -#{f}`.chomp }.join(':') 27 | 28 | RUBY_IMAGE = options.fetch(:image, 'ruby:latest') 29 | FRAMEWORKS = options.fetch(:frameworks, 'rails,sinatra,grape') 30 | 31 | IMAGE_PATH_SAFE = RUBY_IMAGE.tr(':', '_') 32 | IMAGE_NAME = "apm-agent-ruby:#{IMAGE_PATH_SAFE}" 33 | VENDOR_PATH = "/vendor/#{IMAGE_PATH_SAFE}" 34 | 35 | def run(cmd) 36 | "IMAGE_NAME=#{IMAGE_NAME} #{cmd}".tap do |str| 37 | puts str 38 | system str 39 | end 40 | end 41 | 42 | unless options[:skip_build] 43 | run 'docker compose build ' \ 44 | " --build-arg RUBY_IMAGE=#{RUBY_IMAGE}" \ 45 | " --build-arg USER_ID_GROUP=#{USER_ID_GROUP}" \ 46 | " --build-arg FRAMEWORKS=#{FRAMEWORKS}" \ 47 | " --build-arg VENDOR_PATH=#{VENDOR_PATH}" 48 | exit $?.exitstatus unless $?.success? 49 | end 50 | 51 | run 'docker compose run' \ 52 | " -u #{USER_ID_GROUP}" \ 53 | ' --rm' \ 54 | " specs #{ARGV.join}" 55 | -------------------------------------------------------------------------------- /bin/run-bdd: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | specific_feature=$1 5 | 6 | export CUCUMBER_PUBLISH_QUIET=true 7 | 8 | if [[ $specific_feature = '' ]]; then 9 | echo 'Running all features' 10 | 11 | echo "========================================" 12 | cucumber 13 | else 14 | echo "Running only $specific_feature" 15 | 16 | cucumber $specific_feature 17 | fi 18 | -------------------------------------------------------------------------------- /bin/run-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | JUNIT_PREFIX=${JUNIT_PREFIX:-""} 5 | 6 | runRspec(){ 7 | local case=${1:-""} 8 | local bn=${case} 9 | 10 | if [ -n "${case}" ]; then 11 | bn="$(basename ${case} _spec.rb)/" 12 | fi 13 | # If working on an isolated environment then copy the file to 14 | # the original location 15 | TEST_REPORT_DIR=spec/junit-reports/${JUNIT_PREFIX}${bn} 16 | if [ -n "$APP_PATH" ] ; then 17 | TEST_REPORT_DIR=$APP_PATH/$TEST_REPORT_DIR 18 | mkdir -p $TEST_REPORT_DIR 19 | fi 20 | bundle exec rspec \ 21 | -f progress \ 22 | -r yarjuf -f JUnit -o ${TEST_REPORT_DIR}ruby-agent-junit.xml \ 23 | ${case} 24 | } 25 | specific_spec=$1 26 | 27 | if [[ $specific_spec = '' ]]; then 28 | echo 'Running all specs, including integration' 29 | 30 | runRspec 31 | for i in $(find spec/integration -name '*_spec.rb') 32 | do 33 | echo "========================================" 34 | echo $i 35 | echo "========================================" 36 | runRspec "$i" 37 | done 38 | else 39 | echo "Running only $specific_spec" 40 | 41 | runRspec $specific_spec 42 | fi 43 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /bin/with_framework: -------------------------------------------------------------------------------- 1 | #!/bin/bash -x 2 | set -e 3 | 4 | framework=$1 5 | 6 | FRAMEWORK=$framework bundle update 7 | FRAMEWORK=$framework ${@:2} 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '3.4' 3 | 4 | services: 5 | mongodb: 6 | image: mongo:latest 7 | volumes: ['mongodata:/data/db'] 8 | ports: ['27017:27017'] 9 | security_opt: 10 | - no-new-privileges 11 | 12 | specs: 13 | build: 14 | context: . 15 | args: 16 | BUNDLER_VERSION: '2.2.21' 17 | image: '$IMAGE_NAME' 18 | environment: 19 | HOME: '/tmp' 20 | MONGODB_URL: 'mongodb:27017' 21 | entrypoint: 22 | 'spec/entrypoint.sh' 23 | tty: true 24 | volumes: 25 | - .:/app:cached 26 | - ./vendor:/vendor 27 | tmpfs: 28 | - /tmp:exec,mode=1777 29 | depends_on: 30 | - mongodb 31 | user: ${USER_ID} 32 | security_opt: 33 | - no-new-privileges 34 | 35 | ruby_rspec: 36 | image: apm-agent-ruby:${RUBY_VERSION} 37 | environment: 38 | APP_PATH: /opt/app 39 | FRAMEWORK: rails 40 | LOCAL_USER_ID: ${LOCAL_USER_ID} 41 | LOCAL_GROUP_ID: ${LOCAL_GROUP_ID} 42 | MONGODB_URL: 'mongodb:27017' 43 | security_opt: 44 | - no-new-privileges 45 | 46 | volumes: 47 | vendor: 48 | mongodata: 49 | -------------------------------------------------------------------------------- /docs/docset.yml: -------------------------------------------------------------------------------- 1 | project: 'APM Ruby agent docs' 2 | products: 3 | - id: apm-agent 4 | cross_links: 5 | - apm-agent-rum-js 6 | - docs-content 7 | - ecs-logging 8 | - ecs-logging-ruby 9 | toc: 10 | - toc: reference 11 | - toc: release-notes 12 | -------------------------------------------------------------------------------- /docs/reference/advanced-topics.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/advanced.html 4 | --- 5 | 6 | # Advanced topics [advanced] 7 | 8 | * [Adding additional context](/reference/context.md) 9 | * [Custom instrumentation](/reference/custom-instrumentation.md) 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /docs/reference/context.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/context.html 4 | --- 5 | 6 | # Adding additional context [context] 7 | 8 | 9 | ## Adding custom context [_adding_custom_context] 10 | 11 | You can add your own custom, nested JSON-compatible data to the current transaction using `ElasticAPM.set_custom_context(hash)` eg.: 12 | 13 | ```ruby 14 | class ThingsController < ApplicationController 15 | before_action do 16 | ElasticAPM.set_custom_context(company: current_user.company) 17 | end 18 | 19 | # ... 20 | end 21 | ``` 22 | 23 | 24 | ## Adding labels [_adding_labels] 25 | 26 | Labels are special in that they are indexed in your Elasticsearch database and therefore queryable. 27 | 28 | ```ruby 29 | ElasticAPM.set_label(:company_name, 'Acme, Inc.') 30 | ``` 31 | 32 | Note that `.`, `*` and `"` in keys are converted to `_`. 33 | 34 | 35 | ## Providing info about the user [_providing_info_about_the_user] 36 | 37 | You can provide ElasticAPM with info about the current user. 38 | 39 | ```ruby 40 | class ApplicationController < ActionController::Base 41 | before_action do 42 | current_user && ElasticAPM.set_user(current_user) 43 | end 44 | end 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /docs/reference/getting-started-rack.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/getting-started-rack.html 4 | --- 5 | 6 | # Getting started with Rack [getting-started-rack] 7 | 8 | Add the gem to your `Gemfile`: 9 | 10 | ```ruby 11 | gem 'elastic-apm' 12 | ``` 13 | 14 | Create a file `config/elastic_apm.yml`: 15 | 16 | ```yaml 17 | server_url: http://localhost:8200 18 | secret_token: '' 19 | ``` 20 | 21 | Include the middleware, start (and stop) Elastic APM when booting your app: 22 | 23 | ```ruby 24 | # config.ru 25 | 26 | app = lambda do |env| 27 | [200, {'Content-Type' => 'text/plain'}, ['ok']] 28 | end 29 | 30 | # Wraps all requests in transactions and reports exceptions 31 | use ElasticAPM::Middleware 32 | 33 | # Start an instance of the Agent 34 | ElasticAPM.start(service_name: 'NothingButRack') 35 | 36 | run app 37 | 38 | # Gracefully stop the agent when process exits. 39 | # Makes sure any pending transactions are sent. 40 | at_exit { ElasticAPM.stop } 41 | ``` 42 | 43 | 44 | ## Sinatra example [getting-started-sinatra] 45 | 46 | ```ruby 47 | # Example config.ru 48 | 49 | require 'sinatra/base' 50 | 51 | class MySinatraApp < Sinatra::Base 52 | use ElasticAPM::Middleware 53 | 54 | # ... 55 | end 56 | 57 | # Takes optional ElasticAPM::Config values 58 | ElasticAPM.start(app: MySinatraApp, ...) 59 | 60 | # You can also do the following, which is equivalent to the above: 61 | # ElasticAPM::Sinatra.start(MySinatraApp, ...) 62 | 63 | run MySinatraApp 64 | 65 | at_exit { ElasticAPM.stop } 66 | ``` 67 | 68 | 69 | ## Grape example [getting-started-grape] 70 | 71 | ```ruby 72 | # Example config.ru 73 | 74 | require 'grape' 75 | 76 | module Twitter 77 | class API < Grape::API 78 | use ElasticAPM::Middleware 79 | 80 | # ... 81 | end 82 | end 83 | 84 | # Start the agent and hook in your app 85 | ElasticAPM::Grape.start(Twitter::API, config) 86 | 87 | run Twitter::API 88 | ``` 89 | 90 | -------------------------------------------------------------------------------- /docs/reference/getting-started-rails.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/getting-started-rails.html 4 | --- 5 | 6 | # Getting started with Rails [getting-started-rails] 7 | 8 | 9 | ## Setup [_setup] 10 | 11 | Add the gem to your `Gemfile`: 12 | 13 | ```ruby 14 | gem 'elastic-apm' 15 | ``` 16 | 17 | Create a file `config/elastic_apm.yml`: 18 | 19 | ```yaml 20 | server_url: http://localhost:8200 21 | secret_token: '' 22 | ``` 23 | 24 | Or if you prefer environment variables, skip the file and set `ELASTIC_APM_SERVER_URL` and `ELASTIC_APM_SECRET_TOKEN` in your local or server environment. 25 | 26 | This automatically sets up error logging and performance tracking but of course there are knobs to turn if you’d like to. See [*Configuration*](/reference/configuration.md). 27 | 28 | -------------------------------------------------------------------------------- /docs/reference/graphql.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/graphql.html 4 | --- 5 | 6 | # GraphQL [graphql] 7 | 8 | The agent comes with support for GraphQL based APIs. 9 | 10 | This slightly alters how transactions are named when they relate to GraphQL queries, so they are easier to tell apart and debug. 11 | 12 | To enable GraphQL support, add the included Tracer to your schema: 13 | 14 | ```ruby 15 | class MySchema < GraphQL::Schema 16 | # ... 17 | 18 | tracer ElasticAPM::GraphQL # <-- include this 19 | end 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /docs/reference/images/dynamic-config.svg: -------------------------------------------------------------------------------- 1 | DynamicDynamic -------------------------------------------------------------------------------- /docs/reference/set-up-apm-ruby-agent.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/set-up.html 4 | --- 5 | 6 | # Set up the APM Ruby agent [set-up] 7 | 8 | To get you off the ground, we’ve prepared guides for setting up the Agent with different frameworks: 9 | 10 | * [Rails](/reference/getting-started-rails.md) 11 | * [Rack](/reference/getting-started-rack.md) 12 | 13 | For custom instrumentation, see the [*API reference*](/reference/api-reference.md). 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/reference/toc.yml: -------------------------------------------------------------------------------- 1 | project: 'APM Ruby agent reference' 2 | toc: 3 | - file: index.md 4 | - file: set-up-apm-ruby-agent.md 5 | children: 6 | - file: getting-started-rails.md 7 | - file: getting-started-rack.md 8 | - file: supported-technologies.md 9 | - file: configuration.md 10 | - file: advanced-topics.md 11 | children: 12 | - file: context.md 13 | - file: custom-instrumentation.md 14 | - file: api-reference.md 15 | - file: metrics.md 16 | - file: logs.md 17 | - file: opentracing-api.md 18 | - file: graphql.md 19 | # TO DO: Not available on master 20 | # - file: log-correlation.md 21 | - file: performance-tuning.md 22 | - file: upgrading.md -------------------------------------------------------------------------------- /docs/reference/upgrading.md: -------------------------------------------------------------------------------- 1 | --- 2 | mapped_pages: 3 | - https://www.elastic.co/guide/en/apm/agent/ruby/current/upgrading.html 4 | --- 5 | 6 | # Upgrading [upgrading] 7 | 8 | Upgrades between minor versions of the agent, like from 2.1 to 2.2 are always backwards compatible. Upgrades that involve a major version bump often come with some backwards incompatible changes. 9 | 10 | Before upgrading the agent, be sure to review the: 11 | 12 | * [Agent release notes](/release-notes/index.md) 13 | * [Agent and Server compatibility chart](docs-content://solutions/observability/apps/apm-agent-compatibility.md) 14 | 15 | 16 | ## End of life dates [end-of-life-dates] 17 | 18 | We love all our products, but sometimes we must say goodbye to a release so that we can continue moving forward on future development and innovation. Our [End of life policy](https://www.elastic.co/support/eol) defines how long a given release is considered supported, as well as how long a release is considered still in active development or maintenance. 19 | 20 | -------------------------------------------------------------------------------- /docs/release-notes/known-issues.md: -------------------------------------------------------------------------------- 1 | --- 2 | navigation_title: "Known issues" 3 | 4 | --- 5 | 6 | # Elastic APM Ruby Agent known issues [elastic-apm-ruby-agent-known-issues] 7 | 8 | Known issues are significant defects or limitations that may impact your implementation. These issues are actively being worked on and will be addressed in a future release. Review the Elastic APM Ruby Agent known issues to help you make informed decisions, such as upgrading to a new version. 9 | 10 | % Use the following template to add entries to this page. 11 | 12 | % :::{dropdown} Title of known issue 13 | % **Details** 14 | % On [Month/Day/Year], a known issue was discovered that [description of known issue]. 15 | 16 | % **Workaround** 17 | % Workaround description. 18 | 19 | % **Resolved** 20 | % On [Month/Day/Year], this issue was resolved. 21 | 22 | ::: 23 | 24 | _No known issues_ -------------------------------------------------------------------------------- /docs/release-notes/toc.yml: -------------------------------------------------------------------------------- 1 | toc: 2 | - file: index.md 3 | - file: known-issues.md -------------------------------------------------------------------------------- /elastic-apm.gemspec: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | lib = File.expand_path('../lib', __FILE__) 19 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 20 | require 'elastic_apm/version' 21 | 22 | Gem::Specification.new do |spec| 23 | spec.name = 'elastic-apm' 24 | spec.version = ElasticAPM::VERSION 25 | spec.authors = ['Mikkel Malmberg', 'Emily Stolfo'] 26 | spec.email = ['info@elastic.co'] 27 | 28 | spec.summary = 'The official Elastic APM agent for Ruby' 29 | spec.homepage = 'https://github.com/elastic/apm-agent-ruby' 30 | spec.metadata = { 'source_code_uri' => 'https://github.com/elastic/apm-agent-ruby' } 31 | spec.license = 'Apache-2.0' 32 | spec.required_ruby_version = ">= 2.3.0" 33 | 34 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 35 | f.match(%r{^(test|spec|features)/}) 36 | end 37 | 38 | spec.add_dependency('concurrent-ruby', '~> 1.0') 39 | spec.add_dependency('http', '>= 3.0') 40 | spec.add_runtime_dependency('ruby2_keywords') 41 | 42 | spec.require_paths = ['lib'] 43 | end 44 | -------------------------------------------------------------------------------- /features/api_key.feature: -------------------------------------------------------------------------------- 1 | Feature: APM server authentication with API key and secret token 2 | 3 | Scenario: A configured API key is sent in the Authorization header 4 | Given an agent configured with 5 | | setting | value | 6 | | api_key | RTNxMjlXNEJt | 7 | When the agent sends a request to APM server 8 | Then the Authorization header of the request is 'ApiKey RTNxMjlXNEJt' 9 | 10 | Scenario: A configured secret token is sent in the Authorization header 11 | Given an agent configured with 12 | | setting | value | 13 | | secret_token | secr3tT0ken | 14 | When the agent sends a request to APM server 15 | Then the Authorization header of the request is 'Bearer secr3tT0ken' 16 | 17 | Scenario: A configured API key takes precedence over a secret token 18 | Given an agent configured with 19 | | setting | value | 20 | | api_key | MjlXNEJasdfDt | 21 | | secret_token | secr3tT0ken | 22 | When the agent sends a request to APM server 23 | Then the Authorization header of the request is 'ApiKey MjlXNEJasdfDt' 24 | 25 | -------------------------------------------------------------------------------- /features/step_definitions/api_key_steps.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # @api private 4 | module Helpers 5 | def config 6 | @agent.config 7 | end 8 | end 9 | 10 | World(Helpers) 11 | 12 | When('an api key is set to {string} in the config') do |api_key| 13 | config.api_key = api_key 14 | end 15 | 16 | Then('the Authorization header is {string}') do |auth_header| 17 | headers = ElasticAPM::Transport::Headers.new(config).to_h 18 | headers[:Authorization] == auth_header 19 | end 20 | 21 | When('an api key is set in the config') do 22 | config.api_key = '123:456' 23 | end 24 | 25 | Then('the api key is sent in the Authorization header') do 26 | headers = ElasticAPM::Transport::Headers.new(config).to_h 27 | headers[:Authorization].include?(config.api_key) 28 | end 29 | 30 | When('a secret_token is set to {string} in the config') do |api_key| 31 | config.secret_token = api_key 32 | end 33 | 34 | When('an api key is not set in the config') do 35 | end 36 | 37 | Then('the secret token is sent in the Authorization header') do 38 | headers = ElasticAPM::Transport::Headers.new(config).to_h 39 | headers[:Authorization].include?(config.secret_token) 40 | end 41 | -------------------------------------------------------------------------------- /features/step_definitions/common_steps.rb: -------------------------------------------------------------------------------- 1 | After do 2 | @agent&.stop 3 | end 4 | 5 | Given('an agent') do 6 | @agent = ElasticAPM.start 7 | end 8 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cucumber/rspec/doubles' 4 | require 'elastic-apm' 5 | -------------------------------------------------------------------------------- /features/user_agent.feature: -------------------------------------------------------------------------------- 1 | Feature: Agent Transport User agent Header 2 | 3 | Scenario: Default user-agent 4 | Given an agent 5 | When the agent sends a request to APM server 6 | Then the User-Agent header of the request matches regex '^apm-agent-[a-z]+/[^ ]* \(.*\)' 7 | 8 | Scenario: Default user-agent when setting invalid service 9 | Given an agent configured with 10 | | setting | value | 11 | | service_name | myService/()<>@ | 12 | When the agent sends a request to APM server 13 | Then the User-Agent header of the request matches regex '^apm-agent-[a-z]+/[^ ]* \(.*\)' 14 | 15 | Scenario: User-agent with service name only 16 | Given an agent configured with 17 | | setting | value | 18 | | service_name | myService | 19 | When the agent sends a request to APM server 20 | Then the User-Agent header of the request matches regex '^apm-agent-[a-z]+/[^ ]* \(myService\)' 21 | 22 | Scenario Outline: User-agent with service name and service version 23 | Given an agent configured with 24 | | setting | value | 25 | | service_name | | 26 | | service_version | | 27 | When the agent sends a request to APM server 28 | Then the User-Agent header of the request matches regex '^apm-agent-[a-z]+/[^ ]* \( \)' 29 | Examples: 30 | | SERVICE_NAME | ESCAPED_SERVICE_NAME | SERVICE_VERSION | ESCAPED_SERVICE_VERSION | 31 | | myService | myService | v42 | v42 | 32 | | myService | myService | 123(:\;)456 | 123_:_;_456 | 33 | -------------------------------------------------------------------------------- /lib/elastic-apm.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | # Make bundler auto-require work 21 | require 'elastic_apm' 22 | -------------------------------------------------------------------------------- /lib/elastic_apm/central_config/cache_control.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class CentralConfig 22 | # @api private 23 | class CacheControl 24 | def initialize(value) 25 | @header = value 26 | parse!(value) 27 | end 28 | 29 | attr_reader( 30 | :must_revalidate, 31 | :no_cache, 32 | :no_store, 33 | :no_transform, 34 | :public, 35 | :private, 36 | :proxy_revalidate, 37 | :max_age, 38 | :s_maxage 39 | ) 40 | 41 | private 42 | 43 | def parse!(value) 44 | value.split(',').each do |token| 45 | k, v = token.split('=').map(&:strip) 46 | instance_variable_set(:"@#{k.tr('-', '_')}", v ? v.to_i : true) 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/elastic_apm/child_durations.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module ChildDurations 23 | # @api private 24 | module Methods 25 | def child_durations 26 | @child_durations ||= Durations.new 27 | end 28 | 29 | def child_started 30 | child_durations.start 31 | end 32 | 33 | def child_stopped 34 | child_durations.stop 35 | end 36 | end 37 | 38 | # @api private 39 | class Durations 40 | def initialize 41 | @nesting_level = 0 42 | @start = nil 43 | @duration = 0 44 | @mutex = Mutex.new 45 | end 46 | 47 | attr_reader :duration 48 | 49 | def start 50 | @mutex.synchronize do 51 | @nesting_level += 1 52 | @start = Util.micros if @nesting_level == 1 53 | end 54 | end 55 | 56 | def stop 57 | @mutex.synchronize do 58 | @nesting_level -= 1 59 | @duration = (Util.micros - @start) if @nesting_level == 0 60 | end 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/bytes.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class Bytes 24 | MULTIPLIERS = { 25 | 'kb' => 1024, 26 | 'mb' => 1024 * 1_000, 27 | 'gb' => 1024 * 100_000 28 | }.freeze 29 | REGEX = /^(\d+)(b|kb|mb|gb)?$/i.freeze 30 | 31 | def initialize(default_unit: 'kb') 32 | @default_unit = default_unit 33 | end 34 | 35 | def call(value) 36 | _, amount, unit = REGEX.match(String(value)).to_a 37 | unit ||= @default_unit 38 | MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/duration.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class Duration 24 | MULTIPLIERS = { 'ms' => 0.001, 'm' => 60 }.freeze 25 | REGEX = /^(-)?(\d+)(m|ms|s)?$/i.freeze 26 | 27 | def initialize(default_unit: 's') 28 | @default_unit = default_unit 29 | end 30 | 31 | def call(str) 32 | _, negative, amount, unit = REGEX.match(String(str)).to_a 33 | unit ||= @default_unit 34 | seconds = MULTIPLIERS.fetch(unit.downcase, 1) * amount.to_i 35 | seconds = 0 - seconds if negative 36 | seconds 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/log_level_map.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class LogLevelMap 24 | LEVELS = { 25 | debug: Logger::DEBUG, 26 | info: Logger::INFO, 27 | warn: Logger::WARN, 28 | error: Logger::ERROR, 29 | fatal: Logger::FATAL, 30 | trace: Logger::DEBUG, 31 | warning: Logger::WARN, 32 | critical: Logger::FATAL, 33 | off: Logger::FATAL 34 | }.freeze 35 | 36 | DEFAULT = Logger::INFO 37 | 38 | def call(value) 39 | if value.is_a?(Integer) 40 | LEVELS.value?(value) ? value : DEFAULT 41 | else 42 | LEVELS.fetch(value.to_sym, DEFAULT) 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/regexp_list.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class RegexpList 24 | def call(value) 25 | value = value.is_a?(String) ? value.split(',') : Array(value) 26 | value.map { |p| Regexp.new(p) } 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/round_float.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'elastic_apm/util/precision_validator' 21 | 22 | module ElasticAPM 23 | class Config 24 | # @api private 25 | class RoundFloat 26 | def call(value) 27 | Util::PrecisionValidator.validate(value, precision: 4, minimum: 0.0001) 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/round_float_hash_value.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class RoundFloatHashValue 24 | def initialize 25 | @float_converter = RoundFloat.new 26 | end 27 | 28 | def call(hash) 29 | return {} unless hash 30 | 31 | hash.transform_values { |value| @float_converter.call(value) } 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/elastic_apm/config/server_info.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Config 22 | # @api private 23 | class ServerInfo 24 | attr_reader :payload, :config, :http 25 | 26 | VERSION_8_0 = '8.0' 27 | VERSION_0 = '0' 28 | 29 | def initialize(config) 30 | @config = config 31 | @http = Transport::Connection::Http.new(config) 32 | end 33 | 34 | def execute 35 | resp = http.get(config.server_url) 36 | @payload = JSON.parse(resp.body) 37 | rescue 38 | @payload = { "version" => VERSION_0 } 39 | end 40 | 41 | def version 42 | @version ||= begin 43 | execute 44 | payload["version"] ? payload["version"].to_s : VERSION_0 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/elastic_apm/context/request.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Context 22 | # @api private 23 | class Request 24 | attr_accessor :body, :cookies, :env, :headers, :http_version, :method, 25 | :socket, :url 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/elastic_apm/context/request/socket.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Context 23 | # @api private 24 | class Request 25 | # @api private 26 | class Socket 27 | def initialize(req) 28 | @remote_addr = req.env['REMOTE_ADDR'] 29 | end 30 | 31 | attr_reader :remote_addr 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/elastic_apm/context/request/url.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Context 23 | # @api private 24 | class Request 25 | # @api private 26 | class Url 27 | SKIPPED_PORTS = { 28 | 'http' => 80, 29 | 'https' => 443 30 | }.freeze 31 | 32 | def initialize(req) 33 | @protocol = req.scheme 34 | @hostname = req.host 35 | @port = req.port.to_s 36 | @pathname = req.path 37 | @search = req.query_string 38 | @hash = nil 39 | @full = build_full_url req 40 | end 41 | 42 | attr_reader :protocol, :hostname, :port, :pathname, :search, :hash, 43 | :full 44 | 45 | private 46 | 47 | def build_full_url(req) 48 | url = "#{req.scheme}://#{req.host}" 49 | 50 | if req.port != SKIPPED_PORTS[req.scheme] 51 | url += ":#{req.port}" 52 | end 53 | 54 | url + req.fullpath 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/elastic_apm/context/response.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Context 22 | # @api private 23 | class Response 24 | def initialize( 25 | status_code, 26 | headers: {}, 27 | headers_sent: true, 28 | finished: true 29 | ) 30 | @status_code = status_code 31 | @headers_sent = headers_sent 32 | @finished = finished 33 | 34 | self.headers = headers 35 | end 36 | 37 | attr_accessor :status_code, :headers_sent, :finished 38 | attr_reader :headers 39 | 40 | def headers=(headers) 41 | @headers = headers&.transform_values { |v| v.to_s } 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/elastic_apm/context/user.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Context 22 | # @api private 23 | class User 24 | def initialize(id: nil, email: nil, username: nil) 25 | @id = id 26 | @email = email 27 | @username = username 28 | end 29 | 30 | def self.infer(config, record) 31 | return unless record 32 | 33 | new( 34 | id: safe_get(record, config.current_user_id_method)&.to_s, 35 | email: safe_get(record, config.current_user_email_method), 36 | username: safe_get(record, config.current_user_username_method) 37 | ) 38 | end 39 | 40 | attr_accessor :id, :email, :username 41 | 42 | def empty? 43 | !id && !email && !username 44 | end 45 | 46 | def any? 47 | !empty? 48 | end 49 | 50 | class << self 51 | private 52 | 53 | def safe_get(record, method_name) 54 | record.respond_to?(method_name) ? record.send(method_name) : nil 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/elastic_apm/deprecations.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Deprecations 23 | def deprecate(name, replacement = nil) 24 | alias_name = "#{name.to_s.chomp('=')}__deprecated_" 25 | alias_name += '=' if name.to_s.end_with?('=') 26 | 27 | class_eval <<-RUBY, __FILE__, __LINE__ + 1 28 | alias :"#{alias_name}" :"#{name}" 29 | 30 | def #{name}(*args, &block) 31 | warn "[ElasticAPM] [DEPRECATED] `#{name}' is being removed. " \ 32 | "#{replacement && "See `#{replacement}'."}" \ 33 | "\nCalled from \#{caller.first}" 34 | send("#{alias_name}", *args, &block) 35 | end 36 | RUBY 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/elastic_apm/error.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'elastic_apm/stacktrace' 21 | require 'elastic_apm/context' 22 | require 'elastic_apm/error/exception' 23 | require 'elastic_apm/error/log' 24 | 25 | module ElasticAPM 26 | # @api private 27 | class Error 28 | def initialize(culprit: nil, context: nil) 29 | @id = SecureRandom.hex(16) 30 | @culprit = culprit 31 | @timestamp = Util.micros 32 | @context = context 33 | end 34 | 35 | attr_accessor :id, :culprit, :exception, :log, :transaction_id, 36 | :transaction_name, :transaction, :context, :parent_id, :trace_id 37 | attr_reader :timestamp 38 | 39 | def inspect 40 | "' 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/elastic_apm/error/log.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Error 22 | # @api private 23 | class Log 24 | def initialize(message, attrs = {}) 25 | @message = message 26 | 27 | attrs.each do |key, val| 28 | send(:"#{key}=", val) 29 | end 30 | end 31 | 32 | attr_accessor( 33 | :level, 34 | :logger_name, 35 | :message, 36 | :param_message, 37 | :stacktrace 38 | ) 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/elastic_apm/internal_error.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class InternalError < StandardError; end 22 | class ExistingTransactionError < InternalError; end 23 | end 24 | -------------------------------------------------------------------------------- /lib/elastic_apm/metadata.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Metadata 23 | def initialize(config) 24 | @service = ServiceInfo.new(config) 25 | @process = ProcessInfo.new(config) 26 | @system = SystemInfo.new(config) 27 | @labels = config.global_labels 28 | @cloud = CloudInfo.new(config).fetch! 29 | end 30 | 31 | attr_reader :service, :process, :system, :cloud, :labels 32 | end 33 | end 34 | 35 | require 'elastic_apm/metadata/service_info' 36 | require 'elastic_apm/metadata/system_info' 37 | require 'elastic_apm/metadata/process_info' 38 | require 'elastic_apm/metadata/cloud_info' 39 | -------------------------------------------------------------------------------- /lib/elastic_apm/metadata/process_info.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Metadata 22 | # @api private 23 | class ProcessInfo 24 | def initialize(config) 25 | @config = config 26 | 27 | @argv = ARGV 28 | @pid = $PID || Process.pid 29 | @title = $PROGRAM_NAME 30 | end 31 | 32 | attr_reader :argv, :pid, :title 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/elastic_apm/metrics/breakdown_set.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Metrics 22 | # @api private 23 | class BreakdownSet < SpanScopedSet 24 | def initialize(config) 25 | super 26 | 27 | disable! unless config.breakdown_metrics? 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/elastic_apm/metrics/span_scoped_set.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Metrics 22 | # @api private 23 | class SpanScopedSet < Set 24 | def collect 25 | super.tap do |sets| 26 | next unless sets 27 | 28 | sets.each do |set| 29 | move_transaction(set) 30 | move_span(set) 31 | end 32 | end 33 | end 34 | 35 | private 36 | 37 | def move_transaction(set) 38 | name = set.tags&.delete(:'transaction.name') 39 | type = set.tags&.delete(:'transaction.type') 40 | return unless name || type 41 | 42 | set.transaction = { name: name, type: type } 43 | set.tags = nil if set.tags.empty? 44 | end 45 | 46 | def move_span(set) 47 | type = set.tags&.delete(:'span.type') 48 | subtype = set.tags&.delete(:'span.subtype') 49 | return unless type 50 | 51 | set.span = { type: type, subtype: subtype } 52 | set.tags = nil if set.tags.empty? 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/elastic_apm/metrics/transaction_set.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Metrics 22 | # @api private 23 | class TransactionSet < SpanScopedSet 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/elastic_apm/metricset.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Metricset 23 | def initialize( 24 | timestamp: Util.micros, 25 | tags: nil, 26 | transaction: nil, 27 | span: nil, 28 | **samples 29 | ) 30 | @timestamp = timestamp 31 | @tags = tags 32 | @transaction = transaction 33 | @span = span 34 | @samples = samples 35 | end 36 | 37 | attr_accessor :timestamp, :transaction, :span, :tags 38 | attr_reader :samples 39 | 40 | def merge_tags!(tags) 41 | return unless tags 42 | 43 | @tags ||= {} 44 | @tags.merge! tags 45 | end 46 | 47 | def tags? 48 | tags&.any? 49 | end 50 | 51 | def empty? 52 | samples.empty? 53 | end 54 | 55 | def inspect 56 | "" 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/elastic_apm/naively_hashable.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # TODO: Remove this? 22 | # @api private 23 | module NaivelyHashable 24 | def naively_hashable? 25 | true 26 | end 27 | 28 | def to_h 29 | instance_variables.each_with_object({}) do |name, h| 30 | key = name.to_s.delete('@').to_sym 31 | value = instance_variable_get(name) 32 | is_hashable = 33 | value.respond_to?(:naively_hashable?) && value.naively_hashable? 34 | 35 | h[key] = is_hashable ? value.to_h : value 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/elastic_apm/normalizers/grape.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | %w[endpoint_run].each do |lib| 21 | require "elastic_apm/normalizers/grape/#{lib}" 22 | end 23 | -------------------------------------------------------------------------------- /lib/elastic_apm/normalizers/rails.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | %w[ 21 | action_controller 22 | action_mailer 23 | action_view 24 | active_record 25 | ].each do |lib| 26 | require "elastic_apm/normalizers/rails/#{lib}" 27 | end 28 | -------------------------------------------------------------------------------- /lib/elastic_apm/normalizers/rails/action_controller.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Normalizers 22 | module ActionController 23 | # @api private 24 | class ProcessActionNormalizer < Normalizer 25 | register 'process_action.action_controller' 26 | 27 | TYPE = 'app' 28 | SUBTYPE = 'controller' 29 | ACTION = 'action' 30 | 31 | def normalize(transaction, _name, payload) 32 | transaction.name = endpoint(payload) 33 | [transaction.name, TYPE, SUBTYPE, ACTION, nil] 34 | end 35 | 36 | private 37 | 38 | def endpoint(payload) 39 | "#{payload[:controller]}##{payload[:action]}" 40 | end 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/elastic_apm/normalizers/rails/action_mailer.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Normalizers 22 | module ActionMailer 23 | # @api private 24 | class ProcessActionNormalizer < Normalizer 25 | register 'process.action_mailer' 26 | 27 | TYPE = 'app' 28 | SUBTYPE = 'mailer' 29 | ACTION = 'action' 30 | 31 | def normalize(_transaction, _name, payload) 32 | [endpoint(payload), TYPE, SUBTYPE, ACTION, nil] 33 | end 34 | 35 | private 36 | 37 | def endpoint(payload) 38 | "#{payload[:mailer]}##{payload[:action]}" 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/elastic_apm/railtie.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Railtie < ::Rails::Railtie 23 | config.elastic_apm = ActiveSupport::OrderedOptions.new 24 | 25 | Config.schema.each do |key, args| 26 | next unless args.length > 1 27 | config.elastic_apm[key] = args[:default] 28 | end 29 | 30 | initializer 'elastic_apm.initialize' do |app| 31 | config = Config.new(app.config.elastic_apm.merge(app: app)).tap do |c| 32 | # Prepend Rails.root to log_path if present 33 | if c.log_path && !c.log_path.start_with?('/') 34 | c.log_path = ::Rails.root.join(c.log_path) 35 | end 36 | end 37 | 38 | if Rails.start(config) 39 | app.middleware.insert 0, Middleware 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/elastic_apm/resque.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # Defines a before_first_fork hook for starting the ElasticAPM agent 22 | # with Resque. 23 | module Resque 24 | ::Resque.before_first_fork do 25 | ::Resque.logger.debug('Starting ElasticAPM agent') 26 | ElasticAPM.start 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/elastic_apm/span/context/db.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Span 22 | class Context 23 | # @api private 24 | class Db 25 | def initialize( 26 | instance: nil, 27 | statement: nil, 28 | type: nil, 29 | user: nil, 30 | rows_affected: nil 31 | ) 32 | @instance = instance 33 | @statement = statement&.encode('utf-8', invalid: :replace, undef: :replace) 34 | @type = type 35 | @user = user 36 | @rows_affected = rows_affected 37 | end 38 | 39 | attr_accessor :instance, :statement, :type, :user, :rows_affected 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/elastic_apm/span/context/http.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Span 22 | class Context 23 | # @api private 24 | class Http 25 | def initialize(url: nil, status_code: nil, method: nil) 26 | @url = sanitize_url(url) 27 | @status_code = status_code 28 | @method = method 29 | end 30 | 31 | attr_accessor :url, :status_code, :method 32 | 33 | private 34 | 35 | def sanitize_url(uri_or_str) 36 | return unless uri_or_str 37 | 38 | uri = uri_or_str.is_a?(URI) ? uri_or_str.dup : URI(uri_or_str) 39 | uri.password = nil 40 | uri.to_s 41 | end 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/elastic_apm/span/context/links.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Span 22 | class Context 23 | # @api private 24 | class Links < Array 25 | # @api private 26 | class Link < Struct.new(:trace_id, :span_id) 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/elastic_apm/span/context/message.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Span 22 | class Context 23 | # @api private 24 | class Message 25 | def initialize( 26 | queue_name: nil, 27 | age_ms: nil 28 | ) 29 | @queue_name = queue_name 30 | @age_ms = age_ms 31 | end 32 | 33 | attr_reader( 34 | :queue_name, 35 | :age_ms 36 | ) 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /lib/elastic_apm/span/context/service.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | class Span 22 | class Context 23 | # @api private 24 | class Service 25 | include Fields 26 | 27 | field :target 28 | 29 | # @api private 30 | class Target 31 | include Fields 32 | 33 | field :name, default: '' 34 | field :type, default: '' 35 | end 36 | 37 | def initialize(target: nil, **attrs) 38 | super(**attrs) 39 | 40 | self.target = build_target(target) 41 | end 42 | 43 | private 44 | 45 | def build_target(target = nil) 46 | return Target.new unless target 47 | return target if target.is_a?(Target) 48 | 49 | Target.new(**target) 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/action_dispatch.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Spies 23 | # @api private 24 | class ActionDispatchSpy 25 | # @api private 26 | module Ext 27 | def render_exception(request, exception_or_wrapper) 28 | context = ElasticAPM.build_context( 29 | rack_env: request, for_type: :error 30 | ) 31 | exception = 32 | if exception_or_wrapper.is_a?(ActionDispatch::ExceptionWrapper) 33 | exception_or_wrapper.exception 34 | else 35 | exception_or_wrapper 36 | end 37 | ElasticAPM.report(exception, context: context, handled: false) 38 | 39 | super(request, exception_or_wrapper) 40 | end 41 | end 42 | 43 | def install 44 | ::ActionDispatch::ShowExceptions.prepend(Ext) 45 | end 46 | end 47 | 48 | register( 49 | 'ActionDispatch::ShowExceptions', 50 | 'action_dispatch/show_exception', 51 | ActionDispatchSpy.new 52 | ) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/json.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'elastic_apm/span_helpers' 21 | 22 | module ElasticAPM 23 | # @api private 24 | module Spies 25 | # @api private 26 | class JSONSpy 27 | def install 28 | ::JSON.class_eval do 29 | include SpanHelpers 30 | span_class_method :parse, 'JSON#parse', 'json.parse' 31 | span_class_method :parse!, 'JSON#parse!', 'json.parse' 32 | span_class_method :generate, 'JSON#generate', 'json.generate' 33 | end 34 | end 35 | end 36 | 37 | register 'JSON', 'json', JSONSpy.new 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/redis.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Spies 23 | # @api private 24 | class RedisSpy 25 | # @api private 26 | module Ext 27 | def call(command, &block) 28 | name = command[0].to_s.upcase 29 | 30 | return super(command, &block) if command[0] == :auth 31 | 32 | ElasticAPM.with_span(name.to_s, 'db.redis') do 33 | super(command, &block) 34 | end 35 | end 36 | end 37 | 38 | def install 39 | ::Redis::Client.prepend(Ext) 40 | end 41 | end 42 | 43 | register 'Redis', 'redis', RedisSpy.new 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/resque.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Spies 23 | # @api private 24 | class ResqueSpy 25 | TYPE = 'Resque' 26 | 27 | # @api private 28 | module Ext 29 | def perform 30 | name = @payload && @payload['class']&.to_s 31 | transaction = ElasticAPM.start_transaction(name, TYPE) 32 | super 33 | transaction&.done 'success' 34 | transaction&.outcome = Transaction::Outcome::SUCCESS 35 | rescue ::Exception => e 36 | ElasticAPM.report(e, handled: false) 37 | transaction&.done 'error' 38 | transaction&.outcome = Transaction::Outcome::FAILURE 39 | raise 40 | ensure 41 | ElasticAPM.end_transaction 42 | end 43 | end 44 | 45 | def install 46 | ::Resque::Job.prepend(Ext) 47 | end 48 | end 49 | 50 | register 'Resque', 'resque', ResqueSpy.new 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/sinatra.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Spies 23 | # @api private 24 | class SinatraSpy 25 | # @api private 26 | module Ext 27 | def dispatch!(*args, &block) 28 | super(*args, &block).tap do 29 | next unless (transaction = ElasticAPM.current_transaction) 30 | next unless (route = env['sinatra.route']) 31 | 32 | transaction.name = route 33 | end 34 | end 35 | 36 | def compile_template(engine, data, opts, *args, &block) 37 | opts[:__elastic_apm_template_name] = 38 | case data 39 | when Symbol then data.to_s 40 | else format('Inline %s', engine) 41 | end 42 | 43 | super(engine, data, opts, *args, &block) 44 | end 45 | end 46 | 47 | def install 48 | ::Sinatra::Base.prepend(Ext) 49 | end 50 | end 51 | 52 | register 'Sinatra::Base', 'sinatra/base', SinatraSpy.new 53 | require 'elastic_apm/spies/tilt' 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/elastic_apm/spies/tilt.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Spies 23 | # @api private 24 | class TiltSpy 25 | TYPE = 'template.tilt' 26 | 27 | # @api private 28 | module Ext 29 | def render(*args, &block) 30 | name = options[:__elastic_apm_template_name] || 'Unknown template' 31 | 32 | ElasticAPM.with_span name, TYPE do 33 | super(*args, &block) 34 | end 35 | end 36 | end 37 | 38 | def install 39 | ::Tilt::Template.prepend(Ext) 40 | end 41 | end 42 | 43 | register 'Tilt::Template', 'tilt/template', TiltSpy.new 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/elastic_apm/sql/tokens.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Sql 22 | # @api private 23 | module Tokens 24 | OTHER = :OTHER 25 | COMMENT = :COMMENT 26 | IDENT = :IDENT 27 | NUMBER = :NUMBER 28 | STRING = :STRING 29 | 30 | PERIOD = :PERIOD 31 | COMMA = :COMMA 32 | LPAREN = :LPAREN 33 | RPAREN = :RPAREN 34 | 35 | AS = :AS 36 | CALL = :CALL 37 | DELETE = :DELETE 38 | FROM = :FROM 39 | INSERT = :INSERT 40 | INTO = :INTO 41 | OR = :OR 42 | REPLACE = :REPLACE 43 | SELECT = :SELECT 44 | SET = :SET 45 | TABLE = :TABLE 46 | TRUNCATE = :TRUNCATE 47 | UPDATE = :UPDATE 48 | 49 | KEYWORDS = { 50 | 2 => [AS, OR], 51 | 3 => [SET], 52 | 4 => [CALL, FROM, INTO], 53 | 5 => [TABLE], 54 | 6 => [DELETE, INSERT, SELECT, UPDATE], 55 | 7 => [REPLACE], 56 | 8 => [TRUNCATE] 57 | }.freeze 58 | 59 | KEYWORD_MIN_LENGTH = KEYWORDS.keys.first 60 | KEYWORD_MAX_LENGTH = KEYWORDS.keys.last 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/elastic_apm/stacktrace.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | class Stacktrace 23 | attr_accessor :frames 24 | 25 | def length 26 | frames.length 27 | end 28 | 29 | def to_a 30 | frames.map(&:to_h) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/elastic_apm/transport/filters.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'elastic_apm/transport/filters/secrets_filter' 21 | 22 | module ElasticAPM 23 | module Transport 24 | # @api private 25 | module Filters 26 | SKIP = :skip 27 | 28 | def self.new(config) 29 | Container.new(config) 30 | end 31 | 32 | # @api private 33 | class Container 34 | def initialize(config) 35 | @filters = { secrets: SecretsFilter.new(config) } 36 | end 37 | 38 | def add(key, filter) 39 | @filters = @filters.merge(key => filter) 40 | end 41 | 42 | def remove(key) 43 | @filters.delete(key) 44 | end 45 | 46 | def apply!(payload) 47 | @filters.reduce(payload) do |result, (_key, filter)| 48 | result = filter.call(result) 49 | break SKIP if result.nil? 50 | result 51 | end 52 | end 53 | 54 | def length 55 | @filters.length 56 | end 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /lib/elastic_apm/transport/filters/hash_sanitizer.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'elastic_apm/util/deep_dup' 21 | 22 | module ElasticAPM 23 | module Transport 24 | module Filters 25 | # @api private 26 | class HashSanitizer 27 | FILTERED = '[FILTERED]' 28 | 29 | def initialize(key_patterns:) 30 | @key_patterns = key_patterns 31 | end 32 | 33 | attr_accessor :key_patterns 34 | 35 | def strip_from(obj) 36 | strip_from!(Util::DeepDup.dup(obj)) 37 | end 38 | 39 | def strip_from!(obj) 40 | return unless obj.is_a?(Hash) 41 | 42 | obj.each_pair do |k, v| 43 | case v 44 | when Hash 45 | strip_from!(v) 46 | else 47 | next unless filter_key?(k) 48 | obj[k] = FILTERED 49 | end 50 | end 51 | end 52 | 53 | def filter_key?(key) 54 | @key_patterns.any? { |regex| regex.match(key) } 55 | end 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/elastic_apm/transport/serializers/metricset_serializer.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Transport 22 | module Serializers 23 | # @api private 24 | class MetricsetSerializer < Serializer 25 | def build(metricset) 26 | payload = { 27 | timestamp: metricset.timestamp.to_i, 28 | samples: build_samples(metricset.samples) 29 | } 30 | 31 | if metricset.tags? 32 | payload[:tags] = mixed_object(metricset.tags) 33 | end 34 | 35 | if metricset.transaction 36 | payload[:transaction] = metricset.transaction 37 | end 38 | 39 | if metricset.span 40 | payload[:span] = metricset.span 41 | end 42 | 43 | { metricset: payload } 44 | end 45 | 46 | private 47 | 48 | def build_samples(samples) 49 | samples.transform_values do |value| 50 | { value: value } 51 | end 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/elastic_apm/transport/user_agent.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Transport 22 | # @api private 23 | class UserAgent 24 | def initialize(config, version: VERSION) 25 | @version = version 26 | @built = build(config) 27 | end 28 | 29 | def to_s 30 | @built 31 | end 32 | 33 | private 34 | 35 | def build(config) 36 | service = Metadata::ServiceInfo.new(config) 37 | 38 | [ 39 | "elastic-apm-ruby/#{@version}", 40 | formatted_service_info(service) 41 | ].compact.join(' ') 42 | end 43 | 44 | def formatted_service_info(service) 45 | if service.name 46 | "(#{[ 47 | service.name, 48 | service.version 49 | ].compact.join(' ') 50 | })" 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/elastic_apm/util.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | # @api private 22 | module Util 23 | def self.micros(target = Time.now) 24 | utc = target.utc 25 | utc.to_i * 1_000_000 + utc.usec 26 | end 27 | 28 | def self.monotonic_micros 29 | Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond) 30 | end 31 | 32 | def self.git_sha 33 | sha = `git rev-parse --verify HEAD 2>&1`.chomp 34 | $?&.success? ? sha : nil 35 | end 36 | 37 | def self.hex_to_bits(str) 38 | str.hex.to_s(2).rjust(str.size * 4, '0') 39 | end 40 | 41 | def self.reverse_merge!(first, *others) 42 | others.reduce(first) do |curr, other| 43 | curr.merge!(other) { |_, _, new| new } 44 | end 45 | end 46 | 47 | def self.truncate(value, max_length: 1024) 48 | return unless value 49 | 50 | value = String(value) 51 | return value if value.length <= max_length 52 | 53 | value[0...(max_length - 1)] + '…' 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/elastic_apm/util/deep_dup.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Util 22 | # @api private 23 | # 24 | # Makes a deep copy of an Array or Hash 25 | # NB: Not guaranteed to work well with complex objects, only simple Hash, 26 | # Array, String, Number, etc. 27 | class DeepDup 28 | def initialize(obj) 29 | @obj = obj 30 | end 31 | 32 | def dup 33 | deep_dup(@obj) 34 | end 35 | 36 | def self.dup(obj) 37 | new(obj).dup 38 | end 39 | 40 | private 41 | 42 | def deep_dup(obj) 43 | case obj 44 | when Hash then hash(obj) 45 | when Array then array(obj) 46 | else obj.dup 47 | end 48 | end 49 | 50 | def array(arr) 51 | arr.map { |obj| deep_dup(obj) } 52 | end 53 | 54 | def hash(hsh) 55 | result = hsh.dup 56 | 57 | hsh.each_pair do |key, value| 58 | result[key] = deep_dup(value) 59 | end 60 | 61 | result 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/elastic_apm/util/lru_cache.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Util 22 | # @api private 23 | class LruCache 24 | def initialize(max_size = 512, &block) 25 | @max_size = max_size 26 | @data = Hash.new(&block) 27 | @mutex = Mutex.new 28 | end 29 | 30 | def [](key) 31 | @mutex.synchronize do 32 | val = @data[key] 33 | return unless val 34 | add(key, val) 35 | val 36 | end 37 | end 38 | 39 | def []=(key, val) 40 | @mutex.synchronize do 41 | add(key, val) 42 | end 43 | end 44 | 45 | def length 46 | @data.length 47 | end 48 | 49 | def to_a 50 | @data.to_a 51 | end 52 | 53 | private 54 | 55 | def add(key, val) 56 | @data.delete(key) 57 | @data[key] = val 58 | 59 | return unless @data.length > @max_size 60 | 61 | @data.delete(@data.first[0]) 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/elastic_apm/util/precision_validator.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Util 22 | # @api private 23 | # Rounds half away from zero. 24 | # If `minimum` is provided, and the value rounds to 0 (but was not zero to 25 | # begin with), use the minimum instead. 26 | module PrecisionValidator 27 | module_function 28 | 29 | def validate(value, precision: 0, minimum: nil) 30 | float = Float(value) 31 | return nil unless (0.0..1.0).cover?(float) 32 | return float if float == 0 33 | 34 | multiplier = Float(10**precision) 35 | rounded = (float * multiplier + 0.5).floor / multiplier 36 | if rounded == 0 && minimum 37 | minimum 38 | else 39 | rounded 40 | end 41 | rescue ArgumentError 42 | nil 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/elastic_apm/util/throttle.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | module Util 22 | # @api private 23 | 24 | # Usage example: 25 | # Throttle.new(5) { thing to only do once per 5 secs } 26 | class Throttle 27 | def initialize(buffer_secs, &block) 28 | @buffer_secs = buffer_secs 29 | @block = block 30 | end 31 | 32 | def call 33 | if @last_call && seconds_since_last_call < @buffer_secs 34 | return @last_result 35 | end 36 | 37 | @last_call = now 38 | @last_result = @block.call 39 | end 40 | 41 | private 42 | 43 | def now 44 | Process.clock_gettime(Process::CLOCK_MONOTONIC) 45 | end 46 | 47 | def seconds_since_last_call 48 | now - @last_call 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/elastic_apm/version.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | VERSION = '4.8.0' 22 | end 23 | -------------------------------------------------------------------------------- /spec/Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image 2 | ARG RUBY_IMAGE 3 | FROM ${RUBY_IMAGE} 4 | 5 | # Env arguments 6 | ENV \ 7 | APP_WORKDIR="/var/app" \ 8 | CI="1" \ 9 | DEBIAN_FRONTEND="noninteractive" \ 10 | INCLUDE_COVERAGE="1" \ 11 | LANG=C.UTF-8 \ 12 | GOSU_VERSION="1.16" 13 | 14 | ENV \ 15 | BUNDLE_BIN="$APP_WORKDIR/vendor/bin" \ 16 | BUNDLE_CACHE_PATH="$APP_WORKDIR/cache" \ 17 | BUNDLE_PATH="$APP_WORKDIR/vendor" \ 18 | GEM_HOME="$APP_WORKDIR/vendor" \ 19 | PATH="/opt/app/bin:$APP_WORKDIR/vendor/bin:$PATH" 20 | 21 | # Copy specific scripts 22 | COPY build/ /usr/local/bin/ 23 | 24 | # Reset to root user 25 | USER root 26 | 27 | # Install dependencies and upgrade ruby system 28 | RUN \ 29 | apt-get update > /dev/null \ 30 | && apt-get install -qq -y build-essential libpq-dev git tzdata 31 | 32 | # Install gosu 33 | RUN \ 34 | curl -o /usr/local/bin/gosu -sSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-$(dpkg --print-architecture)" \ 35 | && curl -o /usr/local/bin/gosu.asc -sSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-$(dpkg --print-architecture).asc" \ 36 | && gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \ 37 | && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \ 38 | && rm /usr/local/bin/gosu.asc \ 39 | && chmod +x /usr/local/bin/gosu 40 | 41 | # Set workdir 42 | WORKDIR /app 43 | 44 | # Set specific entrypoint 45 | ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ] 46 | -------------------------------------------------------------------------------- /spec/build/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -eo pipefail 5 | 6 | # Remove jruby user if exists 7 | # The uid can collide with the one, we are trying to use 8 | if id "jruby" >/dev/null 2>&1; then 9 | userdel "jruby" 10 | fi 11 | 12 | # Create a new specific user 13 | USER_ID=${LOCAL_USER_ID:-1001} 14 | GROUP_ID=${LOCAL_GROUP_ID:-1001} 15 | echo "Starting with UID: ${USER_ID}, GID: ${GROUP_ID} and APP_WORKDIR: ${APP_WORKDIR}" 16 | [ $(getent group "${GROUP_ID}") ] || groupadd -g "${GROUP_ID}" user 17 | useradd -u "${USER_ID}" --gid "${GROUP_ID}" -s "/bin/false" -d "${APP_WORKDIR}" user 18 | 19 | # Create the app dir 20 | mkdir -p "${APP_WORKDIR}" 21 | 22 | # Install system dependencies 23 | update.sh 24 | 25 | # Fix permission for user 26 | chown -R "${USER_ID}:${GROUP_ID}" "${APP_WORKDIR}" 27 | 28 | # Run command with lower priviledge 29 | exec gosu ${USER_ID}:${GROUP_ID} "${@}" 30 | -------------------------------------------------------------------------------- /spec/build/run-bdd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -exo pipefail 5 | 6 | # Check env vars 7 | [ -z "${APP_PATH}" ] && echo "Environment variable 'APP_PATH' must be defined" && exit 1; 8 | [ -z "${APP_WORKDIR}" ] && echo "Environment variable 'APP_WORKDIR' must be defined" && exit 1; 9 | 10 | # Create an isolate bench folder 11 | cp -r ${APP_PATH}/. "${APP_WORKDIR}" 12 | cd "${APP_WORKDIR}" 13 | 14 | # Install project dependencies 15 | bundle update 16 | 17 | # Run tests 18 | timeout -s9 15m "${APP_WORKDIR}/bin/run-bdd" "${TEST:-}" 19 | -------------------------------------------------------------------------------- /spec/build/run-bench.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -exo pipefail 5 | 6 | # Check env vars 7 | [ -z "${APP_PATH}" ] && echo "Environment variable 'APP_PATH' must be defined" && exit 1; 8 | [ -z "${APP_WORKDIR}" ] && echo "Environment variable 'APP_WORKDIR' must be defined" && exit 1; 9 | [ -z "${REPORT_OUTPUT_NAME}" ] && echo "Environment variable 'REPORT_OUTPUT_NAME' must be defined" && exit 1; 10 | 11 | # Create an isolate bench folder 12 | cp -r ${APP_PATH}/. "${APP_WORKDIR}" 13 | cd "${APP_WORKDIR}" 14 | 15 | # Install project dependencies 16 | bundle install 17 | 18 | # Run benchmark and generate report 19 | bench/benchmark.rb 2> "${APP_PATH}/spec/${REPORT_OUTPUT_NAME}.error" > "${APP_PATH}/spec/${REPORT_OUTPUT_NAME}.raw" 20 | bench/report.rb < "${APP_PATH}/spec/${REPORT_OUTPUT_NAME}.raw" > "${APP_PATH}/spec/${REPORT_OUTPUT_NAME}.bulk" 21 | -------------------------------------------------------------------------------- /spec/build/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -exo pipefail 5 | 6 | # Check env vars 7 | [ -z "${APP_PATH}" ] && echo "Environment variable 'APP_PATH' must be defined" && exit 1; 8 | [ -z "${APP_WORKDIR}" ] && echo "Environment variable 'APP_WORKDIR' must be defined" && exit 1; 9 | 10 | # Create an isolate bench folder 11 | cp -r ${APP_PATH}/. "${APP_WORKDIR}" 12 | cd "${APP_WORKDIR}" 13 | 14 | # Install project dependencies 15 | bundle update 16 | 17 | # Run tests 18 | timeout -s9 15m "${APP_WORKDIR}/bin/run-tests" "${TEST:-}" 19 | -------------------------------------------------------------------------------- /spec/build/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | set -eo pipefail 5 | 6 | # Extract ruby version 7 | RUBY_VERSION=$(ruby -e 'print "#{ RUBY_VERSION }\n"') 8 | 9 | # Install specific dependencies for rails 4.x versions 10 | if [[ "${FRAMEWORK}" =~ ^rails-4\.([0-9]) ]]; then 11 | gem i "rubygems-update:~>2.7" --no-document 12 | update_rubygems --no-document 13 | gem i "bundler:~>1.17.3" --no-document 14 | elif [[ "${RUBY_VERSION}" =~ ^2\.(6|7).+ ]]; then 15 | gem i "rubygems-update:~>3.4.0" --no-document 16 | update_rubygems --no-document 17 | gem i bundler --no-document 18 | # Install specific dependencies for 2.4.x and 2.5.x ruby versions 19 | elif [[ "${RUBY_VERSION}" =~ ^2\.(4|5).+ ]]; then 20 | gem i "rubygems-update:~>2.7" --no-document 21 | update_rubygems --no-document 22 | gem i "bundler:~>2.3.26" --no-document 23 | else 24 | gem update --system --no-document 25 | gem i bundler --no-document 26 | fi 27 | 28 | # Install rake 29 | gem i rake --no-document 30 | -------------------------------------------------------------------------------- /spec/elastic_apm/apm_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | RSpec.describe ElasticAPM do 23 | it 'has a version number' do 24 | expect(ElasticAPM::VERSION).not_to be nil 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/elastic_apm/central_config/cache_control_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe CentralConfig::CacheControl do 24 | let(:header) { nil } 25 | subject { described_class.new(header) } 26 | 27 | context 'with max-age' do 28 | let(:header) { 'max-age=300' } 29 | its(:max_age) { should be 300 } 30 | its(:must_revalidate) { should be nil } 31 | end 32 | 33 | context 'with must-revalidate' do 34 | let(:header) { 'must-revalidate' } 35 | its(:max_age) { should be nil } 36 | its(:must_revalidate) { should be true } 37 | end 38 | 39 | context 'with multiple values' do 40 | let(:header) { 'must-revalidate, public, max-age=300' } 41 | its(:max_age) { should be 300 } 42 | its(:must_revalidate) { should be true } 43 | its(:public) { should be true } 44 | its(:private) { should be nil } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/elastic_apm/context/request/url_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | class Context 24 | class Request 25 | RSpec.describe Url do 26 | context 'from a rack req' do 27 | let(:url) { 'https://elastic.co:8080/nested/path?abc=123' } 28 | let(:req) { Rack::Request.new(Rack::MockRequest.env_for(url)) } 29 | 30 | subject { described_class.new(req) } 31 | 32 | its(:protocol) { is_expected.to eq 'https' } 33 | its(:hostname) { is_expected.to eq 'elastic.co' } 34 | its(:port) { is_expected.to eq '8080' } 35 | its(:pathname) { is_expected.to eq '/nested/path' } 36 | its(:search) { is_expected.to eq 'abc=123' } 37 | its(:hash) { is_expected.to eq nil } 38 | its(:full) { is_expected.to eq url } 39 | end 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/elastic_apm/context/response_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Context::Response do 24 | let(:response) do 25 | described_class.new( 26 | nil, 27 | headers: headers 28 | ) 29 | end 30 | 31 | let(:headers) do 32 | { 33 | a: 1, 34 | b: '2', 35 | c: [1, 2, 3] 36 | } 37 | end 38 | 39 | it 'converts header values to string' do 40 | expect(response.headers).to match( 41 | a: '1', 42 | b: '2', 43 | c: '[1, 2, 3]' 44 | ) 45 | end 46 | 47 | context 'when headers are nil' do 48 | let(:headers) { nil } 49 | it 'sets headers to nil' do 50 | expect(response.headers).to eq(nil) 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/elastic_apm/context/user_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Context::User do 24 | describe '.infer' do 25 | it 'sets values from passed object' do 26 | PossiblyUser = Struct.new(:id, :email, :username) 27 | record = PossiblyUser.new(1, 'a@a', 'abe') 28 | 29 | user = described_class.infer(Config.new, record) 30 | expect(user.id).to eq '1' 31 | expect(user.email).to eq 'a@a' 32 | expect(user.username).to eq 'abe' 33 | end 34 | 35 | it "doesn't explode with missing methods" do 36 | expect do 37 | user = described_class.infer(Config.new, Object.new) 38 | expect(user.id).to be_nil 39 | expect(user.email).to be_nil 40 | expect(user.username).to be_nil 41 | end.to_not raise_exception 42 | end 43 | end 44 | 45 | describe 'empty?' do 46 | it 'is when new' do 47 | expect(Context::User.new).to be_empty 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /spec/elastic_apm/error/log_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Error::Log do 24 | describe '#initialize' do 25 | it 'takes a message and optional attributes' do 26 | log = Error::Log.new 'Things', level: 'debug' 27 | expect(log.message).to eq 'Things' 28 | expect(log.level).to eq 'debug' 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/elastic_apm/error_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Error do 24 | describe 'initialization' do 25 | subject { Error.new } 26 | 27 | its(:id) { should_not be nil } 28 | its(:timestamp) { should_not be nil } 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/elastic_apm/metadata/process_info_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Metadata::ProcessInfo do 24 | describe '#initialize' do 25 | subject { described_class.new(Config.new) } 26 | 27 | it 'knows about the process' do 28 | expect(subject.argv).to be_a Array 29 | expect(subject.pid).to be_a Integer 30 | expect(subject.title).to match(/rspec/) 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/elastic_apm/metadata/service_info_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Metadata::ServiceInfo do 24 | describe '#initialize' do 25 | subject do 26 | described_class.new( 27 | Config.new( 28 | service_name: "my-service", 29 | service_node_name: "my-node" 30 | ) 31 | ) 32 | end 33 | 34 | its(:name) { is_expected.to eq "my-service" } 35 | its(:node_name) { is_expected.to eq "my-node" } 36 | 37 | it 'knows the runtime (mri)', unless: RSpec::Support::Ruby.jruby? do 38 | expect(subject.runtime.name).to eq 'ruby' 39 | expect(subject.runtime.version).to_not be_nil 40 | end 41 | 42 | it 'knows the runtime (JRuby)', if: RSpec::Support::Ruby.jruby? do 43 | expect(subject.runtime.name).to eq 'jruby' 44 | expect(subject.runtime.version).to_not be_nil 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/elastic_apm/metadata/system_info_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Metadata::SystemInfo do 24 | describe '#initialize' do 25 | subject { described_class.new(Config.new) } 26 | 27 | it 'has values' do 28 | %i[detected_hostname architecture platform].each do |key| 29 | expect(subject.send(key)).to_not be_nil 30 | end 31 | end 32 | 33 | context 'hostname' do 34 | it 'has no newline at the end' do 35 | expect(subject.detected_hostname).not_to match(/\n\z/) 36 | end 37 | 38 | context 'when there is an exception' do 39 | before do 40 | allow(Socket).to receive(:gethostname).and_raise(StandardError) 41 | end 42 | 43 | it 'returns nil' do 44 | expect(subject.detected_hostname).to eq(nil) 45 | end 46 | end 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/elastic_apm/metadata_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Metadata do 24 | let(:config) { Config.new(global_labels: { apples: 'oranges' }) } 25 | subject { described_class.new(config) } 26 | 27 | describe '#labels' do 28 | it 'accesses the config\'s labels' do 29 | expect(subject.labels).to eq(apples: 'oranges') 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/elastic_apm/metricset_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Metricset do 24 | describe 'initialize' do 25 | subject { described_class.new(thing: 1) } 26 | its(:timestamp) { should_not be nil } 27 | its(:samples) { should match(thing: 1) } 28 | end 29 | 30 | describe 'empty?' do 31 | context 'with samples' do 32 | subject { described_class.new thing: 1 } 33 | it { should_not be_empty } 34 | end 35 | 36 | context 'with no samples' do 37 | subject { described_class.new } 38 | it { should be_empty } 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/elastic_apm/naively_hashable_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe NaivelyHashable do 24 | class Outer 25 | include NaivelyHashable 26 | 27 | def initialize(inner) 28 | @inner = inner 29 | end 30 | end 31 | 32 | class Inner 33 | include NaivelyHashable 34 | 35 | def initialize(name) 36 | @name = name 37 | end 38 | end 39 | 40 | it 'converts object into hash from ivars' do 41 | expect(Outer.new(Inner.new('Ron Burgundy')).to_h).to eq( 42 | inner: { name: 'Ron Burgundy' } 43 | ) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/elastic_apm/normalizers_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | enable = false 23 | begin 24 | require 'active_support/notifications' 25 | enable = true 26 | rescue LoadError 27 | puts '[INFO] Skipping Normalizers spec' 28 | end 29 | 30 | if enable 31 | require 'elastic_apm/subscriber' 32 | 33 | module ElasticAPM 34 | RSpec.describe Normalizers do 35 | describe 'registration:' do 36 | it 'allows a normalizer to register itself' do 37 | class TestNormalizer < Normalizers::Normalizer 38 | register 'something' 39 | end 40 | 41 | built = Normalizers.build nil 42 | expect(built.for('something')).to be_a TestNormalizer 43 | expect(built.keys).to include 'something' 44 | end 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/elastic_apm/rails_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | if defined?(Rails) 23 | RSpec.describe Rails, :intercept do 24 | describe '.start' do 25 | it 'starts the agent' do 26 | begin 27 | ElasticAPM::Rails.start({}) 28 | expect(ElasticAPM::Agent).to be_running 29 | ensure 30 | ElasticAPM.stop 31 | end 32 | end 33 | end 34 | 35 | describe 'Rails console' do 36 | before do 37 | module Rails 38 | class Console; end 39 | end 40 | end 41 | 42 | after { Rails.send(:remove_const, :Console) } 43 | 44 | it "doesn't start when console" do 45 | begin 46 | ElasticAPM::Rails.start({}) 47 | expect(ElasticAPM.agent).to be nil 48 | expect(ElasticAPM).to_not be_running 49 | ensure 50 | ElasticAPM.stop 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /spec/elastic_apm/span/context/service_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | # 18 | # frozen_string_literal: true 19 | 20 | require "spec_helper" 21 | 22 | module ElasticAPM 23 | class Span 24 | class Context 25 | RSpec.describe Service do 26 | context '.new' do 27 | it 'accepts a hash of arguments' do 28 | result = described_class.new(target: {name: 'name', type: 'sql'}) 29 | expect(result.target).not_to be_nil 30 | expect(result.target.name).to eq('name') 31 | expect(result.target.type).to eq('sql') 32 | end 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/elastic_apm/spies/racecar_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | begin 23 | require 'active_support/notifications' 24 | require "active_support/subscriber" 25 | require 'racecar' 26 | 27 | module ElasticAPM 28 | RSpec.describe 'Spy: Racecar', :intercept do 29 | it 'captures the instrumentation' do 30 | with_agent do 31 | ActiveSupport::Notifications.instrument('start_process_message.racecar') 32 | ActiveSupport::Notifications.instrument('process_message.racecar') do 33 | # this is the body of the racecar consumer #process method 34 | end 35 | first_transaction = @intercepted.transactions.first 36 | expect(first_transaction).not_to be_nil 37 | expect(first_transaction.name).to eq('process_message') 38 | expect(first_transaction.type).to eq('kafka') 39 | end 40 | end 41 | end 42 | end 43 | 44 | rescue LoadError # in case we don't have ActiveSupport 45 | end -------------------------------------------------------------------------------- /spec/elastic_apm/spies/redis_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | require 'fakeredis/rspec' 23 | 24 | module ElasticAPM 25 | RSpec.describe 'Spy: Redis' do 26 | it 'spans queries', :intercept do 27 | redis = ::Redis.new 28 | 29 | with_agent do 30 | ElasticAPM.with_transaction 'T' do 31 | redis.lrange('some:where', 0, -1) 32 | end 33 | end 34 | 35 | span, = @intercepted.spans 36 | 37 | expect(span.name).to eq 'LRANGE' 38 | expect(span.outcome).to eq 'success' 39 | end 40 | 41 | it 'sets span outcome to `failure` for failed operations', :intercept do 42 | redis = ::Redis.new 43 | 44 | with_agent do 45 | ElasticAPM.with_transaction 'Redis failure test' do 46 | begin 47 | redis.bitop("meh", "dest1", "key1") 48 | rescue Redis::CommandError 49 | end 50 | end 51 | end 52 | 53 | span, = @intercepted.spans 54 | 55 | expect(span.outcome).to eq 'failure' 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/elastic_apm/spies/sinatra_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | RSpec.describe 'Spy: Sinatra' do 22 | # See Sinatra integration spec 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/elastic_apm/spies/tilt_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ElasticAPM 21 | RSpec.describe 'Spy: Tilt' do 22 | # See Sinatra integration spec 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /spec/elastic_apm/util/deep_dup_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Util::DeepDup do 24 | context 'hashes' do 25 | it 'makes a copy' do 26 | obj1 = Object.new 27 | obj2 = Object.new 28 | orig = { a: obj1, nested: { b: obj2 } } 29 | 30 | expect(Util::DeepDup.dup(orig)[:a]).to_not be obj1 31 | expect(Util::DeepDup.dup(orig)[:nested][:b]).to_not be obj2 32 | end 33 | end 34 | 35 | context 'arrays' do 36 | it 'makes a copy' do 37 | obj = Object.new 38 | orig = [obj] 39 | 40 | expect(orig.dup.first).to be obj 41 | expect(Util::DeepDup.dup(orig).first).to_not be obj 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/elastic_apm/util/lru_cache_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | module ElasticAPM 23 | RSpec.describe Util::LruCache do 24 | it 'purges when filled' do 25 | subject = described_class.new(2) 26 | 27 | subject[:a] = 1 28 | subject[:b] = 2 29 | subject[:a] 30 | subject[:c] = 3 31 | 32 | expect(subject.length).to be 2 33 | expect(subject.to_a).to match([[:a, 1], [:c, 3]]) 34 | end 35 | 36 | it 'taks a block' do 37 | subject = described_class.new do |cache, key| 38 | cache[key] = 'missing' 39 | end 40 | 41 | expect(subject['other key']).to eq 'missing' 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/elastic_apm/util/throttle_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | 22 | require 'elastic_apm/util/throttle' 23 | 24 | module ElasticAPM 25 | module Util 26 | RSpec.describe Throttle do 27 | let(:thing) { double call: 'ok' } 28 | 29 | subject { described_class.new(0.1) { thing.call } } # 100 milisecs 30 | 31 | it 'calls only once per 100 milisecs' do 32 | expect(thing).to receive(:call).once 33 | 5.times { subject.call } 34 | 35 | sleep 0.1 36 | 37 | expect(thing).to receive(:call).once 38 | 5.times { subject.call } 39 | end 40 | 41 | it 'returns result of last call' do 42 | subject = described_class.new(1) { 'here it is' } 43 | expect(subject.call).to eq('here it is') 44 | expect(subject.call).to eq('here it is') 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | 4 | bundle check || (rm -f Gemfile.lock && bundle) 5 | 6 | # If first arg is a spec path, run spec(s) 7 | if [[ $1 == spec/* ]]; then 8 | bundle exec bin/run-tests $@ 9 | exit $? 10 | fi 11 | 12 | # If no arguments, run all specs 13 | if [[ $# == 0 ]]; then 14 | bundle exec bin/run-tests 15 | exit $? 16 | fi 17 | 18 | # Otherwise, run args as command 19 | $@ 20 | -------------------------------------------------------------------------------- /spec/fixtures/elastic_apm.yml: -------------------------------------------------------------------------------- 1 | --- 2 | server_url: 'somewhere-config.com' 3 | -------------------------------------------------------------------------------- /spec/fixtures/elastic_apm_erb.yml: -------------------------------------------------------------------------------- 1 | --- 2 | server_url: <%= 'somewhere-config.com' %> 3 | -------------------------------------------------------------------------------- /spec/fixtures/proc_meminfo: -------------------------------------------------------------------------------- 1 | MemTotal: 3947960 kB 2 | MemFree: 704748 kB 3 | MemAvailable: 2685608 kB 4 | Buffers: 324060 kB 5 | Cached: 1357076 kB 6 | SwapCached: 9640 kB 7 | Active: 1524876 kB 8 | Inactive: 960344 kB 9 | Active(anon): 622160 kB 10 | Inactive(anon): 212836 kB 11 | Active(file): 902716 kB 12 | Inactive(file): 747508 kB 13 | Unevictable: 3652 kB 14 | Mlocked: 3652 kB 15 | SwapTotal: 1023996 kB 16 | SwapFree: 651900 kB 17 | Dirty: 468 kB 18 | Writeback: 0 kB 19 | AnonPages: 802664 kB 20 | Mapped: 86160 kB 21 | Shmem: 28488 kB 22 | Slab: 698796 kB 23 | SReclaimable: 619664 kB 24 | SUnreclaim: 79132 kB 25 | KernelStack: 6448 kB 26 | PageTables: 16156 kB 27 | NFS_Unstable: 0 kB 28 | Bounce: 0 kB 29 | WritebackTmp: 0 kB 30 | CommitLimit: 2997976 kB 31 | Committed_AS: 4322184 kB 32 | VmallocTotal: 34359738367 kB 33 | VmallocUsed: 0 kB 34 | VmallocChunk: 0 kB 35 | HardwareCorrupted: 0 kB 36 | AnonHugePages: 0 kB 37 | CmaTotal: 0 kB 38 | CmaFree: 0 kB 39 | HugePages_Total: 0 40 | HugePages_Free: 0 41 | HugePages_Rsvd: 0 42 | HugePages_Surp: 0 43 | -------------------------------------------------------------------------------- /spec/fixtures/proc_meminfo_wheezy: -------------------------------------------------------------------------------- 1 | MemTotal: 3947960 kB 2 | MemFree: 704748 kB 3 | Buffers: 324060 kB 4 | Cached: 1357076 kB 5 | SwapCached: 9640 kB 6 | Active: 1524876 kB 7 | Inactive: 960344 kB 8 | Active(anon): 622160 kB 9 | Inactive(anon): 212836 kB 10 | Active(file): 902716 kB 11 | Inactive(file): 747508 kB 12 | Unevictable: 3652 kB 13 | Mlocked: 3652 kB 14 | SwapTotal: 1023996 kB 15 | SwapFree: 651900 kB 16 | Dirty: 468 kB 17 | Writeback: 0 kB 18 | AnonPages: 802664 kB 19 | Mapped: 86160 kB 20 | Shmem: 28488 kB 21 | Slab: 698796 kB 22 | SReclaimable: 619664 kB 23 | SUnreclaim: 79132 kB 24 | KernelStack: 6448 kB 25 | PageTables: 16156 kB 26 | NFS_Unstable: 0 kB 27 | Bounce: 0 kB 28 | WritebackTmp: 0 kB 29 | CommitLimit: 2997976 kB 30 | Committed_AS: 4322184 kB 31 | VmallocTotal: 34359738367 kB 32 | VmallocUsed: 0 kB 33 | VmallocChunk: 0 kB 34 | HardwareCorrupted: 0 kB 35 | AnonHugePages: 0 kB 36 | CmaTotal: 0 kB 37 | CmaFree: 0 kB 38 | HugePages_Total: 0 39 | HugePages_Free: 0 40 | HugePages_Rsvd: 0 41 | HugePages_Surp: 0 42 | -------------------------------------------------------------------------------- /spec/fixtures/proc_self_stat: -------------------------------------------------------------------------------- 1 | 4861 (ruby) R 4441 4861 4441 34820 4861 4194304 2086 0 0 0 {utime} {stime} 0 0 20 0 2 0 170151991 53223424 3110 18446744073709551615 4194304 4197164 140726901561824 140726901547920 140610609386109 0 0 0 1107328591 0 0 0 17 1 0 0 0 0 0 6294976 6295568 36663296 140726901566581 140726901566611 140726901566611 140726901567467 0 2 | -------------------------------------------------------------------------------- /spec/fixtures/proc_stat_debian: -------------------------------------------------------------------------------- 1 | cpu {user} 11619 2457910 {idle} 376284 0 185125 0 0 0 2 | cpu0 3178685 5678 1254045 164774549 230030 0 53036 0 0 0 3 | cpu1 3231872 5941 1203865 164660122 146253 0 132088 0 0 0 4 | intr 636734946 23 10 0 0 2880 0 3 0 1 0 0 36 15 0 0 1660876 0 0 0 0 0 0 0 0 0 22 0 7954532 141 0 0 1 33962710 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 | ctxt 1290513757 6 | btime 1543445836 7 | processes 3990587 8 | procs_running 1 9 | procs_blocked 0 10 | softirq 521577188 0 145989881 7936 42291435 32957532 0 140 130217236 0 170113028 11 | -------------------------------------------------------------------------------- /spec/fixtures/proc_stat_rhel: -------------------------------------------------------------------------------- 1 | cpu {user} 2037 278561 {idle} 15536 0 178811 2 | cpu0 259246 7001 60190 34250993 137517 772 0 3 | intr 354133732 347209999 2272 0 4 4 0 0 3 1 1249247 0 0 80143 0 422626 5169433 4 | ctxt 12547729 5 | btime 1093631447 6 | processes 130523 7 | procs_running 1 8 | procs_blocked 0 9 | -------------------------------------------------------------------------------- /spec/fixtures/unknown_option.yml: -------------------------------------------------------------------------------- 1 | --- 2 | unknown_option_in_config_file: 123 3 | -------------------------------------------------------------------------------- /spec/integration/rails_console_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'integration_helper' 21 | 22 | if defined?(Rails) 23 | require 'action_controller/railtie' 24 | 25 | RSpec.describe 'Rails console', :spec_logger do 26 | before :all do 27 | class RailsConsoleTestApp < Rails::Application 28 | RailsTestHelpers.setup_rails_test_config(config) 29 | 30 | config.elastic_apm.disable_send = true 31 | config.logger = Logger.new(SpecLogger) 32 | end 33 | 34 | # rubocop:disable Style/ClassAndModuleChildren 35 | class ::ApplicationController < ActionController::Base; end 36 | class ::Rails::Console; end 37 | # rubocop:enable Style/ClassAndModuleChildren 38 | 39 | RailsConsoleTestApp.initialize! 40 | end 41 | 42 | after :all do 43 | ElasticAPM.stop 44 | end 45 | 46 | it "doesn't start when console" do 47 | expect(ElasticAPM.agent).to be nil 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/integration/rails_logger_ecs_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'integration_helper' 21 | 22 | if defined?(Rails) 23 | require 'action_controller/railtie' 24 | 25 | RSpec.describe 'Rails logger with Ecs logging option', :allow_running_agent do 26 | before :all do 27 | module RailsTestAppEcsLogger 28 | class Application < Rails::Application 29 | RailsTestHelpers.setup_rails_test_config(config) 30 | 31 | config.disable_send = true 32 | 33 | config.elastic_apm.log_ecs_reformatting = 'override' 34 | end 35 | end 36 | 37 | class ApplicationController < ActionController::Base 38 | end 39 | 40 | MockIntake.stub! 41 | 42 | RailsTestAppEcsLogger::Application.initialize! 43 | end 44 | 45 | it 'uses the Rails logger' do 46 | expect(Rails.logger).to be(ElasticAPM.agent.config.logger) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/integration/rails_logger_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'integration_helper' 21 | 22 | if defined?(Rails) 23 | require 'action_controller/railtie' 24 | 25 | RSpec.describe 'Rails logger', :allow_running_agent, :mock_intake do 26 | before :all do 27 | module RailsTestApp 28 | class Application < Rails::Application 29 | RailsTestHelpers.setup_rails_test_config(config) 30 | 31 | config.disable_send = true 32 | 33 | config.elastic_apm.logger = Logger.new(nil) 34 | config.logger = Logger.new(nil) 35 | end 36 | end 37 | 38 | class ApplicationController < ActionController::Base 39 | end 40 | 41 | MockIntake.stub! 42 | 43 | RailsTestApp::Application.initialize! 44 | end 45 | 46 | it 'sets the custom logger' do 47 | expect(Rails.logger).not_to be(ElasticAPM.agent.config.logger) 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /spec/integration/skip_require_patch_spec.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | # Deliberately setting this before requiring spec_helper.rb so it's set 21 | # before we require the agent 22 | ENV['ELASTIC_APM_SKIP_REQUIRE_PATCH'] = '1' 23 | require 'integration_helper' 24 | 25 | RSpec.describe "Disabling the require hook" do 26 | it "doesn't add aliased original method" do 27 | expect { Kernel.method(:require_without_apm) } 28 | .to raise_error(NameError) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/integration_helper.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | RSpec.configure do |config| 4 | config.before(:each) do |example| 5 | if ElasticAPM.running? && !example.metadata[:allow_running_agent] 6 | raise "Previous example left an agent running" 7 | end 8 | end 9 | 10 | config.after(:each) do |example| 11 | if ElasticAPM.running? && !example.metadata[:allow_running_agent] 12 | raise "This example left an agent running" 13 | end 14 | end 15 | 16 | config.after(:each, spec_logger: true) do |example| 17 | SpecLogger.rewind 18 | next unless example.exception 19 | 20 | puts("Example failed, dumping log:") 21 | puts(SpecLogger.read) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/scripts/benchmarks.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Perform benchmarks for the given docker ruby image. 4 | # NOTE: It's required to be launched inside the root of the project. 5 | # 6 | # Usage: ./spec/scripts/benchmarks.sh jruby:9.1 7 | # 8 | 9 | # Bash strict mode 10 | set -exo pipefail 11 | 12 | # Found current script directory 13 | RELATIVE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 14 | 15 | # Found project directory 16 | BASE_PROJECT="$(dirname "$(dirname "${RELATIVE_DIR}")")" 17 | 18 | # Arguments 19 | IMAGE_NAME=${1:?"missing RUBY IMAGE NAME"} 20 | VERSION=$(echo "${IMAGE_NAME}" | cut -d":" -f2) 21 | REPORT_OUTPUT_NAME=${2:?"missing THE REPORT OUTPUT NAME"} 22 | 23 | # Move in spec 24 | cd "${BASE_PROJECT}/spec" 25 | 26 | # Build custom container image 27 | docker build --pull --force-rm --build-arg "RUBY_IMAGE=${IMAGE_NAME}" -t "apm-agent-ruby:${VERSION}" . 28 | 29 | # Run bench 30 | IMAGE_NAME="${IMAGE_NAME}" \ 31 | LOCAL_GROUP_ID="$(id -g)" \ 32 | LOCAL_USER_ID="$(id -u)" \ 33 | RUBY_VERSION="${VERSION}" \ 34 | docker compose -f ../docker-compose.yml run \ 35 | -e REPORT_OUTPUT_NAME="${REPORT_OUTPUT_NAME}" \ 36 | -v "${BASE_PROJECT}:/opt/app" \ 37 | --rm ruby_rspec \ 38 | run-bench.sh 39 | -------------------------------------------------------------------------------- /spec/scripts/coverage_converge.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker build --build-arg "RUBY_IMAGE=ruby:2.7" --build-arg "VENDOR_PATH=vendor/2.7" --build-arg "BUNDLER_VERSION=2.0.2" --build-arg "USER_ID_GROUP=$(id -u):$(id -u)" -t "apm-agent-ruby:coverage" . 4 | docker run -e "TEST_MATRIX=nil" --mount type=bind,source="$(pwd)",target=/app apm-agent-ruby:coverage spec/scripts/coverage_entrypoint.sh -------------------------------------------------------------------------------- /spec/scripts/coverage_entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | bundle 3 | rake coverage:report 4 | -------------------------------------------------------------------------------- /spec/support/delegate_matcher.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | RSpec::Matchers.define :delegate do |method, opts| 21 | to = opts[:to] 22 | args = opts[:args] 23 | 24 | match do |delegator| 25 | unless to.respond_to?(method) 26 | raise NoMethodError, "no method `#{method}` on #{to.inspect}" 27 | end 28 | 29 | if args 30 | expect(to).to receive(method).at_least(:once).with(*args) { true } 31 | else 32 | expect(to).to receive(method).at_least(:once).with(no_args) { true } 33 | end 34 | 35 | if args&.last.is_a?(Hash) 36 | kw = args.pop 37 | delegator.send method, *args, **kw 38 | else 39 | delegator.send method, *args 40 | end 41 | end 42 | 43 | description do 44 | "delegate :#{method} to #{to}" 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/support/exception_helpers.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module ExceptionHelpers 21 | def actual_exception 22 | 1 / 0 23 | rescue => e 24 | e 25 | end 26 | 27 | class ChainedErrorOne < StandardError; end 28 | class ChainedErrorTwo < StandardError; end 29 | class ChainedErrorThree < StandardError; end 30 | 31 | def actual_chained_exception 32 | raise ChainedErrorThree 33 | rescue ChainedErrorThree 34 | begin 35 | raise ChainedErrorTwo 36 | rescue ChainedErrorTwo 37 | begin 38 | raise ChainedErrorOne 39 | rescue ChainedErrorOne => e 40 | e 41 | end 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/support/match_json_schema_matcher.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | require 'spec_helper' 21 | require 'json-schema' 22 | require 'open-uri' 23 | 24 | base = 'https://raw.githubusercontent.com/elastic/apm-server/master/docs/spec/v2' 25 | 26 | SCHEMA_URLS = { 27 | metadatas: "#{base}/metadata.json", 28 | transactions: "#{base}/transaction.json", 29 | spans: "#{base}/span.json", 30 | errors: "#{base}/error.json", 31 | metricset: "#{base}/metricset.json" 32 | }.freeze 33 | 34 | RSpec::Matchers.define :match_json_schema do |schema| 35 | match do |json| 36 | begin 37 | WebMock.disable! 38 | url = SCHEMA_URLS.fetch(schema) 39 | schema = URI.send(:open, url).read 40 | JSON::Validator.validate!(schema, json) 41 | rescue JSON::ParserError, JSON::Schema::ValidationError # jruby sometimes weirds out 42 | puts json.inspect 43 | raise 44 | ensure 45 | WebMock.enable! 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /spec/support/mock_time.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | RSpec.configure do |config| 21 | config.before :each, mock_time: true do 22 | @mocked_time = Time.utc(1992, 1, 1) 23 | @mocked_clock = 123_000 24 | 25 | def travel(us) 26 | Timecop.freeze(@mocked_time += (us / 1_000_000.0)) 27 | @mocked_clock += us 28 | end 29 | 30 | allow(ElasticAPM::Util).to receive(:monotonic_micros) { @mocked_clock } 31 | 32 | travel 0 33 | end 34 | 35 | config.after :each, mock_time: true do 36 | Timecop.return 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/support/platform_helpers.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module PlatformHelpers 21 | def darwin? 22 | ElasticAPM::Metrics.platform == :darwin 23 | end 24 | 25 | def linux? 26 | ElasticAPM::Metrics.platform == :linux 27 | end 28 | 29 | def self.jruby_92? 30 | defined?(JRUBY_VERSION) && JRUBY_VERSION =~ /^9\.2/ 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/support/rails_test_helpers.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | SpecLogger = StringIO.new 21 | 22 | module RailsTestHelpers 23 | def self.setup_rails_test_config(config) 24 | config.secret_key_base = "__secret_key_base" 25 | config.consider_all_requests_local = false 26 | config.eager_load = false 27 | 28 | config.elastic_apm.api_request_time = "200ms" 29 | config.elastic_apm.disable_start_message = true 30 | 31 | if config.respond_to?(:action_mailer) 32 | config.action_mailer.perform_deliveries = false 33 | end 34 | 35 | config.logger = Logger.new(SpecLogger) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/support/with_agent.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module WithAgent 21 | def with_agent(klass: ElasticAPM, args: [], **config) 22 | unless @mock_intake || @intercepted 23 | raise 'Using with_agent but neither MockIntake nor Intercepted' 24 | end 25 | 26 | @central_config_stub ||= 27 | WebMock.stub_request( 28 | :get, %r{^http://localhost:8200/config/v1/agents/?$} 29 | ).to_return(body: '{}') 30 | 31 | @server_version_stub = 32 | WebMock.stub_request(:get, %r{^http://localhost:8200/$}). 33 | to_return(body: '{"version":"8.0"}') 34 | 35 | klass.start(*args, **config) 36 | yield 37 | ensure 38 | ElasticAPM.stop 39 | 40 | @central_config_stub = nil 41 | @server_version_stub = nil 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /spec/support/with_env.rb: -------------------------------------------------------------------------------- 1 | # Licensed to Elasticsearch B.V. under one or more contributor 2 | # license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright 4 | # ownership. Elasticsearch B.V. licenses this file to you under 5 | # the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # frozen_string_literal: true 19 | 20 | module WithEnv 21 | def with_env(env) 22 | current_values = env.keys.each_with_object({}) do |(key, value), current| 23 | current[key] = ENV.key?(key) ? ENV[value] : :__missing 24 | end 25 | 26 | env.each_key { |key| ENV[key] = env[key] } 27 | 28 | yield 29 | ensure 30 | current_values.each do |key, value| 31 | case value 32 | when :__missing 33 | ENV.delete(key) 34 | else 35 | ENV[key] = value 36 | end 37 | end 38 | end 39 | end 40 | 41 | RSpec.configure do |config| 42 | config.include WithEnv 43 | end 44 | -------------------------------------------------------------------------------- /updatecli-compose.yaml: -------------------------------------------------------------------------------- 1 | # Config file for `updatecli compose ...`. 2 | # https://www.updatecli.io/docs/core/compose/ 3 | policies: 4 | - name: Handle apm-data server specs 5 | policy: ghcr.io/elastic/oblt-updatecli-policies/apm/apm-data-spec:0.6.0@sha256:c0bbdec23541bed38df1342c95aeb601530a113db1ff11715c1c7616ed5e9e8b 6 | values: 7 | - .ci/updatecli/values.d/scm.yml 8 | - .ci/updatecli/values.d/apm-data-spec.yml 9 | - name: Handle apm gherkin specs 10 | policy: ghcr.io/elastic/oblt-updatecli-policies/apm/apm-gherkin:0.6.0@sha256:dbaf4d855c5c212c3b5a8d2cc98c243a2b769ac347198ae8814393a1a0576587 11 | values: 12 | - .ci/updatecli/values.d/scm.yml 13 | - .ci/updatecli/values.d/apm-gherkin.yml 14 | - name: Handle apm json specs 15 | policy: ghcr.io/elastic/oblt-updatecli-policies/apm/apm-json-specs:0.6.0@sha256:e5a74c159ceed02fd20515ea76fa25ff81e3ccf977e74e636f9973db86aa52a5 16 | values: 17 | - .ci/updatecli/values.d/scm.yml 18 | - .ci/updatecli/values.d/apm-json-specs.yml 19 | - name: Update Updatecli policies 20 | policy: ghcr.io/updatecli/policies/autodiscovery/updatecli:0.8.0@sha256:99e9e61b501575c2c176c39f2275998d198b590a3f6b1fe829f7315f8d457e7f 21 | values: 22 | - .ci/updatecli/values.d/scm.yml 23 | - .ci/updatecli/values.d/update-compose.yml 24 | --------------------------------------------------------------------------------