From d39f2b7b7b4e2fea2a5402c63c10d3ce83097129 Mon Sep 17 00:00:00 2001 From: Thomas <> Date: Thu, 23 Mar 2023 17:24:53 +0100 Subject: chore(core): update statistic logger as requested by the BM.I --- .../core/logger/AdvancedStatisticLogger.java | 223 +++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 ms_specific_connector/src/main/java/at/asitplus/eidas/specific/core/logger/AdvancedStatisticLogger.java (limited to 'ms_specific_connector/src/main/java/at/asitplus/eidas/specific/core/logger/AdvancedStatisticLogger.java') diff --git a/ms_specific_connector/src/main/java/at/asitplus/eidas/specific/core/logger/AdvancedStatisticLogger.java b/ms_specific_connector/src/main/java/at/asitplus/eidas/specific/core/logger/AdvancedStatisticLogger.java new file mode 100644 index 00000000..06fb1aba --- /dev/null +++ b/ms_specific_connector/src/main/java/at/asitplus/eidas/specific/core/logger/AdvancedStatisticLogger.java @@ -0,0 +1,223 @@ +package at.asitplus.eidas.specific.core.logger; + +import java.io.IOException; +import java.time.LocalDateTime; + +import org.apache.commons.lang3.StringUtils; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; + +import at.asitplus.eidas.specific.core.MsEidasNodeConstants; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.log.statistic.DetailedMatchtingStatistic; +import at.asitplus.eidas.specific.modules.auth.eidas.v2.utils.MatchingTaskUtils; +import at.gv.egiz.eaaf.core.api.IRequest; +import at.gv.egiz.eaaf.core.api.IStatusMessenger; +import at.gv.egiz.eaaf.core.api.idp.IAuthData; +import at.gv.egiz.eaaf.core.api.logging.IStatisticLogger; +import at.gv.egiz.eaaf.core.exceptions.EaafStorageException; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Extended statistic logger that use a JSON based data model. + * + * @author tlenz + * + */ +@Slf4j +public class AdvancedStatisticLogger implements IStatisticLogger { + + private static final String DATEFORMATER = "yyyy.MM.dd-HH:mm:ss,SSS"; + private static final String DEFAULT_NO_IDP_ID = "no idpId available"; + private static final String DEFAULT_NO_SP_ID = "no appId available"; + + private static final ObjectMapper mapper = new ObjectMapper(); + private static final JavaPropsMapper propsMapper = new JavaPropsMapper(); + + private final IStatusMessenger messageService; + + /** + * Build a JSON based statistic logger that uses error messages from a specific source. + * + * @param source i18n message source + */ + public AdvancedStatisticLogger(IStatusMessenger source) { + this.messageService = source; + + } + + @Override + public void logSuccessOperation(IRequest protocolRequest, IAuthData authData, boolean isSsoSession) { + final StatisticLogEntry entry = buildCoreEntry(protocolRequest); + entry.setSuccess(SuccessEntry.builder() + .spSector(protocolRequest.getServiceProviderConfiguration().getAreaSpecificTargetIdentifier()) + .spCountry("AT") + .citizenCountryCode(authData.getCiticenCountryCode()) + .build()); + entry.setMatching(MatchingDetails.builder() + .matchingMethod(extractMatchingState(protocolRequest)) + .matchingDetails(extractMatchingDetails(protocolRequest)) + .build()); + writeEntryToLog(entry); + + } + + @Override + public void logErrorOperation(Throwable throwable) { + log.trace("Advanced statistic logger only logs on SP level"); + + } + + @Override + public void logErrorOperation(Throwable throwable, IRequest errorRequest) { + final StatisticLogEntry entry = buildCoreEntry(errorRequest); + entry.setError(new ErrorEntry(messageService.getResponseErrorCode(throwable), throwable.getMessage())); + entry.setMatching(MatchingDetails.builder() + .matchingMethod(extractMatchingState(errorRequest)) + .matchingDetails(extractMatchingDetails(errorRequest)) + .build()); + writeEntryToLog(entry); + + } + + @Override + public void internalTesting() throws Exception { + log.trace("Not implemented for a File-based logger"); + + } + + private StatisticLogEntry buildCoreEntry(IRequest errorRequest) { + Object appId = errorRequest.getRawData(MsEidasNodeConstants.DATA_REQUESTERID); + return new StatisticLogEntry( + LocalDateTime.now(), + errorRequest.getUniqueTransactionIdentifier(), + errorRequest.getServiceProviderConfiguration() != null + && errorRequest.getServiceProviderConfiguration().getUniqueIdentifier() != null + ? errorRequest.getServiceProviderConfiguration().getUniqueIdentifier() : DEFAULT_NO_IDP_ID, + appId instanceof String && StringUtils.isNotEmpty((String)appId) + ? (String)appId : DEFAULT_NO_SP_ID); + + } + + private String extractMatchingState(IRequest protocolRequest) { + Object state = protocolRequest.getRawData(MsEidasNodeConstants.DATA_MATCHING_STATE); + return state != null ? state.toString() : MsEidasNodeConstants.MatchingStates.NO_REQUIRED.toString(); + + } + + private DetailedMatchtingStatistic extractMatchingDetails(IRequest protocolRequest) { + try { + if (!MsEidasNodeConstants.MatchingStates.NO_REQUIRED.toString().equals( + extractMatchingState(protocolRequest))) { + return MatchingTaskUtils.getDetailedMatchingStatistic(protocolRequest); + + } + + } catch (EaafStorageException e) { + log.warn("Can not generate detailed matching statistic", e); + + } + + return null; + } + + private void writeEntryToLog(StatisticLogEntry entry) { + try { + log.info(mapper.writeValueAsString(propsMapper.writeValueAsProperties(entry))); + + } catch (final IOException e) { + log.error("Can NOT generate statistic entry for logging", e); + + } + } + + + @Getter + @Setter + @RequiredArgsConstructor + @JsonInclude(Include.NON_NULL) + private static class StatisticLogEntry { + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonDeserialize(using = LocalDateTimeDeserializer.class) + @JsonFormat(pattern = DATEFORMATER) + @JsonProperty("timestamp") + private final LocalDateTime timestamp; + + @JsonProperty("txId") + private final String transactionId; + + @JsonProperty("entityid") + private final String idpEntityId; + + @JsonProperty("unique-sp-id") + private final String uniqueId; + + @JsonProperty("result") + private SuccessEntry success; + + @JsonProperty("error") + private ErrorEntry error; + + @JsonProperty("identityMatching") + private MatchingDetails matching; + + } + + @Getter + @Setter + @Builder + @JsonInclude(Include.NON_NULL) + private static class SuccessEntry { + + @JsonProperty("spSector") + private final String spSector; + + @JsonProperty("spCountry") + private final String spCountry; + + @JsonProperty("citizenCountryCode") + private final String citizenCountryCode; + + } + + @Getter + @Setter + @Builder + @JsonInclude(Include.NON_NULL) + private static class MatchingDetails { + + @JsonProperty("finalMatchingMethod") + private final String matchingMethod; + + @JsonProperty("matchingProcessDetails") + private final DetailedMatchtingStatistic matchingDetails; + + } + + @Getter + @Setter + @RequiredArgsConstructor + private static class ErrorEntry { + + @JsonProperty("code") + private final String errorCode; + + @JsonProperty("msg") + private final String errorMessage; + + } + +} -- cgit v1.2.3