From 366fbeba0e64d4fab647f22f73023666faa1d4c5 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 27 Jun 2023 14:34:52 -0400 Subject: [PATCH 1/5] Add OpenTelemetry Javaagent framework This framework will wire up the OpenTelemetry Javaagent auto instrumentation. It leverages VCAP_SERVICES and the existence of a service binding with a specific name (`otel-collector`). --- config/components.yml | 1 + config/opentelemetry_javaagent.yml | 19 ++++++ docs/framework-opentelemetry_javaagent.md | 40 +++++++++++ .../framework/opentelemetry_javaagent.rb | 54 +++++++++++++++ .../framework/opentelemtry_javaagent_spec.rb | 66 +++++++++++++++++++ 5 files changed, 180 insertions(+) create mode 100644 config/opentelemetry_javaagent.yml create mode 100644 docs/framework-opentelemetry_javaagent.md create mode 100644 lib/java_buildpack/framework/opentelemetry_javaagent.rb create mode 100644 spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb diff --git a/config/components.yml b/config/components.yml index 79a60156fd..90a12ec28e 100644 --- a/config/components.yml +++ b/config/components.yml @@ -67,6 +67,7 @@ frameworks: - "JavaBuildpack::Framework::MariaDbJDBC" - "JavaBuildpack::Framework::MetricWriter" - "JavaBuildpack::Framework::NewRelicAgent" + - "JavaBuildpack::Framework::OpenTelemetryJavaagent" - "JavaBuildpack::Framework::PostgresqlJDBC" - "JavaBuildpack::Framework::RiverbedAppinternalsAgent" - "JavaBuildpack::Framework::SealightsAgent" diff --git a/config/opentelemetry_javaagent.yml b/config/opentelemetry_javaagent.yml new file mode 100644 index 0000000000..2e45e5713e --- /dev/null +++ b/config/opentelemetry_javaagent.yml @@ -0,0 +1,19 @@ +# Cloud Foundry Java Buildpack +# Copyright 2013-2023 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Configuration for the OpenTelemetry Javaagent +--- +version: + +repository_root: https://raw.githubusercontent.com/open-telemetry/opentelemetry-java-instrumentation/cloudfoundry/ diff --git a/docs/framework-opentelemetry_javaagent.md b/docs/framework-opentelemetry_javaagent.md new file mode 100644 index 0000000000..b10a9455b5 --- /dev/null +++ b/docs/framework-opentelemetry_javaagent.md @@ -0,0 +1,40 @@ +# OpenTelemetry Javaagent + +The OpenTelemetry Javaagent buildpack framework will cause an application to be automatically instrumented +with the [OpenTelemetry Javaagent Instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation). + +Data will be sent directly to the OpenTelemetry Collector. + + + + + + + + + + +
Detection CriterionExistence of a bound service containing the string otel-collector
Tagsopentelemetry-javaagent=<version>
+ +Tags are printed to standard output by the buildpack detect script + +## User-Provided Service + +Users are currently expected to provide their own "custom user provided service" (cups) +instance and bind it to their application. The service MUST contain the string `otel-collector`. + +### Choosing a version + +Most users should skip this and simply use the latest version of the agent available (the default). +To override the default and choose a specific version, you can use the `JBP_CONFIG_*` mechanism +and set the `JBP_CONFIG_OPENTELEMETRY_JAVAAGENT` environment variable for your application. + +For example, to use version 1.27.0 of the OpenTelemetry Javaagent Instrumentation, you +could run: +``` +$ cf set-env testapp JBP_CONFIG_OPENTELEMETRY_JAVAAGENT '{version: 1.27.0}' +``` + +# Additional Resources + +* [OpenTelemetry Javaagent Instrumentation](https://github.com/open-telemetry/opentelemetry-java-instrumentation) on GitHub diff --git a/lib/java_buildpack/framework/opentelemetry_javaagent.rb b/lib/java_buildpack/framework/opentelemetry_javaagent.rb new file mode 100644 index 0000000000..bf28eb5941 --- /dev/null +++ b/lib/java_buildpack/framework/opentelemetry_javaagent.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2023 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'java_buildpack/component/versioned_dependency_component' +require 'java_buildpack/framework' + +module JavaBuildpack + module Framework + + # Main class for adding the OpenTelemetry Javaagent instrumentation + class OpenTelemetryJavaagent < JavaBuildpack::Component::VersionedDependencyComponent + + # (see JavaBuildpack::Component::BaseComponent#compile) + def compile + download_jar + end + + # (see JavaBuildpack::Component::BaseComponent#release) + def release + java_opts = @droplet.java_opts + java_opts.add_javaagent(@droplet.sandbox + jar_name) + + # Set the otel.service.name to the application_name + app_name = @application.details['application_name'] + java_opts.add_system_property('otel.service.name', app_name) + end + + protected + + # (see JavaBuildpack::Component::VersionedDependencyComponent#supports?) + def supports? + @application.services.one_service? REQUIRED_SERVICE_NAME_FILTER + end + + # bound service must contain the string `otel-collector` + REQUIRED_SERVICE_NAME_FILTER = /otel-collector/.freeze + + end + end +end diff --git a/spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb b/spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb new file mode 100644 index 0000000000..c1244ef4a7 --- /dev/null +++ b/spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Cloud Foundry Java Buildpack +# Copyright 2013-2020 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +require 'spec_helper' +require 'component_helper' +require 'java_buildpack/framework/opentelemetry_javaagent' +require 'java_buildpack/util/tokenized_version' + +describe JavaBuildpack::Framework::OpenTelemetryJavaagent do + include_context 'with component help' + + let(:configuration) { { 'version' => '1.27.0' } } + let(:vcap_application) { { 'application_name' => 'GreatServiceTM' } } + + it 'does not detect without otel-collector service bind' do + expect(component.detect).to be_nil + end + + context 'when detected' do + + before do + allow(services).to receive(:one_service?).with(/otel-collector/).and_return(true) + end + + it 'detects with opentelemetry-javaagent' do + expect(component.detect).to eq("opentelemetry-javaagent=#{version}") + end + + it 'downloads the opentelemetry javaagent jar', cache_fixture: 'stub-download.jar' do + + component.compile + + expect(sandbox + "opentelemetry_javaagent-#{version}.jar").to exist + end + + it 'updates JAVA_OPTS' do + component.release + + expect(java_opts).to include( + "-javaagent:$PWD/.java-buildpack/opentelemetry_javaagent/opentelemetry_javaagent-#{version}.jar" + ) + end + + it 'sets the service name from the application name' do + component.release + + expect(java_opts).to include('-Dotel.service.name=GreatServiceTM') + end + + end + +end From 5699a500801a80533eb0b4d61e9cebd1335d38c3 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 26 Sep 2023 13:35:05 -0400 Subject: [PATCH 2/5] Rename files to match naming convention. --- .../{opentelemetry_javaagent.yml => open_telemetry_javaagent.yml} | 0 ...lemetry_javaagent.md => framework-open_telemetry_javaagent.md} | 0 .../{opentelemetry_javaagent.rb => open_telemetry_javaagent.rb} | 0 ...telemtry_javaagent_spec.rb => open_telemtry_javaagent_spec.rb} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename config/{opentelemetry_javaagent.yml => open_telemetry_javaagent.yml} (100%) rename docs/{framework-opentelemetry_javaagent.md => framework-open_telemetry_javaagent.md} (100%) rename lib/java_buildpack/framework/{opentelemetry_javaagent.rb => open_telemetry_javaagent.rb} (100%) rename spec/java_buildpack/framework/{opentelemtry_javaagent_spec.rb => open_telemtry_javaagent_spec.rb} (100%) diff --git a/config/opentelemetry_javaagent.yml b/config/open_telemetry_javaagent.yml similarity index 100% rename from config/opentelemetry_javaagent.yml rename to config/open_telemetry_javaagent.yml diff --git a/docs/framework-opentelemetry_javaagent.md b/docs/framework-open_telemetry_javaagent.md similarity index 100% rename from docs/framework-opentelemetry_javaagent.md rename to docs/framework-open_telemetry_javaagent.md diff --git a/lib/java_buildpack/framework/opentelemetry_javaagent.rb b/lib/java_buildpack/framework/open_telemetry_javaagent.rb similarity index 100% rename from lib/java_buildpack/framework/opentelemetry_javaagent.rb rename to lib/java_buildpack/framework/open_telemetry_javaagent.rb diff --git a/spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb b/spec/java_buildpack/framework/open_telemtry_javaagent_spec.rb similarity index 100% rename from spec/java_buildpack/framework/opentelemtry_javaagent_spec.rb rename to spec/java_buildpack/framework/open_telemtry_javaagent_spec.rb From 4e85dc0f999e80bebb8369f509af24808b49bf1a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 27 Sep 2023 11:30:54 -0400 Subject: [PATCH 3/5] Update "user provided service" verbiage. --- docs/framework-open_telemetry_javaagent.md | 12 ++++++++++-- docs/framework-splunk_otel_java_agent.md | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/framework-open_telemetry_javaagent.md b/docs/framework-open_telemetry_javaagent.md index b10a9455b5..804d2e118f 100644 --- a/docs/framework-open_telemetry_javaagent.md +++ b/docs/framework-open_telemetry_javaagent.md @@ -20,8 +20,16 @@ Tags are printed to standard output by the buildpack detect script ## User-Provided Service -Users are currently expected to provide their own "custom user provided service" (cups) -instance and bind it to their application. The service MUST contain the string `otel-collector`. +Users are currently expected to `create-user-provided-service` (cups) of the collector +and bind it to their application. The service MUST contain the string `otel-collector`. + +For example, to create a service named `otel-collector` that represents an environment named `cf-demo`, you could use the following commands: + +``` +$ cf cups otel-collector -p '{"otel.resource.attributes": "deployment.environment=cf-demo"}' +$ cf bind-service myApp otel-collector +$ cf restage myApp +``` ### Choosing a version diff --git a/docs/framework-splunk_otel_java_agent.md b/docs/framework-splunk_otel_java_agent.md index 0ba30e97d5..e8b438376b 100644 --- a/docs/framework-splunk_otel_java_agent.md +++ b/docs/framework-splunk_otel_java_agent.md @@ -20,8 +20,8 @@ Tags are printed to standard output by the buildpack detect script ## User-Provided Service -Users are currently expected to provide their own "custom user provided service" (cups) -instance and bind it to their application. The service MUST contain the string `splunk-o11y`. +Users are currently expected to `create-user-provided-service` (cups) of the collector +and bind it to their application. The service MUST contain the string `splunk-o11y`. For example, to create a service named `splunk-o11y` that represents Observability Cloud realm `us0` and represents a user environment named `cf-demo`, you could use the following From d9bfa179767db3a589b28da05471aa43a5d0f66a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 10 Oct 2023 16:28:10 -0400 Subject: [PATCH 4/5] Apply suggestions from code review Co-authored-by: David O'Sullivan <31728678+pivotal-david-osullivan@users.noreply.github.com> --- ...javaagent_spec.rb => open_telemetry_javaagent_spec.rb} | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) rename spec/java_buildpack/framework/{open_telemtry_javaagent_spec.rb => open_telemetry_javaagent_spec.rb} (84%) diff --git a/spec/java_buildpack/framework/open_telemtry_javaagent_spec.rb b/spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb similarity index 84% rename from spec/java_buildpack/framework/open_telemtry_javaagent_spec.rb rename to spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb index c1244ef4a7..940e5decff 100644 --- a/spec/java_buildpack/framework/open_telemtry_javaagent_spec.rb +++ b/spec/java_buildpack/framework/open_telemetry_javaagent_spec.rb @@ -17,7 +17,7 @@ require 'spec_helper' require 'component_helper' -require 'java_buildpack/framework/opentelemetry_javaagent' +require 'java_buildpack/framework/open_telemetry_javaagent' require 'java_buildpack/util/tokenized_version' describe JavaBuildpack::Framework::OpenTelemetryJavaagent do @@ -37,21 +37,21 @@ end it 'detects with opentelemetry-javaagent' do - expect(component.detect).to eq("opentelemetry-javaagent=#{version}") + expect(component.detect).to eq("open-telemetry-javaagent=#{version}") end it 'downloads the opentelemetry javaagent jar', cache_fixture: 'stub-download.jar' do component.compile - expect(sandbox + "opentelemetry_javaagent-#{version}.jar").to exist + expect(sandbox + "open_telemetry_javaagent-#{version}.jar").to exist end it 'updates JAVA_OPTS' do component.release expect(java_opts).to include( - "-javaagent:$PWD/.java-buildpack/opentelemetry_javaagent/opentelemetry_javaagent-#{version}.jar" + "-javaagent:$PWD/.java-buildpack/open_telemetry_javaagent/open_telemetry_javaagent-#{version}.jar" ) end From ab6bb691f247bb869863994b8f147aabd367c75a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 10 Oct 2023 16:37:41 -0400 Subject: [PATCH 5/5] Populate provided system properties from config. --- lib/java_buildpack/framework/open_telemetry_javaagent.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/java_buildpack/framework/open_telemetry_javaagent.rb b/lib/java_buildpack/framework/open_telemetry_javaagent.rb index bf28eb5941..b78049bbf2 100644 --- a/lib/java_buildpack/framework/open_telemetry_javaagent.rb +++ b/lib/java_buildpack/framework/open_telemetry_javaagent.rb @@ -34,6 +34,15 @@ def release java_opts = @droplet.java_opts java_opts.add_javaagent(@droplet.sandbox + jar_name) + credentials = @application.services.find_service(REQUIRED_SERVICE_NAME_FILTER)['credentials'] + # Add all otel.* credentials from the service bind as jvm system properties + credentials&.each do |key, value| + java_opts.add_system_property(key, value) if key.start_with?('otel.') + end + + # Set the otel.service.name to the application_name if not specified in credentials + return if credentials.key? 'otel.service.name' + # Set the otel.service.name to the application_name app_name = @application.details['application_name'] java_opts.add_system_property('otel.service.name', app_name)