Skip to content

Commit

Permalink
Merge branch 'main' into no_java_sourceset_issue_grpc
Browse files Browse the repository at this point in the history
  • Loading branch information
cdsap authored Nov 12, 2024
2 parents 529cd27 + 10ed7b1 commit 354664f
Showing 53 changed files with 1,271 additions and 157 deletions.
81 changes: 81 additions & 0 deletions adr/0007-quarkus-workshop-structure.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
= Structure for Quarkus Workshops

* Status: _Accepted_
* Date: 2024-11-06
* Authors: @cescoffier
== Context and Problem Statement

Since the public release of Quarkus, we launched a hands-on workshop to help developers get started with it.
Known as the "Quarkus Superheroes" workshop, this workshop allowed developers to learn Quarkus by actively writing and running code in a structured environment, often at conferences or in classroom settings.

The "Quarkus Superheroes" workshop has been highly successful, delivered at various conferences and widely used by developers for self-study.
At the time, we anticipated additional workshops, leading us to establish a dedicated structure within a single repository: Quarkus Workshops (https://github.com/quarkusio/quarkus-workshops).

The initial (and still existing) structure was straightforward:

[source]
----
.
├── README.md
└── quarkus-workshop-super-heroes/
├── dist
├── docs
├── super-heroes
├── README.adoc
└── pom.xml
----

Although this structure was meant to support multiple workshops, only the "Quarkus Superheroes" workshop was added.
Instead of separate workshops, we expanded this initial workshop with additional steps and features.

As we now develop new workshops on various topics, we face limitations with the single repository structure.
For example, the Quarkus LangChain4J workshop was created separately to demonstrate Quarkus LangChain4J usage, yet it isn’t integrated into the main workshop repository.
Additionally, having a single repository complicates using GitHub Pages for documentation.

Given the current and future workshops, it’s essential to reconsider the structure to allow easier management and discoverability of each workshop.

== Proposed New Structure

Our experience shows that hosting all workshops in one repository isn’t optimal. We propose a new structure as follows:

1. Each workshop will be hosted in its own repository.
This simplifies management, avoids conflicts in `README` and documentation setup, and improves workshop discoverability.
2. Naming convention: Each workshop repository should follow the format `quarkus-workshop-<topic>`, where `<topic>` represents the workshop subject (e.g., `quarkus-workshop-superheroes`, `quarkus-workshop-langchain4j`).
3. Documentation should be hosted with GitHub Pages in each repository, making each workshop more accessible.
4. Each workshop repository should have the `workshop` topic to facilitate discoverability.
5. We will keep https://quarkus.io/quarkus-workshops/ as a landing page, which people can use to find workshops.
In order to preserve the GitHub history, the quarkus-workshops repository should be renamed to https://quarkus.io/quarkus-workshop-superheroes, and then a new repository should be created, using the old name, `quarkus-workshops`.
6. This _landing_ repository can also be used to host redirects. For example, the existing URL https://quarkus.io/quarkus-workshops/super-heroes/ should be kept valid by using a redirect.

== Considered Options

=== Option 1: Continue with the current single-repository approach

This would mean keeping all workshops under the existing repository.
However, as observed, this approach has not met expectations and makes workshop management more challenging.

=== Option 2: Create a separate organization for workshops

A dedicated organization could host all workshops, offering a single access point.
However, this approach could reduce discoverability, but would not use the Quarkus organization’s CI resources.
CI resource usage is minor, as workshops are not frequently updated.

== Consequences

=== Positive

* Simplified workshop management.
* Greater autonomy for workshop maintainers.
* Consolidation of workshops previously hosted in separate repositories.

=== Negative

* Lack of a central place to list all workshops. This could be mitigated by creating a dedicated page on the Quarkus website.
* Potential CI resource shortage as each workshop repository uses _quarkusio_ organization CI resources.
However, this is unlikely to be a significant issue, as, generally, workshops don't use much CI resources.
That being said, it would require monitoring to ensure it doesn't become a problem.

=== Neutral

* Existing workshops would need restructuring to align with the new approach, especially the Quarkus Superheroes workshop.
8 changes: 8 additions & 0 deletions docs/src/main/asciidoc/_includes/opentelemetry-config.adoc
Original file line number Diff line number Diff line change
@@ -34,3 +34,11 @@ quarkus.otel.exporter.otlp.logs.endpoint=http://logs-uri:4317 // <3>
<1> The endpoint for the traces exporter.
<2> The endpoint for the metrics exporter.
<3> The endpoint for the logs exporter.

If you need that your spans and logs to be exported directly as they finish
(e.g. in a serverless environment / application), you can set this property to `true`.
This replaces the default batching of data.
[source,properties]
----
quarkus.otel.simple=true
----
Original file line number Diff line number Diff line change
@@ -96,6 +96,7 @@ AdditionalBeanBuildItem ensureProducerIsRetained() {
return AdditionalBeanBuildItem.builder()
.setUnremovable()
.addBeanClasses(
AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SimpleLogRecordProcessorCustomizer.class,
AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracingResourceCustomizer.class,
AutoConfiguredOpenTelemetrySdkBuilderCustomizer.SamplerCustomizer.class,
AutoConfiguredOpenTelemetrySdkBuilderCustomizer.TracerProviderCustomizer.class,
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@
import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterConfigBuilder;
import io.quarkus.opentelemetry.runtime.config.runtime.exporter.OtlpExporterRuntimeConfig;
import io.quarkus.opentelemetry.runtime.exporter.otlp.OTelExporterRecorder;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundSpanProcessor;
import io.quarkus.tls.TlsConfigurationRegistry;
import io.quarkus.tls.TlsRegistryBuildItem;
import io.quarkus.vertx.core.deployment.CoreVertxBuildItem;
@@ -84,7 +84,8 @@ void config(BuildProducer<RunTimeConfigBuilderBuildItem> runTimeConfigBuilderPro
@BuildStep(onlyIf = OtlpExporterProcessor.OtlpTracingExporterEnabled.class)
@Record(ExecutionTime.RUNTIME_INIT)
@Consume(TlsRegistryBuildItem.class)
void createBatchSpanProcessor(OTelExporterRecorder recorder,
void createSpanProcessor(OTelExporterRecorder recorder,
OTelBuildConfig oTelBuildConfig,
OTelRuntimeConfig otelRuntimeConfig,
OtlpExporterRuntimeConfig exporterRuntimeConfig,
CoreVertxBuildItem vertxBuildItem,
@@ -95,16 +96,16 @@ void createBatchSpanProcessor(OTelExporterRecorder recorder,
return;
}
syntheticBeanBuildItemBuildProducer.produce(SyntheticBeanBuildItem
.configure(LateBoundBatchSpanProcessor.class)
.configure(LateBoundSpanProcessor.class)
.types(SpanProcessor.class)
.setRuntimeInit()
.scope(Singleton.class)
.unremovable()
.addInjectionPoint(ParameterizedType.create(DotName.createSimple(Instance.class),
new Type[] { ClassType.create(DotName.createSimple(SpanExporter.class.getName())) }, null))
.addInjectionPoint(ClassType.create(DotName.createSimple(TlsConfigurationRegistry.class)))
.createWith(recorder.batchSpanProcessorForOtlp(otelRuntimeConfig, exporterRuntimeConfig,
vertxBuildItem.getVertx()))
.createWith(recorder.spanProcessorForOtlp(oTelBuildConfig, otelRuntimeConfig,
exporterRuntimeConfig, vertxBuildItem.getVertx()))
.done());
}

Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig;
import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundSpanProcessor;
import io.quarkus.opentelemetry.runtime.tracing.intrumentation.vertx.HttpInstrumenterVertxTracer;
import io.quarkus.test.QuarkusUnitTest;
import io.vertx.core.spi.observability.HttpRequest;
@@ -26,7 +26,7 @@ public class OpenTelemetryDisabledSdkTest {
.overrideConfigKey("quarkus.otel.sdk.disabled", "true");

@Inject
LateBoundBatchSpanProcessor batchSpanProcessor;
LateBoundSpanProcessor spanProcessor;

@Inject
OpenTelemetry openTelemetry;
@@ -40,7 +40,7 @@ public class OpenTelemetryDisabledSdkTest {
@Test
void testNoTracer() {
// The OTel API doesn't provide a clear way to check if a tracer is an effective NOOP tracer.
Assertions.assertTrue(batchSpanProcessor.isDelegateNull(), "BatchSpanProcessor delegate must not be set");
Assertions.assertTrue(spanProcessor.isDelegateNull(), "SpanProcessor delegate must not be set");
}

@Test
Original file line number Diff line number Diff line change
@@ -11,7 +11,7 @@

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundBatchSpanProcessor;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.LateBoundSpanProcessor;
import io.quarkus.test.QuarkusUnitTest;

public class OtlpTraceExporterDisabledTest {
@@ -25,15 +25,15 @@ public class OtlpTraceExporterDisabledTest {
OpenTelemetry openTelemetry;

@Inject
Instance<LateBoundBatchSpanProcessor> lateBoundBatchSpanProcessorInstance;
Instance<LateBoundSpanProcessor> lateBoundSpanProcessorInstance;

@Inject
Instance<MetricExporter> metricExporters;

@Test
void testOpenTelemetryButNoBatchSpanProcessor() {
assertNotNull(openTelemetry);
assertFalse(lateBoundBatchSpanProcessorInstance.isResolvable());
assertFalse(lateBoundSpanProcessorInstance.isResolvable());
assertFalse(metricExporters.isResolvable());
}
}
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.lang.reflect.InvocationTargetException;
import java.util.Locale;

import jakarta.inject.Inject;

@@ -36,6 +37,7 @@ public class OpenTelemetrySamplerConfigTest {
void test() throws NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
Sampler sampler = TestUtil.getSampler(openTelemetry);

assertEquals(String.format("TraceIdRatioBased{%.6f}", 0.5d), sampler.getDescription());
// Fix the locale to ROOT, so we don't get 0,500000
assertEquals(String.format(Locale.ROOT, "TraceIdRatioBased{%.6f}", 0.5d), sampler.getDescription());
}
}
Original file line number Diff line number Diff line change
@@ -18,6 +18,10 @@
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.common.Clock;
import io.opentelemetry.sdk.logs.LogRecordProcessor;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.IdGenerator;
@@ -27,7 +31,7 @@
import io.quarkus.arc.All;
import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig;
import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.RemoveableLateBoundBatchSpanProcessor;
import io.quarkus.opentelemetry.runtime.exporter.otlp.tracing.RemoveableLateBoundSpanProcessor;
import io.quarkus.opentelemetry.runtime.propagation.TextMapPropagatorCustomizer;
import io.quarkus.opentelemetry.runtime.tracing.DelayedAttributes;
import io.quarkus.opentelemetry.runtime.tracing.DropTargetsSampler;
@@ -39,6 +43,47 @@ public interface AutoConfiguredOpenTelemetrySdkBuilderCustomizer {

void customize(AutoConfiguredOpenTelemetrySdkBuilder builder);

@Singleton
final class SimpleLogRecordProcessorCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer {
private SimpleLogRecordProcessorBiFunction biFunction;

public SimpleLogRecordProcessorCustomizer(
OTelBuildConfig oTelBuildConfig,
Instance<LogRecordExporter> ilre) {
if (oTelBuildConfig.simple() && ilre.isResolvable()) {
LogRecordProcessor lrp = SimpleLogRecordProcessor.create(ilre.get());
this.biFunction = new SimpleLogRecordProcessorBiFunction(lrp);
}
}

@Override
public void customize(AutoConfiguredOpenTelemetrySdkBuilder builder) {
if (biFunction != null) {
builder.addLogRecordProcessorCustomizer(biFunction);
}
}
}

class SimpleLogRecordProcessorBiFunction
implements BiFunction<LogRecordProcessor, ConfigProperties, LogRecordProcessor> {

private final LogRecordProcessor logRecordProcessor;

public SimpleLogRecordProcessorBiFunction(LogRecordProcessor logRecordProcessor) {
this.logRecordProcessor = logRecordProcessor;
}

@Override
public LogRecordProcessor apply(LogRecordProcessor lrp, ConfigProperties cp) {
// only change batch lrp, leave others
if (lrp instanceof BatchLogRecordProcessor) {
return logRecordProcessor;
} else {
return lrp;
}
}
}

@Singleton
final class TracingResourceCustomizer implements AutoConfiguredOpenTelemetrySdkBuilderCustomizer {

@@ -174,7 +219,7 @@ public SdkTracerProviderBuilder apply(SdkTracerProviderBuilder tracerProviderBui
spanProcessors.stream().filter(new Predicate<SpanProcessor>() {
@Override
public boolean test(SpanProcessor sp) {
return !(sp instanceof RemoveableLateBoundBatchSpanProcessor);
return !(sp instanceof RemoveableLateBoundSpanProcessor);
}
})
.forEach(tracerProviderBuilder::addSpanProcessor);
Original file line number Diff line number Diff line change
@@ -37,6 +37,17 @@ public interface OTelBuildConfig {
@WithDefault("true")
boolean enabled();

/**
* Should we use simple processor for spans and log records.
* This will disable batch processing and the exporter will send
* telemetry data right away.
* This is recommended for serverless applications.
* <p>
* Defaults to <code>false</code>.
*/
@WithDefault("false")
boolean simple();

/**
* Trace exporter configurations.
*/
Loading

0 comments on commit 354664f

Please sign in to comment.