Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set extension properties directly #41244

Merged
merged 1 commit into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 37 additions & 42 deletions docs/src/main/asciidoc/observability-devservices-lgtm.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,32 +35,28 @@

=== Metrics

If you're using https://micrometer.io/[MicroMeter's] Quarkiverse OTLP registry to push metrics to Grafana OTel LGTM, this is how you would define the export endpoint url; where `quarkus.otel-collector.url` is provided by the Observability Dev Services extension.
If you need metrics, add the Micrometer OTLP registry to your build file:

[source,properties]
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
# Micrometer OTLP registry
%test.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
%dev.quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
%prod.quarkus.micrometer.export.otlp.url=http://localhost:4318/v1/metrics
<dependency>
<groupId>io.quarkiverse.micrometer.registry</groupId>
<artifactId>quarkus-micrometer-registry-otlp</artifactId>
</dependency>
----
Please note that the `${quarkus.otel-collector.url}` value is generated by quarkus when it starts the Grafana OTel LGTM Dev Resource.

Along OTel collector enpoint url, LGTM Dev Resource also provides a Grafana endpoint url - under `quarkus.grafana.url` property.

In this case LGTM Dev Resource would be automatically started and used by Observability Dev Services.

If you don't want all the hassle with Dev Services (e.g. lookup and re-use of existing running containers, etc) you can simply disable Dev Services and enable just Dev Resource usage:

[source,properties]
[source,gradle,role="secondary asciidoc-tabs-target-sync-gradle"]
.build.gradle

Check warning on line 50 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.CaseSensitiveTerms] Use 'Gradle' rather than 'gradle'. Raw Output: {"message": "[Quarkus.CaseSensitiveTerms] Use 'Gradle' rather than 'gradle'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 50, "column": 8}}}, "severity": "INFO"}
----
quarkus.observability.enabled=false
quarkus.observability.dev-resources=true
implementation("io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-otlp")
----

When using the https://micrometer.io/[MicroMeter's] Quarkiverse OTLP registry to push metrics to Grafana OTel LGTM, the `quarkus.micrometer.export.otlp.url` property is automatically set to OTel collector endpoint as seen from the outside of the docker container.

Check warning on line 55 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'by using' or 'that uses' rather than 'using'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 55, "column": 5}}}, "severity": "INFO"}

Check warning on line 55 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 55, "column": 215}}}, "severity": "INFO"}

=== Tracing

Just add the quarkus-opentelemetry extension to your build file:
For Tracing add the `quarkus-opentelemetry` extension to your build file:
[source,xml,role="primary asciidoc-tabs-target-sync-cli asciidoc-tabs-target-sync-maven"]
.pom.xml
----
Expand All @@ -76,34 +72,43 @@
implementation("io.quarkus:quarkus-opentelemetry")
----

On the `application.properties` file, you can define:
[source,properties]
----
# OpenTelemetry
quarkus.otel.exporter.otlp.traces.protocol=http/protobuf
%test.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
%dev.quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
%prod.quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:4318
----
The `quarkus.otel.exporter.otlp.traces.endpoint` property is automatically set to OTel collector endpoint as seen from the outside of the docker container.

Check warning on line 75 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 75, "column": 107}}}, "severity": "INFO"}

The `quarkus.otel.exporter.otlp.traces.protocol` is set to `http/protobuf`.

=== Access Grafana

Check warning on line 79 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'Access Grafana'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'Access Grafana'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 79, "column": 5}}}, "severity": "INFO"}

Once you start your app in dev mode:

include::{includes}/devtools/dev.adoc[]

You will see a message like this:
You will see a log entry like this:

