-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Support OpenTelemetry Log signal #30712
Comments
/cc @radcortez (opentelemetry) |
According to the linked page, the spec is now stable 😁 |
Yes @t1. But supporting it on Quarkus will take some time to implement and it's not in the shot term roadmap. |
I'm probably not deep enough into OTel, but isn't this about transporting log statements? If it is, I think this is crucial. I'm currently looking for a future-proof solution, and OTel looks very promising. But if Quarkus supports only traces and neither metrics nor logs (not to speak of profiling), I'll have to look for alternatives. Or do I get it wrong? |
OTel OTLP protocol output will be standard. There's native OTel Tracing support. You can already get Micrometer metrics output using the OTel's OTLP protocol and receive that in the standard OTel collector. Micrometer just provides a more convenient and complete API to define metrics. In relation to logs, there will be the need to forward current loggers output to OTel. That work will be done later. |
Hi @brunobat, To get logs exported to an OTEL collector is it possible to use some of the logs appenders provided in the examples OpenTelemetry Log Appenders ? I know that Quarkus relies on the JBoss Logging library but is it possible to use the same approach? |
That's the plan, however, logging is not yet supported by the |
@brunobat is there any update regarding the roadmap to implement this feature or any way to support? |
Hi, is not the best approach, but until the feature is ready we have managed to have the logs also exported by overwriting the default Open telemetry bean and providing our own addLoggerProviderCustomizer import io.opentelemetry.api.OpenTelemetry;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.runtime.StartupEvent;
import io.smallrye.config.Priorities;
import jakarta.annotation.Priority;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
@Singleton
@IfBuildProperty(name = "quarkus.otel.enabled", stringValue = "true")
public class OpenTelemetryStartup {
@Inject OpenTelemetry openTelemetry;
void onStart(@Priority(Priorities.APPLICATION - 1) @Observes StartupEvent event) {
// need @Inject to override OpenTelemetry default bean
}
} import static java.lang.Boolean.TRUE;
import static java.util.Collections.emptyList;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.IdGenerator;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.opentelemetry.runtime.config.build.OTelBuildConfig;
import io.quarkus.opentelemetry.runtime.config.runtime.OTelRuntimeConfig;
import io.quarkus.opentelemetry.runtime.tracing.DelayedAttributes;
import io.quarkus.opentelemetry.runtime.tracing.DropTargetsSampler;
import io.quarkus.opentelemetry.runtime.tracing.TracerRecorder;
import io.quarkus.opentelemetry.runtime.tracing.TracerUtil;
import io.quarkus.runtime.ApplicationConfig;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.NameIterator;
import io.smallrye.config.SmallRyeConfig;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.inject.Produces;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.microprofile.config.ConfigProvider;
// TODO: copied from
// https://github.com/quarkusio/quarkus/blob/3.2.7.Final/extensions/opentelemetry/runtime/src/main/java/io/quarkus/opentelemetry/runtime/OpenTelemetryProducer.java
// will be removed when quarkus adds support for logging, also remove from, also
// remove OpenTelemetryStartup
@Singleton
public class OpenTelemetryProducer {
@Inject Instance<IdGenerator> idGenerator;
@Inject @Any Instance<Resource> resources;
@Inject @Any Instance<DelayedAttributes> delayedAttributes;
@Inject @Any Instance<Sampler> sampler;
@Inject @Any Instance<SpanProcessor> spanProcessors;
@Inject OTelBuildConfig oTelBuildConfig;
@Inject OTelRuntimeConfig oTelRuntimeConfig;
@Inject ApplicationConfig appConfig;
@Produces
@IfBuildProperty(name = "quarkus.otel.enabled", stringValue = "true")
@Singleton
public OpenTelemetry getOpenTelemetry() {
final Map<String, String> oTelConfigs = getOtelConfigs();
if (oTelRuntimeConfig.sdkDisabled()) {
return AutoConfiguredOpenTelemetrySdk.builder()
.setResultAsGlobal(true)
.registerShutdownHook(false)
.addPropertiesSupplier(() -> oTelConfigs)
.build()
.getOpenTelemetrySdk();
}
final AutoConfiguredOpenTelemetrySdk autoConfiguredOpenTelemetrySdk =
AutoConfiguredOpenTelemetrySdk.builder()
.setResultAsGlobal(true)
.registerShutdownHook(false)
.addPropertiesSupplier(() -> oTelConfigs)
.setServiceClassLoader(Thread.currentThread().getContextClassLoader())
// no customization needed for spanExporter. Loads SPI from CDI
.addResourceCustomizer(
(existingResource, configProperties) -> {
if (oTelBuildConfig.traces().enabled().orElse(TRUE)) {
Resource consolidatedResource =
existingResource.merge(
Resource.create(delayedAttributes.get())); // from cdi
// if user explicitly set 'otel.service.name', make sure we don't override it
// with defaults
// inside resource customizer
String serviceName =
oTelRuntimeConfig
.serviceName()
.filter(sn -> !sn.equals(appConfig.name.orElse("unset")))
.orElse(null);
// Merge resource instances with env attributes
Resource resource =
resources.stream()
.reduce(Resource.empty(), Resource::merge)
.merge(
TracerUtil.mapResourceAttributes(
oTelRuntimeConfig.resourceAttributes().orElse(emptyList()),
serviceName)); // from properties
return consolidatedResource.merge(resource);
} else {
return Resource.builder().build();
}
})
.addSamplerCustomizer(
(existingSampler, configProperties) -> {
if (oTelBuildConfig.traces().enabled().orElse(TRUE)) {
final Sampler effectiveSampler =
sampler.stream()
.findFirst()
.map(Sampler.class::cast) // use CDI if it exists
.orElse(existingSampler);
// collect default filtering targets (Needed for all samplers)
List<String> dropTargets = new ArrayList<>();
if (oTelRuntimeConfig
.traces()
.suppressNonApplicationUris()) { // default is true
dropTargets.addAll(TracerRecorder.dropNonApplicationUriTargets);
}
if (!oTelRuntimeConfig.traces().includeStaticResources()) { // default is false
dropTargets.addAll(TracerRecorder.dropStaticResourceTargets);
}
// make sure dropped targets are not sampled
if (!dropTargets.isEmpty()) {
return new DropTargetsSampler(effectiveSampler, dropTargets);
} else {
return effectiveSampler;
}
} else {
return Sampler.alwaysOff();
}
})
.addTracerProviderCustomizer(
(builder, configProperties) -> {
if (oTelBuildConfig.traces().enabled().orElse(TRUE)) {
idGenerator.stream().findFirst().ifPresent(builder::setIdGenerator); // from cdi
spanProcessors.stream().forEach(builder::addSpanProcessor);
}
return builder;
})
.addLoggerProviderCustomizer(
(existing, configProperties) ->
existing.addLogRecordProcessor(
BatchLogRecordProcessor.builder(OtlpGrpcLogRecordExporter.getDefault())
.build()))
.build();
return autoConfiguredOpenTelemetrySdk.getOpenTelemetrySdk();
}
private Map<String, String> getOtelConfigs() {
Map<String, String> oTelConfigs = new HashMap<>();
SmallRyeConfig config = ConfigProvider.getConfig().unwrap(SmallRyeConfig.class);
// instruct OTel that we are using the AutoConfiguredOpenTelemetrySdk
oTelConfigs.put("otel.java.global-autoconfigure.enabled", "true");
// load new properties
for (String propertyName : config.getPropertyNames()) {
if (propertyName.startsWith("quarkus.otel.")) {
ConfigValue configValue = config.getConfigValue(propertyName);
if (configValue.getValue() != null) {
NameIterator name = new NameIterator(propertyName);
name.next();
oTelConfigs.put(name.getName().substring(name.getPosition() + 1), configValue.getValue());
}
}
}
return oTelConfigs;
}
} Best regards |
@loicmathieu can you submit your draft PR for this issue? |
Hi, |
Hi, draft PR is here: #38239 |
Quarkus >3.3.0 changed how OpenTelemetry is produced. How can I export the logs to OpenTelemetry collector? Should I use the agent that just exports the logs and disable trace/metrics instrumentation? |
I need to display logs in Jaeger, but I'm not able to export them with opentelemetry. Do you already have a solution for this? |
@alexalmeida52 you can follow #38239 that shoud provide this functionality. For the moment, there is some work to be done at the Quarkus OpenTelemetry implementation to be able to send logs via the logs bridge so the PR is blocked until this work is done. |
@alexalmeida52, as far as I know, Jaeger is not able to display logs, just traces. But you should check that in https://github.com/jaegertracing/jaeger |
Description
We need to support the OpenTelemetry logging signal available at: https://opentelemetry.io/docs/instrumentation/java/
The spec/impl is still under development (at the creation of this issue it's still experimental) and work should after it's marked as stable.
Depends on #29911
Implementation ideas
No response
The text was updated successfully, but these errors were encountered: