Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
bcee5f4
deprecate onboarding-enabler zutil, move to apiml-utility
Jan 14, 2026
0e0b568
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 14, 2026
5243473
Merge remote-tracking branch 'origin' into reboot/feat/otel-standard
Jan 14, 2026
4fbb002
initial commit
Jan 14, 2026
1acb61c
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 14, 2026
2d03079
wip add calculated attributes
Jan 16, 2026
976bb8b
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 16, 2026
46fb08b
wip testing
Jan 16, 2026
7d12592
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 16, 2026
1fa20d2
compile issue
Jan 16, 2026
06206ef
add defaults
Jan 16, 2026
d9d2541
otel test wip
Jan 16, 2026
d0e1967
test works
Jan 16, 2026
9fa1332
test data is empty
Jan 16, 2026
245a277
attempts to have acceptance tests working
Jan 21, 2026
a5e5b46
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 21, 2026
a65086e
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Jan 22, 2026
014845a
InMemoryMetricReader for tests
richard-salac Jan 22, 2026
c42575e
rename test, update structure
Jan 22, 2026
07054ca
add unit tests
Jan 23, 2026
f6dfe77
wip
Jan 23, 2026
92f6300
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 23, 2026
c342fc4
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 27, 2026
94c9370
fix for acceptance test
Jan 27, 2026
753c7c1
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 27, 2026
f4c4416
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Jan 27, 2026
1015c38
unit test
Jan 27, 2026
1e2b2fc
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Jan 27, 2026
a8be34c
complete functional test
Jan 27, 2026
86094cb
remove standard attribute from test
Jan 27, 2026
8dd9c79
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Jan 27, 2026
49520e1
attempt to fix otel settings start.sh
Jan 28, 2026
68680c3
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Jan 29, 2026
a667103
add unit test
Jan 29, 2026
ba4e739
fix merge issue
Jan 30, 2026
f80fba5
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Jan 30, 2026
c7c98ba
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Feb 2, 2026
ed9c5c3
Merge branch 'v3.x.x' into reboot/feat/otel-standard
richard-salac Feb 2, 2026
ca9d625
pr review 1
Feb 3, 2026
05b870d
use attributes, add exporter settings
Feb 3, 2026
80874a4
fix for blank otel properties in start.sh
Feb 3, 2026
5c70cf5
add zos attributes
Feb 3, 2026
0a4a818
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Feb 4, 2026
1991a26
add comment
Feb 4, 2026
c3a45be
wip pr review
Feb 10, 2026
4e4a639
fix tests
Feb 10, 2026
4461124
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Feb 10, 2026
905a440
remove debug
Feb 10, 2026
b4a28f7
pr review
Feb 11, 2026
d398ec4
refactor to use nonzos resource provider
Feb 11, 2026
ea0ce0b
fix resource provider order
richard-salac Feb 11, 2026
34baf94
fix resource provider order
richard-salac Feb 11, 2026
19c17d5
use zos profile
Feb 12, 2026
829b142
remove job id
Feb 13, 2026
d6f3d06
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Feb 13, 2026
628ccef
cleanup config
richard-salac Feb 13, 2026
de78efa
handle missing zos symbols
Feb 13, 2026
37401eb
Merge branch 'reboot/feat/otel-standard' of https://github.com/zowe/a…
Feb 13, 2026
ae3fb11
fix test
Feb 13, 2026
02bda92
add apiml prefix for service.name
Feb 13, 2026
839bee1
read os.version from jvm properties
richard-salac Feb 17, 2026
f6d3d20
Merge remote-tracking branch 'origin/v3.x.x' into reboot/feat/otel-st…
Feb 27, 2026
75959c5
fix unit test
Feb 27, 2026
9723718
fix merge
Mar 2, 2026
599afe7
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Mar 2, 2026
bbc60c0
chore: OpenTelemetry integration test validating resource attributes …
richard-salac Mar 2, 2026
8b960c4
Merge branch 'v3.x.x' into reboot/feat/otel-standard
richard-salac Mar 2, 2026
66eca45
Merge branch 'v3.x.x' into reboot/feat/otel-standard
richard-salac Mar 2, 2026
42a9a94
Merge branch 'v3.x.x' into reboot/feat/otel-standard
richard-salac Mar 3, 2026
2195540
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Mar 3, 2026
7931a1f
add tests for deployment environment name
richard-salac Mar 3, 2026
1082797
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Mar 4, 2026
798672e
otel it readme update
richard-salac Mar 5, 2026
eeaa8d4
Merge branch 'v3.x.x' into reboot/feat/otel-standard
pablocarle Mar 5, 2026
d05495f
fix org.zowe.apiml.product.opentelemetry.ApimlZosOpenTelemetryResourc…
richard-salac Mar 5, 2026
c683ef0
minor fixes in readme
Mar 5, 2026
c6cbec2
update otel dockers
richard-salac Mar 5, 2026
5f9daa4
solve sonar issues
Mar 5, 2026
43db4bb
Merge branch 'reboot/feat/otel-standard' of https://github.com/zowe/a…
Mar 5, 2026
95dfc80
Merge branch 'v3.x.x' into reboot/feat/otel-standard
richard-salac Mar 6, 2026
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
14 changes: 13 additions & 1 deletion .github/workflows/service-registration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,20 @@ jobs:
run: >
./gradlew runStartUpCheck --info --scan -Denvironment.startServices=true

- name: Start OpenTelemetry containers
run: |
cd otel
sh/start_containers.sh

- name: Run startup check for modulith
run: >
./gradlew runStartUpCheck --info --scan -Denvironment.startServices=true -Denvironment.modulith=true -Denvironment.config=-modulith
./gradlew runStartUpCheckWithOpenTelemetry --info --scan -Denvironment.startServices=true -Denvironment.modulith=true -Denvironment.config=-modulith

- name: Validate telemetry data and stop containers
if: always()
run: |
cd otel
sh/validate_and_stop.sh

- name: Store results
uses: actions/upload-artifact@v4
Expand All @@ -78,5 +89,6 @@ jobs:
name: Register-${{ env.JOB_ID }}
path: |
*/build/reports/**
otel/**

- uses: ./.github/actions/teardown
1 change: 1 addition & 0 deletions apiml-common/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies {
implementation libs.spring.boot.starter.actuator
implementation libs.spring.boot.starter.web
implementation libs.spring.cloud.starter.eureka.client
implementation libs.opentelemetry.spring.boot.starter
compileOnly libs.netty.reactor.http
implementation libs.eureka.core

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.product.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Component;

import javax.annotation.Nonnull;

@ConditionalOnMissingBean(ApimlZosOpenTelemetryResourceProvider.class)
@Component
public class ApimlNonZosOpenTelemetryResourceProvider extends ApimlOpenTelemetryResourceProvider {

@Override
protected @Nonnull Attributes internalCalculateAttributes() {
return Attributes.empty();
}

@Override
protected String generateServiceName() {
var systemName = StringUtils.isBlank(apimlId) ? hostname : apimlId;
return "apiml:" + systemName + ":" + port;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.product.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.resources.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Nonnull;

@Slf4j
public abstract class ApimlOpenTelemetryResourceProvider implements ResourceProvider {

public static final String OS_VERSION = "os.version";

@Value("${otel.resource.attributes.service.namespace:#{null}}")
private String serviceNamespace;

@Value("${apiml.service.hostname:localhost}")
protected String hostname;

@Value("${apiml.service.port:10010}")
protected int port;

@Value("${otel.resource.attributes.service.name:#{null}}")
private String serviceName;

@Value("${apiml.service.apimlId:#{null}}")
protected String apimlId;

public @Nonnull Attributes calculateAttributes() {
Comment thread
richard-salac marked this conversation as resolved.
var attributesBuilder = Attributes.builder();

if (StringUtils.isBlank(serviceNamespace)) {
log.debug("service.namespace is not provided in configuration");
}

if (StringUtils.isBlank(serviceName)) {
var generatedServiceName = generateServiceName();
attributesBuilder.put("service.name", generatedServiceName);
log.debug("service.name not provided in configuration, using generated default {}", generatedServiceName);
}

var instanceId = generateInstanceId();
attributesBuilder.put(ZosOpenTelemetryAttributes.OTEL_ZOS_INSTANCE_ID, instanceId);
log.debug("using generated service.instance.id {}", instanceId);

// io.opentelemetry.instrumentation.resources.OsResource resolves the version but uses it only to populate os.description
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/16211
attributesBuilder.put(OS_VERSION, System.getProperty(OS_VERSION));

attributesBuilder.putAll(internalCalculateAttributes());
return attributesBuilder.build();
}

protected abstract @Nonnull Attributes internalCalculateAttributes();

@Override
public Resource createResource(@Nonnull ConfigProperties config) {
var attributesBuilder = Attributes.builder();

attributesBuilder.putAll(calculateAttributes());
return Resource.create(attributesBuilder.build());
}

private String generateInstanceId() {
return String.format("%s:gateway:%d", hostname, port);
}

protected abstract String generateServiceName();

@Override
public int order() {
/* To run after
io.opentelemetry.instrumentation.resources.JarServiceNameDetector
but before
io.opentelemetry.sdk.autoconfigure.EnvironmentResourceProvider
io.opentelemetry.sdk.extension.incubator.resources.ServiceInstanceIdResourceProvider
*/
return 10000;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.product.opentelemetry;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.zowe.apiml.product.zos.ZosSystemInformation;

import javax.annotation.Nonnull;

import java.util.Optional;

import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_ENVIRON;
import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_JOB_NAME;
import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_SMF_ID;
import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_SYSNAME;
import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_SYSPLEX;
import static org.zowe.apiml.product.zos.ZosSystemInformation.ZOS_USER_ID;

@Component
@RequiredArgsConstructor
@Profile("zos")
@Slf4j
public class ApimlZosOpenTelemetryResourceProvider extends ApimlOpenTelemetryResourceProvider {

private final ZosSystemInformation zosSystemInformation;

@Value("${otel.resource.attributes.deployment.environment.name:#{null}}")
private String environmentName;
Comment thread
pablocarle marked this conversation as resolved.

@Value("${otel.resource.attributes.zos.sysplex.name:#{null}}")
private String sysplexName;

@Value("${otel.resource.attributes.mainframe.lpar.name:#{null}}")
private String lparName;

@Value("${otel.resource.attributes.zos.smf.id:#{null}}")
private String smfId;

@PostConstruct
void afterPropertiesSet() {
log.debug("Using ZOS OpenTelemetry resource provider");
}

private void attribute(AttributesBuilder attributesBuilder, @Nonnull String openTelemetryAttribute, String zosAttribute) {
var zosAttributes = zosSystemInformation.get();
var zosValue = zosAttributes.get(zosAttribute);
if (zosValue != null && StringUtils.isNotBlank(zosValue.toString())) {
log.debug(openTelemetryAttribute + " not provided in configuration, using z/OS obtained {}", zosValue);
attributesBuilder.put(openTelemetryAttribute, zosValue.toString());
} else {
log.debug(openTelemetryAttribute + " not provided in configuration. Could not determine it from system");
}
}

@SuppressWarnings("null")
@Override
@Nonnull
protected Attributes internalCalculateAttributes() {
var attributesBuilder = Attributes.builder();

var zosAttributes = zosSystemInformation.get();

if (StringUtils.isBlank(environmentName)) {
attribute(attributesBuilder, "deployment.environment.name", ZOS_ENVIRON);
}

if (StringUtils.isBlank(sysplexName)) {
attribute(attributesBuilder, "zos.sysplex.name", ZOS_SYSPLEX);
}

if (StringUtils.isBlank(lparName)) {
attribute(attributesBuilder, "mainframe.lpar.name", ZOS_SYSNAME);
}

if (StringUtils.isBlank(smfId)) {
attribute(attributesBuilder, "zos.smf.id", ZOS_SMF_ID);
}
Comment thread
pablocarle marked this conversation as resolved.

Optional.ofNullable(zosAttributes.get(ZOS_JOB_NAME))
.map(String::valueOf)
.filter(StringUtils::isNotBlank)
.ifPresent(zosJobName -> attributesBuilder.put(ZosOpenTelemetryAttributes.OTEL_ZOS_JOBNAME, zosJobName));

Optional.ofNullable(zosAttributes.get(ZOS_USER_ID))
.map(String::valueOf)
.filter(StringUtils::isNotBlank)
.ifPresent(zosUserId -> attributesBuilder.put(ZosOpenTelemetryAttributes.OTEL_ZOS_USERID, zosUserId));

return attributesBuilder.build();
Comment thread
pablocarle marked this conversation as resolved.
}

@Override
protected String generateServiceName() {
var zosAttributes = zosSystemInformation.get();
var systemName = StringUtils.isBlank(apimlId) ? zosAttributes.get(ZOS_SYSPLEX) : apimlId;
return "apiml:" + systemName + ":" + port;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.product.opentelemetry;

/**
* Set of attributes set by API ML
*/
public class ZosOpenTelemetryAttributes {

Check warning on line 16 in apiml-common/src/main/java/org/zowe/apiml/product/opentelemetry/ZosOpenTelemetryAttributes.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a private constructor to hide the implicit public one.

See more on https://sonarcloud.io/project/issues?id=zowe_api-layer&issues=AZxH6RaZnVWfvRQcs3sd&open=AZxH6RaZnVWfvRQcs3sd&pullRequest=4456

public static final String OTEL_ZOS_JOBNAME = "process.zos.jobname";
public static final String OTEL_ZOS_USERID = "process.zos.userid";
public static final String OTEL_ZOS_INSTANCE_ID = "service.instance.id";

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*/

package org.zowe.apiml.product.opentelemetry;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.mock;

@ExtendWith(MockitoExtension.class)
class ApimlNonZosOpenTelemetryResourceProviderTest {

private ApimlNonZosOpenTelemetryResourceProvider resourceProvider;

@BeforeEach
void setUp() {
this.resourceProvider = new ApimlNonZosOpenTelemetryResourceProvider();
ReflectionTestUtils.setField(resourceProvider, "hostname", "localhost");
ReflectionTestUtils.setField(resourceProvider, "port", 10010);
}

@Test
void testCalculateAttributes() {
var result = resourceProvider.calculateAttributes();
assertFalse(result.isEmpty());
}

@Test
void testCreateResource() {
var result = resourceProvider.createResource(mock(ConfigProperties.class));
assertFalse(result.getAttributes().isEmpty());
}

@Test
void testServiceName() {
var result = resourceProvider.generateServiceName();
assertEquals("apiml:localhost:10010", result);
}

}
Loading
Loading