[source, log]
----
Lgtm Dev Services Starting: 2024-02-20 11:15:24,540 INFO [org.tes.con.wai.str.HttpWaitStrategy] (build-32) /loving_chatelet: Waiting for 60 seconds for URL: http://localhost:61907/ (where port 61907 maps to container port 3000)
[io.qu.ob.de.ObservabilityDevServiceProcessor] (build-35) Dev Service Lgtm started, config: {grafana.endpoint=http://localhost:42797, quarkus.otel.exporter.otlp.traces.endpoint=http://localhost:34711, otel-collector.url=localhost:34711, quarkus.micrometer.export.otlp.url=http://localhost:34711/v1/metrics, quarkus.otel.exporter.otlp.traces.protocol=http/protobuf}

----
Remember that Grafana is accessible in an ephemeral port, so you need to check the logs to see which port is being used. In this example, it's port 61907.
Remember that Grafana is accessible in an ephemeral port, so you need to check the logs to see which port is being used. In this example, the grafana endpoint is `grafana.endpoint=http://localhost:42797`.

Check warning on line 92 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'. Raw Output: {"message": "[Quarkus.Fluff] Depending on the context, consider using 'Rewrite the sentence, or use 'must', instead of' rather than 'need to'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 92, "column": 66}}}, "severity": "INFO"}

Check warning on line 92 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 92, "column": 95}}}, "severity": "INFO"}

If you miss the message you can always check the port with this Docker command:
[source, bash]
----
docker ps | grep grafana
----

=== Additional configuration

This extension will configure your `quarkus-opentelemetry` and `quarkus-micrometer-registry-otlp` extensions to send data to the OTel Collector bundled with the Grafana OTel LGTM image.

If you don't want all the hassle with Dev Services (e.g. lookup and re-use of existing running containers, etc) you can simply disable Dev Services and enable just Dev Resource usage:

Check warning on line 104 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.SentenceLength] Try to keep sentences to an average of 32 words or fewer. Raw Output: {"message": "[Quarkus.SentenceLength] Try to keep sentences to an average of 32 words or fewer.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 104, "column": 1}}}, "severity": "INFO"}

Check warning on line 104 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'for example' rather than 'e.g.' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'for example' rather than 'e.g.' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 104, "column": 53}}}, "severity": "WARNING"}

Check warning on line 104 in docs/src/main/asciidoc/observability-devservices-lgtm.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'and so on' rather than 'etc' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'and so on' rather than 'etc' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/observability-devservices-lgtm.adoc", "range": {"start": {"line": 104, "column": 108}}}, "severity": "WARNING"}

[source,properties]
----
quarkus.observability.enabled=false
quarkus.observability.dev-resources=true
----

=== Tests

And for the least 'auto-magical' usage in the tests, simply disable both (Dev Resources are already disabled by default):
Expand Down Expand Up @@ -142,16 +147,6 @@
implementation("io.quarkiverse.micrometer.registry:quarkus-micrometer-registry-otlp")
----

On the test `application.properties` file, you need to define:
[source,properties]
----
# Micrometer OTLP registry
quarkus.micrometer.export.otlp.url=http://${quarkus.otel-collector.url}/v1/metrics
# OpenTelemetry
quarkus.otel.exporter.otlp.traces.protocol=http/protobuf
quarkus.otel.exporter.otlp.traces.endpoint=http://${quarkus.otel-collector.url}
----

Simply inject the Meter registry into your code -- it will periodically push metrics to Grafana LGTM's OTLP HTTP endpoint.

