Skip to content

Add cloud logging service feature#1847

Open
Yavor16 wants to merge 2 commits into
masterfrom
export-logs-to-logging-service-squash
Open

Add cloud logging service feature#1847
Yavor16 wants to merge 2 commits into
masterfrom
export-logs-to-logging-service-squash

Conversation

@Yavor16
Copy link
Copy Markdown
Contributor

@Yavor16 Yavor16 commented May 28, 2026

No description provided.

# Conflicts:
#	multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/backup-existing-app.bpmn
#	multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/deploy-app.bpmn
#	multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/stop-dependent-modules.bpmn
#	multiapps-controller-process/src/main/resources/org/cloudfoundry/multiapps/controller/process/undeploy-app.bpmn
@Yavor16 Yavor16 force-pushed the export-logs-to-logging-service-squash branch from 20788df to 7220532 Compare May 28, 2026 06:57
@Yavor16 Yavor16 force-pushed the export-logs-to-logging-service-squash branch from c1abde6 to bb7fc04 Compare June 1, 2026 10:51
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 1, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
78.9% Coverage on New Code (required ≥ 80%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE


@Override
protected String getStepErrorMessage(ProcessContext context) {
return "Well, failed! Deal with it!";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:O why so rude?

import org.cloudfoundry.multiapps.controller.core.auditlogging.model.ConfigurationChangeActions;
import org.cloudfoundry.multiapps.controller.persistence.model.LoggingConfiguration;

public class CloudLoggingServiceConfigurationAuditLog {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because SAP Cloud logging service is an internal proprietary service it should not be exposed in the open source. The whole logic related to the service must be move in the internal project.

Move every SAP related thing to the internal project and only leave the what is actually open source in this PR

Map<String, String> identifiers = new HashMap<>();
identifiers.put(MTA_ID_PROPERTY_NAME, loggingConfiguration.getMtaId());
identifiers.put(NAMESPACE_PROPERTY_NAME, loggingConfiguration.getNamespace());
auditLoggingFacade.logDataAccessAuditLog(new AuditLogConfiguration(username,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why buildIdentifiers is not used in this method?

identifiers));
}

public void logListLoggingConfigurations(String username, String spaceId) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really used?

Comment on lines +91 to +95
identifiers.put(NAMESPACE_PROPERTY_NAME, loggingConfiguration.getNamespace());
identifiers.put(TARGET_SPACE_PROPERTY_NAME, loggingConfiguration.getTargetSpace());
identifiers.put(TARGET_ORG_PROPERTY_NAME, loggingConfiguration.getTargetOrg());
identifiers.put(SERVICE_INSTANCE_NAME_PROPERTY_NAME, loggingConfiguration.getServiceInstanceName());
identifiers.put(SERVICE_KEY_NAME_PROPERTY_NAME, loggingConfiguration.getServiceKeyName());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the IDs of target space and service instance?

return logsMap;
}

private void getMessagesToLog(String log, Map<LogLevel, List<LogLogLog>> logsMap) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return a Map of the logs instead of in-parameters

Comment on lines +294 to +296
InputStream serverCaStream = getCredentialInputStream(loggingConfiguration.getServerCa());
InputStream clientCertStream = getCredentialInputStream(loggingConfiguration.getClientCert());
InputStream clientKeyStream = getCredentialInputStream(loggingConfiguration.getClientKey());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these streams closed?

.build();
}

public record LogLogLog(String log, LocalDateTime dateTime) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this Log :D

}
}

private boolean areCloudLoggingParametersValid(String serviceInstanceName, String serviceKeyName) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inverted naming?

String credential = (String) credentials.get(credentialsName);

if (credential == null) {
throw new IllegalArgumentException("Missing required " + credentialsName + " credential for SAP Cloud Logging export");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SLException instead.

Copy link
Copy Markdown
Contributor

@IvanBorislavovDimitrov IvanBorislavovDimitrov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional comments on OperationLogsExporter not yet covered in the earlier review.

}

private boolean hasRequestFailed(ResponseEntity<Void> response) {
if (response == null) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null response is treated as success here. .toBodilessEntity().block() can return null (empty Mono, cancellation, certain WebClient error paths). Treating that as a non-failure means a silently-dropped batch is reported as sent. Either treat null as failure, or document precisely under which condition it can be null and still be safe.


for (ExternalOperationLogEntry entry : externalLogEntries) {
String entryJson = JsonUtil.toJson(entry);
int entrySize = entryJson.getBytes().length;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entryJson.getBytes().length uses the JVM default charset. The body that actually goes on the wire is UTF-8 (Jackson default). On a non-UTF-8 default platform you'll under- or over-estimate the batch size and either oversize a request or split too aggressively. Use entryJson.getBytes(StandardCharsets.UTF_8).length.


String cleanedMessage = extractMessage(message);
String level = logLevels.get(levelIndex);
LocalDateTime date = dateLevels.get(levelIndex);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logLevels.get(levelIndex) / dateLevels.get(levelIndex) have no bounds check. The split regex and the level/date regexes walk the same input independently, so a single malformed line desynchronises them and you get an IndexOutOfBoundsException. Not fail-safe — even with isFailSafe = true the exception is unchecked and escapes.


private List<LocalDateTime> getLogDate(String log) {
Matcher matcher = MESSAGE_LOG_DATE_PATTERN.matcher(log);
List<LocalDateTime> logLevels = new ArrayList<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable is named logLevels but holds LocalDateTime. Rename to logDates.

private String extractMessage(String message) {
String trimmed = message.substring(message.indexOf("]") + 1)
.trim();
return trimmed.substring(0, trimmed.length() - 1);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trimmed.substring(0, trimmed.length() - 1) assumes the message ends with a delimiter. On an empty trimmed this throws StringIndexOutOfBoundsException; on a message that doesn't actually end in the expected # we silently drop the last real character. Guard the length, or use a regex that captures the body explicitly.

webClient = clientCache.get(loggingConfiguration.getOperationId());
}

LOGGER.debug(MessageFormat.format(Messages.CREATING_WEBCLIENT_WITH_MTLS_CONFIGURATION_FOR_ENDPOINT_1,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This debug line fires on every call — including cache hits — but the message reads "creating WebClient with mTLS configuration…", which is wrong on a hit. Move it inside the if branch above so it only logs when a client is actually created.

}

private void logErrorOrThrowExceptionBasedOnFailSafe(LoggingConfiguration loggingConfiguration, String message) {
if (loggingConfiguration.isFailSafe()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loggingConfiguration.isFailSafe() returns @Nullable Boolean; auto-unboxing here will NPE if the field is null. Either default it at the model layer (e.g. @Value.Default) or null-check at the call site. The same trap exists in ExternalLoggingServiceConfigurationsCalculator#getServiceKeyWithLoggingConfiguration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants