added batch job related attributes to OpenTelemetry span (#5972)

This commit is contained in:
Emre Dincturk 2024-06-06 14:07:03 -04:00 committed by GitHub
parent 00a6591586
commit 48c387ecb0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 109 additions and 3 deletions

View File

@ -31,6 +31,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.annotations.WithSpan;
import jakarta.annotation.Nonnull;
@ -65,6 +66,13 @@ import java.util.stream.Collectors;
public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & IPointcut>
implements IBaseInterceptorService<POINTCUT>, IBaseInterceptorBroadcaster<POINTCUT> {
private static final Logger ourLog = LoggerFactory.getLogger(BaseInterceptorService.class);
private static final AttributeKey<String> OTEL_INTERCEPTOR_POINTCUT_NAME_ATT_KEY =
AttributeKey.stringKey("hapifhir.interceptor.pointcut_name");
private static final AttributeKey<String> OTEL_INTERCEPTOR_CLASS_NAME_ATT_KEY =
AttributeKey.stringKey("hapifhir.interceptor.class_name");
private static final AttributeKey<String> OTEL_INTERCEPTOR_METHOD_NAME_ATT_KEY =
AttributeKey.stringKey("hapifhir.interceptor.method_name");
private final List<Object> myInterceptors = new ArrayList<>();
private final ListMultimap<POINTCUT, BaseInvoker> myGlobalInvokers = ArrayListMultimap.create();
private final ListMultimap<POINTCUT, BaseInvoker> myAnonymousInvokers = ArrayListMultimap.create();
@ -573,11 +581,11 @@ public abstract class BaseInterceptorService<POINTCUT extends Enum<POINTCUT> & I
private Object invokeMethod(Object[] args) throws InvocationTargetException, IllegalAccessException {
// Add attributes to the opentelemetry span
Span currentSpan = Span.current();
currentSpan.setAttribute("hapifhir.interceptor.pointcut_name", myPointcut.name());
currentSpan.setAttribute(OTEL_INTERCEPTOR_POINTCUT_NAME_ATT_KEY, myPointcut.name());
currentSpan.setAttribute(
"hapifhir.interceptor.class_name",
OTEL_INTERCEPTOR_CLASS_NAME_ATT_KEY,
myMethod.getDeclaringClass().getName());
currentSpan.setAttribute("hapifhir.interceptor.method_name", myMethod.getName());
currentSpan.setAttribute(OTEL_INTERCEPTOR_METHOD_NAME_ATT_KEY, myMethod.getName());
return myMethod.invoke(getInterceptor(), args);
}

View File

@ -0,0 +1,6 @@
---
type: add
issue: 5980
title: "When processing a batch job, OpenTelemetry spans named `hapifhir.batch_job.execute` are now generated by the
worker threads. These spans have the following span attributes related to the batch job: `hapifhir.batch_job.definition_id`,
`hapifhir.batch_job.definition_version`, `hapifhir.batch_job.instance_id`, `hapifhir.batch_job.step_id`, `hapifhir.batch_job.chunk_id`."

View File

@ -68,6 +68,12 @@
<artifactId>jakarta.el</artifactId>
</dependency>
<!-- OpenTelemetry -->
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>org.awaitility</groupId>

View File

@ -27,13 +27,17 @@ import ca.uhn.fhir.batch2.model.JobWorkCursor;
import ca.uhn.fhir.batch2.model.StatusEnum;
import ca.uhn.fhir.batch2.model.WorkChunk;
import ca.uhn.fhir.batch2.progress.JobInstanceStatusUpdater;
import ca.uhn.fhir.batch2.util.BatchJobOpenTelemetryUtils;
import ca.uhn.fhir.model.api.IModelJson;
import ca.uhn.fhir.util.Logs;
import io.opentelemetry.instrumentation.annotations.WithSpan;
import jakarta.annotation.Nonnull;
import org.slf4j.Logger;
import java.util.Date;
import static ca.uhn.fhir.batch2.util.BatchJobOpenTelemetryUtils.JOB_STEP_EXECUTION_SPAN_NAME;
public class JobStepExecutor<PT extends IModelJson, IT extends IModelJson, OT extends IModelJson> {
private static final Logger ourLog = Logs.getBatchTroubleshootingLog();
@ -67,7 +71,16 @@ public class JobStepExecutor<PT extends IModelJson, IT extends IModelJson, OT ex
myJobInstanceStatusUpdater = new JobInstanceStatusUpdater(theJobDefinitionRegistry);
}
@WithSpan(JOB_STEP_EXECUTION_SPAN_NAME)
public void executeStep() {
BatchJobOpenTelemetryUtils.addAttributesToCurrentSpan(
myInstance.getJobDefinitionId(),
myInstance.getJobDefinitionVersion(),
myInstance.getInstanceId(),
myCursor.getCurrentStepId(),
myWorkChunk == null ? null : myWorkChunk.getId());
JobStepExecutorOutput<PT, IT, OT> stepExecutorOutput =
myJobExecutorSvc.doExecution(myCursor, myInstance, myWorkChunk);

View File

@ -33,6 +33,7 @@ import ca.uhn.fhir.batch2.model.JobWorkCursor;
import ca.uhn.fhir.batch2.model.StatusEnum;
import ca.uhn.fhir.batch2.model.WorkChunk;
import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
import ca.uhn.fhir.batch2.util.BatchJobOpenTelemetryUtils;
import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.sched.HapiJob;
import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs;
@ -41,6 +42,7 @@ import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition;
import ca.uhn.fhir.model.api.IModelJson;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.instrumentation.annotations.WithSpan;
import jakarta.annotation.Nonnull;
import org.apache.commons.lang3.time.DateUtils;
import org.quartz.JobExecutionContext;
@ -70,6 +72,7 @@ import static ca.uhn.fhir.batch2.model.StatusEnum.COMPLETED;
import static ca.uhn.fhir.batch2.model.StatusEnum.ERRORED;
import static ca.uhn.fhir.batch2.model.StatusEnum.FINALIZE;
import static ca.uhn.fhir.batch2.model.StatusEnum.IN_PROGRESS;
import static ca.uhn.fhir.batch2.util.BatchJobOpenTelemetryUtils.JOB_STEP_EXECUTION_SPAN_NAME;
public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorService, IHasScheduledJobs {
public static final String SCHEDULED_JOB_ID = ReductionStepExecutorScheduledJob.class.getName();
@ -159,10 +162,18 @@ public class ReductionStepExecutorServiceImpl implements IReductionStepExecutorS
}
@VisibleForTesting
@WithSpan(JOB_STEP_EXECUTION_SPAN_NAME)
<PT extends IModelJson, IT extends IModelJson, OT extends IModelJson>
ReductionStepChunkProcessingResponse executeReductionStep(
String theInstanceId, JobWorkCursor<PT, IT, OT> theJobWorkCursor) {
BatchJobOpenTelemetryUtils.addAttributesToCurrentSpan(
theJobWorkCursor.getJobDefinition().getJobDefinitionId(),
theJobWorkCursor.getJobDefinition().getJobDefinitionVersion(),
theInstanceId,
theJobWorkCursor.getCurrentStepId(),
null);
JobDefinitionStep<PT, IT, OT> step = theJobWorkCursor.getCurrentStep();
// wipmb For 6.8 - this runs four tx. That's at least 2 too many

View File

@ -0,0 +1,62 @@
/*-
* #%L
* HAPI FHIR JPA Server - Batch2 Task Processor
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* 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.
* #L%
*/
package ca.uhn.fhir.batch2.util;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.Span;
import jakarta.annotation.Nullable;
public class BatchJobOpenTelemetryUtils {
public static final String JOB_STEP_EXECUTION_SPAN_NAME = "hapifhir.batch_job.execute";
private static final AttributeKey<String> OTEL_JOB_DEF_ID_ATT_KEY =
AttributeKey.stringKey("hapifhir.batch_job.definition_id");
private static final AttributeKey<String> OTEL_JOB_DEF_VER_ATT_KEY =
AttributeKey.stringKey("hapifhir.batch_job.definition_version");
private static final AttributeKey<String> OTEL_JOB_STEP_ID_ATT_KEY =
AttributeKey.stringKey("hapifhir.batch_job.step_id");
private static final AttributeKey<String> OTEL_JOB_INSTANCE_ID_ATT_KEY =
AttributeKey.stringKey("hapifhir.batch_job.instance_id");
private static final AttributeKey<String> OTEL_JOB_CHUNK_ID_ATT_KEY =
AttributeKey.stringKey("hapifhir.batch_job.chunk_id");
private BatchJobOpenTelemetryUtils() {}
public static void addAttributesToCurrentSpan(
String theJobDefinitionId,
int theJobDefinitionVersion,
String theJobInstanceId,
String theStepId,
@Nullable String theChunkId) {
Span currentSpan = Span.current();
AttributesBuilder attBuilder = Attributes.builder()
.put(OTEL_JOB_DEF_ID_ATT_KEY, theJobDefinitionId)
.put(OTEL_JOB_DEF_VER_ATT_KEY, Integer.toString(theJobDefinitionVersion))
.put(OTEL_JOB_STEP_ID_ATT_KEY, theStepId)
.put(OTEL_JOB_INSTANCE_ID_ATT_KEY, theJobInstanceId)
// it is ok to put null values, builder ignores them
.put(OTEL_JOB_CHUNK_ID_ATT_KEY, theChunkId);
currentSpan.setAllAttributes(attBuilder.build());
}
}