[source, java]
Expand Down Expand Up @@ -182,14 +177,14 @@
----
public class LgtmTestBase {

@ConfigProperty(name = "quarkus.grafana.url")
String url; // NOTE -- injected Grafana endpoint url!
@ConfigProperty(name = "grafana.endpoint")
String endpoint; // NOTE -- injected Grafana endpoint!

@Test
public void testTracing() {
String response = RestAssured.get("/api/poke?f=100").body().asString();
System.out.println(response);
GrafanaClient client = new GrafanaClient("http://" + url, "admin", "admin");
GrafanaClient client = new GrafanaClient(endpoint, "admin", "admin");
Awaitility.await().atMost(61, TimeUnit.SECONDS).until(
client::user,
u -> "admin".equals(u.login));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.logging.Logger;

import io.quarkus.arc.deployment.BeanArchiveIndexBuildItem;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
Expand All @@ -28,15 +33,18 @@
import io.quarkus.deployment.console.StartupLogCompressor;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.deployment.logging.LoggingSetupBuildItem;
import io.quarkus.deployment.metrics.MetricsCapabilityBuildItem;
import io.quarkus.devservices.common.ContainerLocator;
import io.quarkus.observability.common.config.ContainerConfig;
import io.quarkus.observability.common.config.ContainerConfigUtil;
import io.quarkus.observability.common.config.ModulesConfiguration;
import io.quarkus.observability.devresource.Container;
import io.quarkus.observability.devresource.DevResourceLifecycleManager;
import io.quarkus.observability.devresource.DevResources;
import io.quarkus.observability.devresource.ExtensionsCatalog;
import io.quarkus.observability.runtime.config.ObservabilityConfiguration;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.metrics.MetricsFactory;

@BuildSteps(onlyIfNot = IsNormal.class, onlyIf = { GlobalDevServicesConfig.Enabled.class,
ObservabilityDevServiceProcessor.IsEnabled.class })
Expand All @@ -46,6 +54,7 @@ class ObservabilityDevServiceProcessor {
private static final Map<String, DevServicesResultBuildItem.RunningDevService> devServices = new ConcurrentHashMap<>();
private static final Map<String, ContainerConfig> capturedDevServicesConfigurations = new ConcurrentHashMap<>();
private static final Map<String, Boolean> firstStart = new ConcurrentHashMap<>();
public static final DotName OTLP_REGISTRY = DotName.createSimple("io.micrometer.registry.otlp.OtlpMeterRegistry");

public static class IsEnabled implements BooleanSupplier {
ObservabilityConfiguration config;
Expand Down Expand Up @@ -74,7 +83,10 @@ public void startContainers(LaunchModeBuildItem launchMode,
CuratedApplicationShutdownBuildItem closeBuildItem,
LoggingSetupBuildItem loggingSetupBuildItem,
GlobalDevServicesConfig devServicesConfig,
BuildProducer<DevServicesResultBuildItem> services) {
BuildProducer<DevServicesResultBuildItem> services,
BeanArchiveIndexBuildItem indexBuildItem,
Capabilities capabilities,
Optional<MetricsCapabilityBuildItem> metricsConfiguration) {

if (!configuration.enabled()) {
log.infof("Observability dev services are disabled in config");
Expand Down Expand Up @@ -103,7 +115,11 @@ public void startContainers(LaunchModeBuildItem launchMode,

// only do get, not remove, so it can be re-used
DevServicesResultBuildItem.RunningDevService devService = devServices.get(devId);
ContainerConfig currentDevServicesConfiguration = dev.config(configuration);
ContainerConfig currentDevServicesConfiguration = dev.config(
configuration,
new ExtensionsCatalog(
capabilities.isPresent(Capability.OPENTELEMETRY_TRACER),
hasMicrometerOtlp(metricsConfiguration, indexBuildItem)));

if (devService != null) {
ContainerConfig capturedDevServicesConfiguration = capturedDevServicesConfigurations.get(devId);
Expand Down Expand Up @@ -152,19 +168,22 @@ public void startContainers(LaunchModeBuildItem launchMode,
}

if (firstStart.computeIfAbsent(devId, x -> true)) {
Runnable closeTask = () -> {
DevServicesResultBuildItem.RunningDevService current = devServices.get(devId);
if (current != null) {
try {
current.close();
} catch (Throwable t) {
log.errorf("Failed to stop %s container", devId, t);
Runnable closeTask = new Runnable() {
brunobat marked this conversation as resolved.
Show resolved Hide resolved
@Override
public void run() {
DevServicesResultBuildItem.RunningDevService current = devServices.get(devId);
if (current != null) {
try {
current.close();
} catch (Throwable t) {
log.errorf("Failed to stop %s container", devId, t);
}
}
firstStart.remove(devId);
//noinspection resource
devServices.remove(devId);
capturedDevServicesConfigurations.remove(devId);
}
firstStart.remove(devId);
//noinspection resource
devServices.remove(devId);
capturedDevServicesConfigurations.remove(devId);
};
closeBuildItem.addCloseTask(closeTask, true);
}
Expand All @@ -173,6 +192,18 @@ public void startContainers(LaunchModeBuildItem launchMode,
});
}

private static boolean hasMicrometerOtlp(Optional<MetricsCapabilityBuildItem> metricsConfiguration,
BeanArchiveIndexBuildItem indexBuildItem) {
if (metricsConfiguration.isPresent() &&
metricsConfiguration.get().metricsSupported(MetricsFactory.MICROMETER)) {
ClassInfo clazz = indexBuildItem.getIndex().getClassByName(OTLP_REGISTRY);
if (clazz != null) {
return true;
}
}
return false;
}

private DevServicesResultBuildItem.RunningDevService startContainer(
String devId,
DevResourceLifecycleManager<ContainerConfig> dev,
Expand All @@ -190,14 +221,17 @@ private DevServicesResultBuildItem.RunningDevService startContainer(
return null;
}

final Supplier<DevServicesResultBuildItem.RunningDevService> defaultContainerSupplier = () -> {
Container<?> container = dev.container(capturedDevServicesConfiguration, root);
timeout.ifPresent(container::withStartupTimeout);
Map<String, String> config = dev.start();
log.infof("Dev Service %s started, config: %s", devId, config);
return new DevServicesResultBuildItem.RunningDevService(
Feature.OBSERVABILITY.getName(), container.getContainerId(),
container.closeableCallback(capturedDevServicesConfiguration.serviceName()), config);
final Supplier<DevServicesResultBuildItem.RunningDevService> defaultContainerSupplier = new Supplier<DevServicesResultBuildItem.RunningDevService>() {
@Override
public DevServicesResultBuildItem.RunningDevService get() {
Container<?> container = dev.container(capturedDevServicesConfiguration, root);
timeout.ifPresent(container::withStartupTimeout);
Map<String, String> config = dev.start();
log.infof("Dev Service %s started, config: %s", devId, config);
return new DevServicesResultBuildItem.RunningDevService(
Feature.OBSERVABILITY.getName(), container.getContainerId(),
container.closeableCallback(capturedDevServicesConfiguration.serviceName()), config);
}
};

Map<String, String> config = new LinkedHashMap<>(); // old config
Expand All @@ -206,10 +240,13 @@ private DevServicesResultBuildItem.RunningDevService startContainer(
.locateContainer(
capturedDevServicesConfiguration.serviceName(), capturedDevServicesConfiguration.shared(),
LaunchMode.current(), (p, ca) -> config.putAll(dev.config(p, ca.getHost(), ca.getPort())))
.map(cid -> {
log.infof("Dev Service %s re-used, config: %s", devId, config);
return new DevServicesResultBuildItem.RunningDevService(Feature.OBSERVABILITY.getName(), cid,
null, config);
.map(new Function<String, DevServicesResultBuildItem.RunningDevService>() {
@Override
public DevServicesResultBuildItem.RunningDevService apply(String cid) {
log.infof("Dev Service %s re-used, config: %s", devId, config);
return new DevServicesResultBuildItem.RunningDevService(Feature.OBSERVABILITY.getName(), cid,
null, config);
}
})
.orElseGet(defaultContainerSupplier);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,18 @@ public interface DevResourceLifecycleManager<T extends ContainerConfig> extends
* @param configuration main observability configuration
* @return module's config
*/
@Deprecated
T config(ModulesConfiguration configuration);

/**
* Get resource's config from main observability configuration and extension catalog
*
* @param configuration main observability configuration
* @param catalog observability catalog. If OpenTelemetry or Micrometer are enabled.
* @return module's config
*/
T config(ModulesConfiguration configuration, ExtensionsCatalog catalog);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have this one call deprecated by default -- so we don't break existing impls (if there are any ...).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why to deprecate this one?... It's the new method.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not this one -- deprecate the old one.
And have the new one call the old one by default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, but they are needed for different things and I didn't want to mess things too much.

Copy link
Contributor

@alesj alesj Jul 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True ... but this way we can don't break any existing ones (if there are some :-) ...


/**
* Should we enable / start this dev resource.
* e.g. we could already have actual service running
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.observability.devresource;

/**
* Relevant Observability extensions present.
*/
public record ExtensionsCatalog(boolean hasOpenTelemetry,
boolean hasMicrometerOtlp) {
}
Loading
Loading