Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions api-catalog-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${CATALOG_CODE} java \
-Dibm.serversocket.recover=true \
-Dfile.encoding=UTF-8 \
-Dlogging.charset.console=${ZOWE_CONSOLE_LOG_CHARSET} \
-Dapiml.logging.timezone=${ZWE_zowe_logging_timezone:-UTC} \
-Djava.io.tmpdir=${TMPDIR:-/tmp} \
-Dspring.profiles.active=${ZWE_configs_spring_profiles_active:-} \
-Dapiml.service.hostname=${ZWE_haInstance_hostname:-localhost} \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
"type": "java.lang.Boolean",
"defaultValue": "false",
"description": "Specifies whether to enable standalone mode.\nStandalone mode allows displaying, without the need for authentication, services that are stored on the disk. API Catalog does not connect to any other service."
},
{
"name": "logging.timezone",
"type": "java.lang.String",
"defaultValue": "UTC",
"description": "Specifies the timezone to be used for logging."
}
]
}
1 change: 1 addition & 0 deletions api-catalog-services/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ logging:
org.eclipse.jetty: WARN
org.apache.http.conn.ssl.DefaultHostnameVerifier: DEBUG #logs only SSLException
javax.net.ssl: ERROR # For running with java 17
timezone: ${apiml.logging.timezone:UTC}

##############################################################################################
# APIML configuration section
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* 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.logging;

import ch.qos.logback.classic.LoggerContext;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.zowe.apiml.message.log.ApimlLogger;
import org.zowe.apiml.product.logging.annotations.InjectApimlLogger;

import javax.annotation.PostConstruct;
import java.time.ZoneId;
import java.util.TimeZone;

/**
* Configuration class for logging timezone settings
*/
@Component
public class LoggingTimezoneConfig {

private static final String UT_COMMON_TIMEZONE_SET_LOG_KEY = "org.zowe.apiml.common.timezone.set";
private static final String UT_COMMON_TIMEZONE_INVALID_LOG_KEY = "org.zowe.apiml.common.timezone.invalid";
private static final String UTC_TIMEZONE = "UTC";
private static final String LOGGING_TIMEZONE = "LOGGING_TIMEZONE";
private static final String LOCAL = "LOCAL";
private static final String TZ = "TZ";

@InjectApimlLogger
private ApimlLogger apimlLog = ApimlLogger.empty();

@Value("${apiml.logging.timezone:UTC}")
private String configuredTimezone;

@PostConstruct
public void init() {
String timezone = determineTimezone();
TimeZone.setDefault(TimeZone.getTimeZone(timezone));

// Update Logback context timezone
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.putProperty(LOGGING_TIMEZONE, timezone);

apimlLog.log(UT_COMMON_TIMEZONE_SET_LOG_KEY, timezone);
}

private String determineTimezone() {
if (LOCAL.equalsIgnoreCase(configuredTimezone)) {
// Check for TZ environment variable first
String tzEnv = System.getenv(TZ);
if (tzEnv != null && !tzEnv.isEmpty()) {
try {
// Validate if the TZ value is a valid timezone
ZoneId.of(tzEnv);
return tzEnv;
} catch (Exception e) {
apimlLog.log(UT_COMMON_TIMEZONE_INVALID_LOG_KEY, tzEnv);
return TimeZone.getDefault().getID();
}
}
return TimeZone.getDefault().getID();
}

try {
// Validate configured timezone
ZoneId.of(configuredTimezone);
return configuredTimezone;
} catch (Exception e) {
apimlLog.log(UT_COMMON_TIMEZONE_INVALID_LOG_KEY, configuredTimezone);
return UTC_TIMEZONE;
}
}
}
14 changes: 14 additions & 0 deletions apiml-utility/src/main/resources/utility-log-messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ messages:
reason: "All key API Mediation Layer services started."
action: "No action required."

- key: org.zowe.apiml.common.timezone.set
number: ZWEUT001
type: INFO
text: "API Mediation Layer timezone set to: %s"
reason: "The API ML components will use this timezone for logging timestamps."
action: "No action is required. This is an informational message indicating successful timezone configuration."

- key: org.zowe.apiml.common.timezone.invalid
number: ZWEUT002
type: WARNING
text: "Invalid timezone configuration: %s, falling back to default"
reason: "The configured timezone ID is not valid according to the Java TimeZone database, or the TZ environment variable contains an invalid value."
action: "Verify that the timezone specified in zowe.yaml (zowe.logging.timezone) or TZ environment variable is a valid timezone ID from the IANA Time Zone database (e.g., 'America/New_York', 'Europe/London'). The system will use UTC as the default timezone."

# General messages
# 100-199

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* 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.logging;

import ch.qos.logback.classic.LoggerContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.TimeZone;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringJUnitConfig(LoggingTimezoneConfig.class)
class LoggingTimezoneConfigTest {

@Autowired
private LoggingTimezoneConfig config;
private String originalTimezone;

@BeforeEach
void setUp() {
originalTimezone = TimeZone.getDefault().getID();
config = new LoggingTimezoneConfig();
}

@AfterEach
void tearDown() {
// Clear system properties and restore original timezone after each test
System.clearProperty("logging.timezone");
TimeZone.setDefault(TimeZone.getTimeZone(originalTimezone));
}

@Nested
class WhenDeterminingTimezone {

@Test
void givenValidConfiguredTimezone_thenUseIt() {
System.getProperties().setProperty("logging.timezone", "UTC");
config.init();
assertEquals("UTC", TimeZone.getDefault().getID());
verifyLogbackContext("UTC");
}


@Test
void givenInvalidConfiguredTimezone_thenUseUTC() {
System.getProperties().setProperty("logging.timezone", "INVALID");
config.init();
assertEquals("UTC", TimeZone.getDefault().getID());
verifyLogbackContext("UTC");
}
}

@Nested
class WhenUsingLocalTimezone {

@Test
void givenLocalConfiguration_thenUseSystemDefault() {
System.getProperties().setProperty("logging.timezone", "LOCAL");
String defaultTimezone = TimeZone.getDefault().getID();
config.init();
assertEquals(defaultTimezone, TimeZone.getDefault().getID());
verifyLogbackContext(defaultTimezone);
}

@Test
void givenLocalConfigurationAndValidTZEnv_thenUseTZValue() {
System.getProperties().setProperty("logging.timezone", "LOCAL");
String tzValue = "Europe/London";

// Store original TZ value
String originalTZ = System.getenv("TZ");
try {
// Set TZ environment variable
setEnvironmentVariable("TZ", tzValue);

config.init();
String expectedTimezone = System.getenv("TZ") != null ? tzValue : TimeZone.getDefault().getID();
assertEquals(expectedTimezone, TimeZone.getDefault().getID());
verifyLogbackContext(expectedTimezone);
} finally {
// Restore original TZ value
if (originalTZ != null) {
setEnvironmentVariable("TZ", originalTZ);
}
}
}
}

@Test
void verifyDefaultValueUTC_whenNoPropertySet() {
config.init();
assertEquals("UTC", TimeZone.getDefault().getID());
verifyLogbackContext("UTC");
}

private void verifyLogbackContext(String expectedTimezone) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
assertEquals(expectedTimezone, loggerContext.getProperty("LOGGING_TIMEZONE"));
}

// Note: This method might not work in all test environments due to security restrictions
private void setEnvironmentVariable(String key, String value) {
try {
ProcessBuilder pb = new ProcessBuilder();
pb.environment().put(key, value);
} catch (Exception e) {
// Ignore if we can't set environment variables in test
}
}
}
1 change: 1 addition & 0 deletions caching-service-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${CACHING_CODE} java \
-Dibm.serversocket.recover=true \
-Dfile.encoding=UTF-8 \
-Dlogging.charset.console=${ZOWE_CONSOLE_LOG_CHARSET} \
-Dapiml.logging.timezone=${ZWE_zowe_logging_timezone:-UTC} \
-Djava.io.tmpdir=${TMPDIR:-/tmp} \
-Dspring.profiles.active=${ZWE_configs_spring_profiles_active:-} \
-Dspring.profiles.include=$LOG_LEVEL \
Expand Down
1 change: 1 addition & 0 deletions caching-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ logging:
org.apache.http.conn.ssl.DefaultHostnameVerifier: DEBUG #logs only SSLException
org.springframework.security.config.annotation.web.builders.WebSecurity: ERROR
javax.net.ssl: ERROR # For running with java 17
timezone: ${apiml.logging.timezone:UTC}
eureka:
client:
instanceInfoReplicationIntervalSeconds: 30
Expand Down
3 changes: 2 additions & 1 deletion caching-service/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<property name="MIN_INDEX" value="${rollingPolicy.minIndex:-1}"/>
<property name="MAX_FILE_SIZE" value="${rollingPolicy.file.maxSize:-50MB}"/>
<property name="STORAGE_LOCATION" value="${apiml.logs.location}" />
<property name="apimlLogPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS,UTC} %clr(&lt;${logbackService:-${logbackServiceName}}:%thread:${PID:- }&gt;){magenta} %X{userid:-} %clr(%-5level) %clr(\\(%logger{15}\\)){cyan} %msg%n"/>
<property name="LOGGING_TIMEZONE" value="${logging.timezone:-UTC}"/>
<property name="apimlLogPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS,${LOGGING_TIMEZONE}} %clr(&lt;${logbackService:-${logbackServiceName}}:%thread:${PID:- }&gt;){magenta} %X{userid:-} %clr(%-5level) %clr(\\(%logger{15}\\)){cyan} %msg%n"/>

<turboFilter class="org.zowe.apiml.product.logging.UseridFilter"/>
<if condition='property("spring.profiles.include").contains("debug")||property("spring.profiles.include").contains("diag")||property("spring.profiles.include").contains("dev")'>
Expand Down
1 change: 1 addition & 0 deletions cloud-gateway-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${CLOUD_GATEWAY_CODE} java \
-Dibm.serversocket.recover=true \
-Dfile.encoding=UTF-8 \
-Dlogging.charset.console=${ZOWE_CONSOLE_LOG_CHARSET} \
-Dapiml.logging.timezone=${ZWE_zowe_logging_timezone:-UTC} \
-Djava.io.tmpdir=${TMPDIR:-/tmp} \
-Dspring.profiles.active=${ZWE_configs_spring_profiles_active:-} \
-Dspring.profiles.include=$LOG_LEVEL \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@
"type": "java.util.Set",
"defaultValue": false,
"description": "Specifies what custom metadata are displayed in the /registry endpoint."
},
{
"name": "logging.timezone",
"type": "java.lang.String",
"defaultValue": "UTC",
"description": "Specifies the timezone to be used for logging."
}
]
}
1 change: 1 addition & 0 deletions cloud-gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ logging:
reactor.netty.http.client: INFO
reactor.netty.http.client.HttpClientConnect: OFF
javax.net.ssl: ERROR # For running with java 17
timezone: ${apiml.logging.timezone:UTC}

management:
endpoint:
Expand Down
3 changes: 2 additions & 1 deletion cloud-gateway-service/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<property name="MIN_INDEX" value="${rollingPolicy.minIndex:-1}"/>
<property name="MAX_FILE_SIZE" value="${rollingPolicy.file.maxSize:-50MB}"/>
<property name="STORAGE_LOCATION" value="${apiml.logs.location}" />
<property name="apimlLogPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS,UTC} %clr(&lt;${logbackService:-${logbackServiceName}}:%thread:${PID:- }&gt;){magenta} %X{userid:-} %clr(%-5level) %clr(\\(%logger{15}\\)){cyan} %msg%n"/>
<property name="LOGGING_TIMEZONE" value="${logging.timezone:-UTC}"/>
<property name="apimlLogPattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS,${LOGGING_TIMEZONE}} %clr(&lt;${logbackService:-${logbackServiceName}}:%thread:${PID:- }&gt;){magenta} %X{userid:-} %clr(%-5level) %clr(\\(%logger{15}\\)){cyan} %msg%n"/>

<turboFilter class="org.zowe.apiml.product.logging.UseridFilter"/>
<if condition='property("spring.profiles.include").contains("debug")||property("spring.profiles.include").contains("diag")||property("spring.profiles.include").contains("dev")'>
Expand Down
1 change: 1 addition & 0 deletions discovery-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${DISCOVERY_CODE} java \
-Dibm.serversocket.recover=true \
-Dfile.encoding=UTF-8 \
-Dlogging.charset.console=${ZOWE_CONSOLE_LOG_CHARSET} \
-Dapiml.logging.timezone=${ZWE_zowe_logging_timezone:-UTC} \
-Djava.io.tmpdir=${TMPDIR:-/tmp} \
-Dspring.profiles.active=${ZWE_configs_spring_profiles_active:-https} \
-Dspring.profiles.include=$LOG_LEVEL \
Expand Down
1 change: 1 addition & 0 deletions discovery-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ logging:
org.apache.http.conn.ssl.DefaultHostnameVerifier: DEBUG #logs only SSLException
javax.net.ssl: ERROR # For running with java 17
# com.netflix.eureka.resources: WARN
timezone: ${apiml.logging.timezone:UTC}

apiml:
# The `apiml` node contains API Mediation Layer specific configuration
Expand Down
1 change: 1 addition & 0 deletions gateway-package/src/main/resources/bin/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ _BPX_JOBNAME=${ZWE_zowe_job_prefix}${GATEWAY_CODE} java \
-Dibm.serversocket.recover=true \
-Dfile.encoding=UTF-8 \
-Dlogging.charset.console=${ZOWE_CONSOLE_LOG_CHARSET} \
-Dapiml.logging.timezone=${ZWE_zowe_logging_timezone:-UTC} \
-Djava.io.tmpdir=${TMPDIR:-/tmp} \
-Dspring.profiles.active=${ZWE_configs_spring_profiles_active:-} \
-Dspring.profiles.include=$LOG_LEVEL \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
"name": "apiml.security.oidc.registry",
"type": "java.lang.String",
"description": "Specifies the distributed identities registry that will be used for identity mapping."
},
{
"name": "logging.timezone",
"type": "java.lang.String",
"defaultValue": "UTC",
"description": "Specifies the timezone to be used for logging."
}
]
}
1 change: 1 addition & 0 deletions gateway-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ logging:
org.apache.http.conn.ssl.DefaultHostnameVerifier: DEBUG #logs only SSLException
org.eclipse.jetty.util.ssl: ERROR
org.apache.tomcat.util.net.SSLUtilBase: ERROR
timezone: ${apiml.logging.timezone:UTC}

apiml:
# The `apiml` node contains API Mediation Layer specific configuration
Expand Down
Loading
Loading