Jaeger Operator in Kubernetes: Complete Java Guide

Introduction

Jaeger is a popular distributed tracing system used for monitoring and troubleshooting microservices. The Jaeger Operator simplifies Jaeger deployment and management in Kubernetes. This guide covers how to interact with Jaeger Operator and Jaeger instances using Java.

Dependencies and Setup

1. Maven Dependencies

<properties>
<fabric8.version>6.9.2</fabric8.version>
<opentelemetry.version>1.34.1</opentelemetry.version>
<jaeger.version>1.8.1</jaeger.version>
<spring-boot.version>3.2.0</spring-boot.version>
</properties>
<dependencies>
<!-- Kubernetes Client -->
<dependency>
<groupId>io.fabric8</groupId>
<artifactId>kubernetes-client</artifactId>
<version>${fabric8.version}</version>
</dependency>
<!-- OpenTelemetry API -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- OpenTelemetry SDK -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- Jaeger Exporter -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-jaeger</artifactId>
<version>${opentelemetry.version}</version>
</dependency>
<!-- OpenTelemetry Instrumentation -->
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-annotations</artifactId>
<version>1.32.0</version>
</dependency>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring-boot.version}</version>
<scope>test</scope>
</dependency>
<!-- TestContainers -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.3</version>
<scope>test</scope>
</dependency>
</dependencies>

Jaeger CRD Models

2. Jaeger Custom Resource Definitions

package com.example.jaeger.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.fabric8.kubernetes.api.model.Namespaced;
import io.fabric8.kubernetes.client.CustomResource;
import io.fabric8.kubernetes.model.annotation.Group;
import io.fabric8.kubernetes.model.annotation.Kind;
import io.fabric8.kubernetes.model.annotation.Version;
@Group("jaegertracing.io")
@Version("v1")
@Kind("Jaeger")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Jaeger extends CustomResource<JaegerSpec, JaegerStatus> implements Namespaced {
@Override
protected JaegerSpec initSpec() {
return new JaegerSpec();
}
@Override
protected JaegerStatus initStatus() {
return new JaegerStatus();
}
}
package com.example.jaeger.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JaegerSpec {
private JaegerStrategy strategy;
private JaegerStorageSpec storage;
private JaegerIngressSpec ingress;
private JaegerAgentSpec agent;
private JaegerCollectorSpec collector;
private JaegerQuerySpec query;
private JaegerUISpec ui;
private JaegerSamplingSpec sampling;
private Map<String, String> annotations;
private Map<String, String> labels;
private JaegerResources resources;
private String serviceAccount;
private JaegerSecurityContext securityContext;
private JaegerImage image;
private JaegerVolumeMounts volumeMounts;
private JaegerAffinity affinity;
private JaegerTolerations tolerations;
// Getters and Setters
public JaegerStrategy getStrategy() { return strategy; }
public void setStrategy(JaegerStrategy strategy) { this.strategy = strategy; }
public JaegerStorageSpec getStorage() { return storage; }
public void setStorage(JaegerStorageSpec storage) { this.storage = storage; }
public JaegerIngressSpec getIngress() { return ingress; }
public void setIngress(JaegerIngressSpec ingress) { this.ingress = ingress; }
public JaegerAgentSpec getAgent() { return agent; }
public void setAgent(JaegerAgentSpec agent) { this.agent = agent; }
public JaegerCollectorSpec getCollector() { return collector; }
public void setCollector(JaegerCollectorSpec collector) { this.collector = collector; }
public JaegerQuerySpec getQuery() { return query; }
public void setQuery(JaegerQuerySpec query) { this.query = query; }
public JaegerUISpec getUi() { return ui; }
public void setUi(JaegerUISpec ui) { this.ui = ui; }
public JaegerSamplingSpec getSampling() { return sampling; }
public void setSampling(JaegerSamplingSpec sampling) { this.sampling = sampling; }
public Map<String, String> getAnnotations() { return annotations; }
public void setAnnotations(Map<String, String> annotations) { this.annotations = annotations; }
public Map<String, String> getLabels() { return labels; }
public void setLabels(Map<String, String> labels) { this.labels = labels; }
public JaegerResources getResources() { return resources; }
public void setResources(JaegerResources resources) { this.resources = resources; }
public String getServiceAccount() { return serviceAccount; }
public void setServiceAccount(String serviceAccount) { this.serviceAccount = serviceAccount; }
public JaegerSecurityContext getSecurityContext() { return securityContext; }
public void setSecurityContext(JaegerSecurityContext securityContext) { this.securityContext = securityContext; }
public JaegerImage getImage() { return image; }
public void setImage(JaegerImage image) { this.image = image; }
public JaegerVolumeMounts getVolumeMounts() { return volumeMounts; }
public void setVolumeMounts(JaegerVolumeMounts volumeMounts) { this.volumeMounts = volumeMounts; }
public JaegerAffinity getAffinity() { return affinity; }
public void setAffinity(JaegerAffinity affinity) { this.affinity = affinity; }
public JaegerTolerations getTolerations() { return tolerations; }
public void setTolerations(JaegerTolerations tolerations) { this.tolerations = tolerations; }
public enum JaegerStrategy {
AllInOne, Production, Streaming
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerStorageSpec {
private JaegerStorageType type;
private ElasticsearchSpec elasticsearch;
private CassandraSpec cassandra;
private String secretName;
private Map<String, String> options;
// Getters and Setters
public JaegerStorageType getType() { return type; }
public void setType(JaegerStorageType type) { this.type = type; }
public ElasticsearchSpec getElasticsearch() { return elasticsearch; }
public void setElasticsearch(ElasticsearchSpec elasticsearch) { this.elasticsearch = elasticsearch; }
public CassandraSpec getCassandra() { return cassandra; }
public void setCassandra(CassandraSpec cassandra) { this.cassandra = cassandra; }
public String getSecretName() { return secretName; }
public void setSecretName(String secretName) { this.secretName = secretName; }
public Map<String, String> getOptions() { return options; }
public void setOptions(Map<String, String> options) { this.options = options; }
public enum JaegerStorageType {
memory, elasticsearch, cassandra, badger, grpc-plugin
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class ElasticsearchSpec {
private String serverUrl;
private String username;
private String password;
private Integer shards;
private Integer replicas;
private Integer maxNumSpans;
private String indexPrefix;
private Boolean createIndexTemplates;
// Getters and Setters
public String getServerUrl() { return serverUrl; }
public void setServerUrl(String serverUrl) { this.serverUrl = serverUrl; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public Integer getShards() { return shards; }
public void setShards(Integer shards) { this.shards = shards; }
public Integer getReplicas() { return replicas; }
public void setReplicas(Integer replicas) { this.replicas = replicas; }
public Integer getMaxNumSpans() { return maxNumSpans; }
public void setMaxNumSpans(Integer maxNumSpans) { this.maxNumSpans = maxNumSpans; }
public String getIndexPrefix() { return indexPrefix; }
public void setIndexPrefix(String indexPrefix) { this.indexPrefix = indexPrefix; }
public Boolean getCreateIndexTemplates() { return createIndexTemplates; }
public void setCreateIndexTemplates(Boolean createIndexTemplates) { this.createIndexTemplates = createIndexTemplates; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class CassandraSpec {
private String servers;
private String keyspace;
private String username;
private String password;
private Integer replicationFactor;
private Boolean traceTTL;
// Getters and Setters
public String getServers() { return servers; }
public void setServers(String servers) { this.servers = servers; }
public String getKeyspace() { return keyspace; }
public void setKeyspace(String keyspace) { this.keyspace = keyspace; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public Integer getReplicationFactor() { return replicationFactor; }
public void setReplicationFactor(Integer replicationFactor) { this.replicationFactor = replicationFactor; }
public Boolean getTraceTTL() { return traceTTL; }
public void setTraceTTL(Boolean traceTTL) { this.traceTTL = traceTTL; }
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerIngressSpec {
private Boolean enabled;
private String className;
private Map<String, String> annotations;
private JaegerIngressSecurity security;
// Getters and Setters
public Boolean getEnabled() { return enabled; }
public void setEnabled(Boolean enabled) { this.enabled = enabled; }
public String getClassName() { return className; }
public void setClassName(String className) { this.className = className; }
public Map<String, String> getAnnotations() { return annotations; }
public void setAnnotations(Map<String, String> annotations) { this.annotations = annotations; }
public JaegerIngressSecurity getSecurity() { return security; }
public void setSecurity(JaegerIngressSecurity security) { this.security = security; }
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerIngressSecurity {
private Boolean enabled;
private String secretName;
// Getters and Setters
public Boolean getEnabled() { return enabled; }
public void setEnabled(Boolean enabled) { this.enabled = enabled; }
public String getSecretName() { return secretName; }
public void setSecretName(String secretName) { this.secretName = secretName; }
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerAgentSpec {
private String strategy;
private JaegerResources resources;
private Map<String, String> options;
// Getters and Setters
public String getStrategy() { return strategy; }
public void setStrategy(String strategy) { this.strategy = strategy; }
public JaegerResources getResources() { return resources; }
public void setResources(JaegerResources resources) { this.resources = resources; }
public Map<String, String> getOptions() { return options; }
public void setOptions(Map<String, String> options) { this.options = options; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerCollectorSpec {
private JaegerResources resources;
private Map<String, String> options;
// Getters and Setters
public JaegerResources getResources() { return resources; }
public void setResources(JaegerResources resources) { this.resources = resources; }
public Map<String, String> getOptions() { return options; }
public void setOptions(Map<String, String> options) { this.options = options; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerQuerySpec {
private JaegerResources resources;
private Map<String, String> options;
// Getters and Setters
public JaegerResources getResources() { return resources; }
public void setResources(JaegerResources resources) { this.resources = resources; }
public Map<String, String> getOptions() { return options; }
public void setOptions(Map<String, String> options) { this.options = options; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerUISpec {
private Map<String, String> options;
// Getters and Setters
public Map<String, String> getOptions() { return options; }
public void setOptions(Map<String, String> options) { this.options = options; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerSamplingSpec {
private String type;
private Double param;
private String samplingServerUrl;
private Integer maxOperations;
private String configuration;
// Getters and Setters
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public Double getParam() { return param; }
public void setParam(Double param) { this.param = param; }
public String getSamplingServerUrl() { return samplingServerUrl; }
public void setSamplingServerUrl(String samplingServerUrl) { this.samplingServerUrl = samplingServerUrl; }
public Integer getMaxOperations() { return maxOperations; }
public void setMaxOperations(Integer maxOperations) { this.maxOperations = maxOperations; }
public String getConfiguration() { return configuration; }
public void setConfiguration(String configuration) { this.configuration = configuration; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerResources {
private Map<String, String> requests;
private Map<String, String> limits;
// Getters and Setters
public Map<String, String> getRequests() { return requests; }
public void setRequests(Map<String, String> requests) { this.requests = requests; }
public Map<String, String> getLimits() { return limits; }
public void setLimits(Map<String, String> limits) { this.limits = limits; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerSecurityContext {
private Long runAsUser;
private Long runAsGroup;
private Boolean runAsNonRoot;
// Getters and Setters
public Long getRunAsUser() { return runAsUser; }
public void setRunAsUser(Long runAsUser) { this.runAsUser = runAsUser; }
public Long getRunAsGroup() { return runAsGroup; }
public void setRunAsGroup(Long runAsGroup) { this.runAsGroup = runAsGroup; }
public Boolean getRunAsNonRoot() { return runAsNonRoot; }
public void setRunAsNonRoot(Boolean runAsNonRoot) { this.runAsNonRoot = runAsNonRoot; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerImage {
private String image;
private String tag;
private String imagePullPolicy;
// Getters and Setters
public String getImage() { return image; }
public void setImage(String image) { this.image = image; }
public String getTag() { return tag; }
public void setTag(String tag) { this.tag = tag; }
public String getImagePullPolicy() { return imagePullPolicy; }
public void setImagePullPolicy(String imagePullPolicy) { this.imagePullPolicy = imagePullPolicy; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerVolumeMounts {
private String name;
private String mountPath;
private Boolean readOnly;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getMountPath() { return mountPath; }
public void setMountPath(String mountPath) { this.mountPath = mountPath; }
public Boolean getReadOnly() { return readOnly; }
public void setReadOnly(Boolean readOnly) { this.readOnly = readOnly; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerAffinity {
private Map<String, Object> nodeAffinity;
private Map<String, Object> podAffinity;
private Map<String, Object> podAntiAffinity;
// Getters and Setters
public Map<String, Object> getNodeAffinity() { return nodeAffinity; }
public void setNodeAffinity(Map<String, Object> nodeAffinity) { this.nodeAffinity = nodeAffinity; }
public Map<String, Object> getPodAffinity() { return podAffinity; }
public void setPodAffinity(Map<String, Object> podAffinity) { this.podAffinity = podAffinity; }
public Map<String, Object> getPodAntiAffinity() { return podAntiAffinity; }
public void setPodAntiAffinity(Map<String, Object> podAntiAffinity) { this.podAntiAffinity = podAntiAffinity; }
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerTolerations {
private String key;
private String operator;
private String value;
private String effect;
private Long tolerationSeconds;
// Getters and Setters
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getOperator() { return operator; }
public void setOperator(String operator) { this.operator = operator; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
public String getEffect() { return effect; }
public void setEffect(String effect) { this.effect = effect; }
public Long getTolerationSeconds() { return tolerationSeconds; }
public void setTolerationSeconds(Long tolerationSeconds) { this.tolerationSeconds = tolerationSeconds; }
}
}
package com.example.jaeger.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
import java.util.Map;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class JaegerStatus {
private String phase;
private String message;
private List<JaegerComponentStatus> components;
private String version;
private Map<String, String> conditions;
// Getters and Setters
public String getPhase() { return phase; }
public void setPhase(String phase) { this.phase = phase; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public List<JaegerComponentStatus> getComponents() { return components; }
public void setComponents(List<JaegerComponentStatus> components) { this.components = components; }
public String getVersion() { return version; }
public void setVersion(String version) { this.version = version; }
public Map<String, String> getConditions() { return conditions; }
public void setConditions(Map<String, String> conditions) { this.conditions = conditions; }
@JsonInclude(JsonInclude.Include.NON_NULL)
public static class JaegerComponentStatus {
private String name;
private String status;
private String message;
private Integer replicas;
private Integer readyReplicas;
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public Integer getReplicas() { return replicas; }
public void setReplicas(Integer replicas) { this.replicas = replicas; }
public Integer getReadyReplicas() { return readyReplicas; }
public void setReadyReplicas(Integer readyReplicas) { this.readyReplicas = readyReplicas; }
}
}

Jaeger Operator Management

3. Jaeger Operator Service

package com.example.jaeger.service;
import com.example.jaeger.model.Jaeger;
import com.example.jaeger.model.JaegerStatus;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.KubernetesClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class JaegerOperatorService {
private static final Logger log = LoggerFactory.getLogger(JaegerOperatorService.class);
private final KubernetesClient kubernetesClient;
private final String namespace;
public JaegerOperatorService(KubernetesClient kubernetesClient,
@Value("${jaeger.operator.namespace:default}") String namespace) {
this.kubernetesClient = kubernetesClient;
this.namespace = namespace;
}
public Jaeger createJaegerInstance(String name, Jaeger.Spec spec) {
log.info("Creating Jaeger instance: {} in namespace: {}", name, namespace);
Jaeger jaeger = new Jaeger();
jaeger.getMetadata().setName(name);
jaeger.getMetadata().setNamespace(namespace);
jaeger.setSpec(spec);
return kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.resource(jaeger)
.create();
}
public Jaeger createAllInOneJaeger(String name) {
log.info("Creating AllInOne Jaeger instance: {}", name);
Jaeger.Spec spec = new Jaeger.Spec();
spec.setStrategy(Jaeger.Spec.JaegerStrategy.AllInOne);
// Configure storage
Jaeger.Spec.JaegerStorageSpec storageSpec = new Jaeger.Spec.JaegerStorageSpec();
storageSpec.setType(Jaeger.Spec.JaegerStorageSpec.JaegerStorageType.memory);
spec.setStorage(storageSpec);
// Configure UI
Jaeger.Spec.JaegerUISpec uiSpec = new Jaeger.Spec.JaegerUISpec();
uiSpec.setOptions(Map.of(
"query.bearer-token-propagation", "true"
));
spec.setUi(uiSpec);
return createJaegerInstance(name, spec);
}
public Jaeger createProductionJaeger(String name, String elasticsearchUrl) {
log.info("Creating Production Jaeger instance: {} with Elasticsearch: {}", name, elasticsearchUrl);
Jaeger.Spec spec = new Jaeger.Spec();
spec.setStrategy(Jaeger.Spec.JaegerStrategy.Production);
// Configure Elasticsearch storage
Jaeger.Spec.JaegerStorageSpec storageSpec = new Jaeger.Spec.JaegerStorageSpec();
storageSpec.setType(Jaeger.Spec.JaegerStorageSpec.JaegerStorageType.elasticsearch);
Jaeger.Spec.JaegerStorageSpec.ElasticsearchSpec esSpec = 
new Jaeger.Spec.JaegerStorageSpec.ElasticsearchSpec();
esSpec.setServerUrl(elasticsearchUrl);
esSpec.setCreateIndexTemplates(true);
storageSpec.setElasticsearch(esSpec);
spec.setStorage(storageSpec);
// Configure collector
Jaeger.Spec.JaegerCollectorSpec collectorSpec = new Jaeger.Spec.JaegerCollectorSpec();
collectorSpec.setResources(createStandardResources());
spec.setCollector(collectorSpec);
// Configure query
Jaeger.Spec.JaegerQuerySpec querySpec = new Jaeger.Spec.JaegerQuerySpec();
querySpec.setResources(createStandardResources());
spec.setQuery(querySpec);
return createJaegerInstance(name, spec);
}
public Jaeger createStreamingJaeger(String name, String elasticsearchUrl, String kafkaBootstrapServers) {
log.info("Creating Streaming Jaeger instance: {}", name);
Jaeger.Spec spec = new Jaeger.Spec();
spec.setStrategy(Jaeger.Spec.JaegerStrategy.Streaming);
// Configure Elasticsearch storage
Jaeger.Spec.JaegerStorageSpec storageSpec = new Jaeger.Spec.JaegerStorageSpec();
storageSpec.setType(Jaeger.Spec.JaegerStorageSpec.JaegerStorageType.elasticsearch);
Jaeger.Spec.JaegerStorageSpec.ElasticsearchSpec esSpec = 
new Jaeger.Spec.JaegerStorageSpec.ElasticsearchSpec();
esSpec.setServerUrl(elasticsearchUrl);
storageSpec.setElasticsearch(esSpec);
spec.setStorage(storageSpec);
// Configure Kafka options
spec.setCollector(new Jaeger.Spec.JaegerCollectorSpec());
spec.getCollector().setOptions(Map.of(
"kafka.producer.brokers", kafkaBootstrapServers,
"kafka.producer.topic", "jaeger-spans"
));
return createJaegerInstance(name, spec);
}
public Optional<Jaeger> getJaegerInstance(String name) {
try {
Jaeger jaeger = kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.withName(name)
.get();
return Optional.ofNullable(jaeger);
} catch (KubernetesClientException e) {
log.error("Error retrieving Jaeger instance: {}", name, e);
return Optional.empty();
}
}
public List<Jaeger> listJaegerInstances() {
return kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.list()
.getItems();
}
public List<Jaeger> listJaegerInstancesByLabel(String labelSelector) {
return kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.withLabelSelector(labelSelector)
.list()
.getItems();
}
public boolean deleteJaegerInstance(String name) {
try {
return kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.withName(name)
.delete();
} catch (KubernetesClientException e) {
log.error("Error deleting Jaeger instance: {}", name, e);
return false;
}
}
public Jaeger updateJaegerInstance(String name, Jaeger.Spec updatedSpec) {
return kubernetesClient.resources(Jaeger.class)
.inNamespace(namespace)
.withName(name)
.edit(jaeger -> {
jaeger.setSpec(updatedSpec);
return jaeger;
});
}
public JaegerStatus getJaegerStatus(String name) {
Optional<Jaeger> jaegerOpt = getJaegerInstance(name);
return jaegerOpt.map(Jaeger::getStatus).orElse(null);
}
public boolean isJaegerReady(String name) {
JaegerStatus status = getJaegerStatus(name);
return status != null && "Running".equals(status.getPhase());
}
public void waitForJaegerReady(String name, long timeoutMillis) throws InterruptedException {
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < timeoutMillis) {
if (isJaegerReady(name)) {
log.info("Jaeger instance {} is ready", name);
return;
}
Thread.sleep(5000); // Check every 5 seconds
}
throw new RuntimeException("Timeout waiting for Jaeger instance to be ready: " + name);
}
private Jaeger.Spec.JaegerResources createStandardResources() {
Jaeger.Spec.JaegerResources resources = new Jaeger.Spec.JaegerResources();
resources.setRequests(Map.of(
"cpu", "500m",
"memory", "512Mi"
));
resources.setLimits(Map.of(
"cpu", "1000m",
"memory", "1Gi"
));
return resources;
}
}

OpenTelemetry Integration

4. Tracing Configuration Service

package com.example.jaeger.service;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.semconv.ResourceAttributes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PreDestroy;
import java.util.concurrent.TimeUnit;
@Service
public class TracingService {
private static final Logger log = LoggerFactory.getLogger(TracingService.class);
private final OpenTelemetry openTelemetry;
private final Tracer tracer;
private final String serviceName;
private final String jaegerEndpoint;
public TracingService(
@Value("${spring.application.name:unknown-service}") String serviceName,
@Value("${jaeger.endpoint:http://localhost:14250}") String jaegerEndpoint) {
this.serviceName = serviceName;
this.jaegerEndpoint = jaegerEndpoint;
this.openTelemetry = initializeOpenTelemetry();
this.tracer = openTelemetry.getTracer(serviceName);
log.info("Initialized tracing for service: {} with Jaeger endpoint: {}", serviceName, jaegerEndpoint);
}
private OpenTelemetry initializeOpenTelemetry() {
// Create Jaeger exporter
JaegerGrpcSpanExporter jaegerExporter = JaegerGrpcSpanExporter.builder()
.setEndpoint(jaegerEndpoint)
.setTimeout(30, TimeUnit.SECONDS)
.build();
// Create resource
Resource resource = Resource.getDefault()
.merge(Resource.create(Attributes.of(
ResourceAttributes.SERVICE_NAME, serviceName,
ResourceAttributes.DEPLOYMENT_ENVIRONMENT, "production"
)));
// Create tracer provider
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
.addSpanProcessor(BatchSpanProcessor.builder(jaegerExporter).build())
.setResource(resource)
.build();
// Create OpenTelemetry instance
return OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.buildAndRegisterGlobal();
}
public OpenTelemetry getOpenTelemetry() {
return openTelemetry;
}
public Tracer getTracer() {
return tracer;
}
public Tracer getTracer(String instrumentationName) {
return openTelemetry.getTracer(instrumentationName);
}
public Tracer getTracer(String instrumentationName, String version) {
return openTelemetry.getTracer(instrumentationName, version);
}
@PreDestroy
public void shutdown() {
log.info("Shutting down tracing service");
if (openTelemetry instanceof OpenTelemetrySdk) {
((OpenTelemetrySdk) openTelemetry).getSdkTracerProvider().shutdown();
}
}
}

5. Distributed Tracing Instrumentation

```java
package com.example.jaeger.instrumentation;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class TracingAspect {

private static final Logger log = LoggerFactory.getLogger(TracingAspect.class);
private final Tracer tracer;
public TracingAspect(Tracer tracer) {
this.tracer = tracer;
}
@Around("@annotation(com.example.jaeger.instrumentation.Traced)")
public Object traceMethod(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Traced traced = method.getAnnotation(Traced.class);
String spanName = traced.value().isEmpty() ? 
method.getDeclaringClass().getSimpleName() + "." + method.getName() : 
traced.value();
Span span = tracer.spanBuilder(spanName)
.setSpanKind(SpanKind.INTERNAL)
.startSpan();
try (Scope scope = span.makeCurrent()) {
// Add method parameters as span attributes
Object[] args = joinPoint.getArgs();
String[] parameterNames = signature.getParameterNames();
for (int i = 0; i < args.length; i++) {
if (args[i] != null) {
span.setAttribute("method.param." + parameterNames[i], args[i].toString());
}
}
Object result = joinPoint.proceed();
if (result != null) {
span.setAttribute("method.result.type", result.getClass().getSimpleName());
}
span.setStatus(StatusCode.OK);
return result;
} catch (Exception e) {
span.setStatus(StatusCode.ERROR, e.getMessage());
span.recordException(e);
throw e;
} finally {
span.end();
}
}
@Around("@annotation(com.example.jaeger.instrumentation.TracedExternal)")
public Object traceExternalCall(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
TracedExternal traced = method.getAnnotation(TracedExternal.class);
String spanName = traced.value().isEmpty() ? 
method.getDeclaringClass().getSimpleName()

Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/

OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/

OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/

Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.

https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics

Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2

Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.

https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide

Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2

Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.

https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide

Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.

https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server

Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper