diff --git a/.gitignore b/.gitignore index 176230d5..8082ba68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,88 +1,89 @@ -# ending with / = directory, starting with / = absolute (from .gitignore location) -# e.g. target/ is target directory ignored anywhere in the tree, /.idea/ is only in the root -# to override generic rule, use !, e.g. !/some/module/build/ would not be ignored - -# Java/Maven build tool files -target/ -build/ -test-output/ -dependency-reduced-pom.xml -apache-maven-3.9.9/ -apache-maven-3.9.9-bin.zip -.claude - -# Node modules -node_modules/ - -# IDEA files -/.idea/ -/.run/ -*.iml - -# Eclipse files -.project -.classpath -.settings -.settings/ - -# Other IDE files -.c9/ -*.launch -*.sublime-workspace - -# Visual Studio Code -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# JRebel files -rebel.xml - -# jenv related -.java-version - -# NetBeans files -/nb-configuration.xml -/nbactions.xml - -# Backup, log and other files -*~ -*.log -*.versionsBackup -.DS_Store -Thumbs.db -_mess -.checkstyle - -# midPoint Studio temporary files -/scratches/ - -# Angular / Frontend -frontend/node_modules/ -frontend/dist/ -frontend/.angular/ -frontend/package-lock.json -angular-frontend/node_modules/ -angular-frontend/dist/ -angular-frontend/tmp/ -angular-frontend/out-tsc/ -angular-frontend/bazel-out/ -angular-frontend/.angular/ -angular-frontend/.sass-cache/ -angular-frontend/connect.lock -angular-frontend/coverage/ -angular-frontend/libpeerconnection.log -angular-frontend/testem.log -angular-frontend/typings/ -angular-frontend/__screenshots__/ - -# Logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* - -# Docker secrets -docker/secrets/ +# ending with / = directory, starting with / = absolute (from .gitignore location) +# e.g. target/ is target directory ignored anywhere in the tree, /.idea/ is only in the root +# to override generic rule, use !, e.g. !/some/module/build/ would not be ignored + +# Java/Maven build tool files +target/ +build/ +test-output/ +dependency-reduced-pom.xml +apache-maven-3.9.9/ +apache-maven-3.9.9-bin.zip +.claude + +# Node modules +node_modules/ + +# IDEA files +/.idea/ +/.run/ +*.iml + +# Eclipse files +.project +.classpath +.settings +.settings/ + +# Other IDE files +.c9/ +*.launch +*.sublime-workspace + +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# JRebel files +rebel.xml + +# jenv related +.java-version + +# NetBeans files +/nb-configuration.xml +/nbactions.xml + +# Backup, log and other files +*~ +*.log +logs/ +*.versionsBackup +.DS_Store +Thumbs.db +_mess +.checkstyle + +# midPoint Studio temporary files +/scratches/ + +# Angular / Frontend +frontend/node_modules/ +frontend/dist/ +frontend/.angular/ +frontend/package-lock.json +angular-frontend/node_modules/ +angular-frontend/dist/ +angular-frontend/tmp/ +angular-frontend/out-tsc/ +angular-frontend/bazel-out/ +angular-frontend/.angular/ +angular-frontend/.sass-cache/ +angular-frontend/connect.lock +angular-frontend/coverage/ +angular-frontend/libpeerconnection.log +angular-frontend/testem.log +angular-frontend/typings/ +angular-frontend/__screenshots__/ + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Docker secrets +docker/secrets/ diff --git a/pom.xml b/pom.xml index 4207f492..f2776249 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,10 @@ + + GitHub + https://github.com/Evolveum/integration-catalog/issues + 21 @@ -109,11 +113,56 @@ + + + + src/main/resources + true + + banner.txt + + + + src/main/resources + false + + banner.txt + + + org.springframework.boot spring-boot-maven-plugin + + + io.github.git-commit-id + git-commit-id-maven-plugin + 9.0.1 + + + get-git-info + initialize + + revision + + + + + false + true + + diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/IntegrationCatalogApplication.java b/src/main/java/com/evolveum/midpoint/integration/catalog/IntegrationCatalogApplication.java index 9549ad92..3d5e9f96 100644 --- a/src/main/java/com/evolveum/midpoint/integration/catalog/IntegrationCatalogApplication.java +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/IntegrationCatalogApplication.java @@ -27,6 +27,11 @@ public class IntegrationCatalogApplication { = (Logger) LoggerFactory.getLogger(IntegrationCatalogApplication.class); public static void main(String[] args) { + // Guarantee the banner is the first thing logged. Spring Boot's background + // pre-initializer warms up classes (e.g. Hibernate Validator) on a separate + // thread and can log before the banner is printed; disabling it keeps the + // banner on top. Must be set before SpringApplication.run(...). + System.setProperty("spring.backgroundpreinitializer.ignore", "true"); SpringApplication.run(IntegrationCatalogApplication.class, args); } diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/ConsoleLoggingToggle.java b/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/ConsoleLoggingToggle.java new file mode 100644 index 00000000..a4639fc8 --- /dev/null +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/ConsoleLoggingToggle.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2010-2025 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.integration.catalog.configuration; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import org.slf4j.LoggerFactory; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * Keeps the terminal quiet once the application is running, without losing warnings/errors. + *

+ * During startup the console ("stdout") appender has no level filter, so all initialization + * logs are visible in the terminal (and saved to file). Once the application is fully + * initialized ({@link ApplicationReadyEvent}), a {@link ThresholdFilter} set to {@code WARN} + * is added to the console appender, so from then on only {@code WARN}/{@code ERROR} flash in + * the terminal while {@code INFO}/{@code DEBUG} keep going to the file only. The file appender + * is never touched, so the log file continues to capture everything. + */ +@Component +public class ConsoleLoggingToggle { + + private static final Logger LOG = (Logger) LoggerFactory.getLogger(ConsoleLoggingToggle.class); + + /** Must match the appender name in log4j.xml. */ + private static final String CONSOLE_APPENDER_NAME = "stdout"; + + /** Minimum level allowed to reach the terminal after startup. */ + private static final String RUNTIME_CONSOLE_THRESHOLD = "WARN"; + + @EventListener(ApplicationReadyEvent.class) + public void raiseConsoleThresholdAfterStartup() { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + Logger rootLogger = context.getLogger(Logger.ROOT_LOGGER_NAME); + Appender consoleAppender = rootLogger.getAppender(CONSOLE_APPENDER_NAME); + + if (consoleAppender == null) { + LOG.warn("Console appender '{}' was not found on the root logger; terminal output was not changed.", + CONSOLE_APPENDER_NAME); + return; + } + + // Log the hand-off while the console still shows INFO, so this is the last + // informational line the user sees in the terminal. + LOG.info("Application fully initialized. Terminal now shows {}+ only; all logs continue to the file.", + RUNTIME_CONSOLE_THRESHOLD); + + ThresholdFilter filter = new ThresholdFilter(); + filter.setLevel(RUNTIME_CONSOLE_THRESHOLD); + filter.setContext(context); + filter.start(); + consoleAppender.addFilter(filter); + } +} diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/RequestLoggingFilter.java b/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/RequestLoggingFilter.java new file mode 100644 index 00000000..d3fff716 --- /dev/null +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/configuration/RequestLoggingFilter.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2010-2025 Evolveum and contributors + * + * Licensed under the EUPL-1.2 or later. + */ + +package com.evolveum.midpoint.integration.catalog.configuration; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * Logs one line per REST request: HTTP method, URI, resulting status and duration. + * Covers every {@code /api/**} endpoint uniformly, so individual controller methods + * don't have to repeat request/response boilerplate logging. + */ +@Slf4j +@Component +public class RequestLoggingFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + long start = System.currentTimeMillis(); + String method = request.getMethod(); + String uri = request.getRequestURI(); + String query = request.getQueryString(); + String fullUri = query == null ? uri : uri + "?" + query; + + log.debug("--> {} {}", method, fullUri); + try { + filterChain.doFilter(request, response); + } finally { + long duration = System.currentTimeMillis() - start; + int status = response.getStatus(); + if (status >= 500) { + log.error("<-- {} {} {} ({} ms)", method, fullUri, status, duration); + } else if (status >= 400) { + log.warn("<-- {} {} {} ({} ms)", method, fullUri, status, duration); + } else { + log.info("<-- {} {} {} ({} ms)", method, fullUri, status, duration); + } + } + } + + @Override + protected boolean shouldNotFilter(HttpServletRequest request) { + // Only log REST API traffic, not SPA static-resource forwarding. + return !request.getRequestURI().startsWith("/api/"); + } +} diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/controller/AuthController.java b/src/main/java/com/evolveum/midpoint/integration/catalog/controller/AuthController.java index 83ae5d17..e09739e1 100644 --- a/src/main/java/com/evolveum/midpoint/integration/catalog/controller/AuthController.java +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/controller/AuthController.java @@ -14,12 +14,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; +@Slf4j @RestController @RequestMapping("/api/auth") @Tag(name = "Auth", description = "Authentication endpoints") @@ -38,20 +40,29 @@ public AuthController(AuthService authService) { }) @PostMapping("/login") public ResponseEntity login(@Valid @RequestBody LoginRequestDto request) { + log.info("login attempt username={}", request.username()); return authService.login(request.username(), request.password()) - .map(ResponseEntity::ok) - .orElse(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()); + .map(response -> { + log.info("login successful username={}", request.username()); + return ResponseEntity.ok(response); + }) + .orElseGet(() -> { + log.warn("login failed username={}", request.username()); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + }); } @Operation(summary = "Get organization members", description = "Returns all usernames in the same organization as the given user") @GetMapping("/organization/members") public ResponseEntity> getOrganizationMembers(@RequestParam String username) { + log.debug("getOrganizationMembers username={}", username); return ResponseEntity.ok(authService.getOrganizationMembers(username)); } @Operation(summary = "Get all maintainers", description = "Returns all usernames and organization names — for superuser use") @GetMapping("/all-maintainers") public ResponseEntity> getAllMaintainers() { + log.debug("getAllMaintainers"); return ResponseEntity.ok(authService.getAllMaintainers()); } } diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/controller/Controller.java b/src/main/java/com/evolveum/midpoint/integration/catalog/controller/Controller.java index e20ebd2b..8d299e4a 100644 --- a/src/main/java/com/evolveum/midpoint/integration/catalog/controller/Controller.java +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/controller/Controller.java @@ -76,11 +76,13 @@ public Controller(ApplicationService applicationService, }) @GetMapping("/applications/{id}") public ResponseEntity getApplication(@PathVariable UUID id) { + log.debug("getApplication id={}", id); try { Application app = applicationService.getApplication(id); ApplicationDto dto = applicationMapper.mapToApplicationDto(app); return ResponseEntity.ok(dto); } catch (RuntimeException ex) { + log.error("getApplication failed id={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to load application: " + ex.getMessage(), ex); } @@ -94,9 +96,11 @@ public ResponseEntity getApplication(@PathVariable UUID id) { }) @GetMapping("/application-tags") public ResponseEntity> getApplicationTags() { + log.debug("getApplicationTags"); try { return ResponseEntity.ok(applicationService.getApplicationTags()); } catch (RuntimeException ex) { + log.warn("getApplicationTags failed: {}", ex.getMessage()); return ResponseEntity.notFound().build(); } } @@ -106,6 +110,7 @@ public ResponseEntity> getApplicationTags() { @ApiResponse(responseCode = "200", description = "Integration method types retrieved successfully") @GetMapping("/integration-method-types") public ResponseEntity> getIntegrationMethodTypes() { + log.debug("getIntegrationMethodTypes"); return ResponseEntity.ok(applicationService.getIntegrationMethodTypes()); } @@ -114,6 +119,7 @@ public ResponseEntity> getIntegrationMethodTypes() { @ApiResponse(responseCode = "200", description = "MidPoint versions retrieved successfully") @GetMapping("/midpoint-versions") public ResponseEntity> getMidpointVersions() { + log.debug("getMidpointVersions"); return ResponseEntity.ok(applicationService.getMidpointVersions()); } @@ -125,9 +131,11 @@ public ResponseEntity> getMidpointVersions() { }) @GetMapping("/countries-of-origin") public ResponseEntity> getCountriesOfOrigin() { + log.debug("getCountriesOfOrigin"); try { return ResponseEntity.ok(applicationService.getCountriesOfOrigin()); } catch (RuntimeException ex) { + log.warn("getCountriesOfOrigin failed: {}", ex.getMessage()); return ResponseEntity.notFound().build(); } } @@ -135,6 +143,7 @@ public ResponseEntity> getCountriesOfOrigin() { @Operation(summary = "Check if connector bundle name exists", description = "Returns true if the specified bundle name is already taken") @GetMapping("/upload/check-bundle-name") public ResponseEntity checkBundleNameExists(@RequestParam String bundleName) { + log.debug("checkBundleNameExists bundleName={}", bundleName); boolean exists = applicationService.checkBundleNameExists(bundleName); return ResponseEntity.ok(exists); } @@ -144,11 +153,14 @@ public ResponseEntity checkBundleNameExists(@RequestParam String bundle public ResponseEntity uploadConnector( @RequestBody UploadImplementationDto dto, @RequestHeader(value = "X-User-Name", required = false, defaultValue = "anonymous") String username) { + log.info("uploadConnector user={}", username); try { return ResponseEntity.status(HttpStatus.OK).body(applicationService.uploadConnector(dto, username)); } catch (IllegalArgumentException e) { + log.warn("uploadConnector rejected user={}: {}", username, e.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); } catch (org.springframework.dao.DataIntegrityViolationException e) { + log.warn("uploadConnector data conflict user={}: {}", username, e.getMostSpecificCause().getMessage()); throw new ResponseStatusException(HttpStatus.CONFLICT, "Publish failed due to a data conflict (e.g. duplicate bundle version). " + "Please ensure the database migration 03_fix_bundle_version_unique_constraint.sql has been applied. " + @@ -163,6 +175,7 @@ public ResponseEntity uploadConnector( }) @PostMapping("/upload/continue/{oid}") public ResponseEntity completeBuildSuccessfully(@RequestBody ContinueForm continueForm, @PathVariable UUID oid) { + log.info("completeBuildSuccessfully oid={}", oid); applicationService.successBuild(oid, continueForm); return ResponseEntity.ok().build(); } @@ -174,6 +187,7 @@ public ResponseEntity completeBuildSuccessfully(@RequestBody ContinueForm }) @PostMapping("/upload/continue/fail/{oid}") public ResponseEntity completeBuildWithFailure(@RequestBody FailForm failForm, @PathVariable UUID oid) { + log.info("completeBuildWithFailure oid={}", oid); applicationService.failBuild(oid, failForm); return ResponseEntity.ok().build(); } @@ -188,9 +202,11 @@ public ResponseEntity> searchApplication( @RequestBody SearchForm searchForm, @PathVariable int size, @PathVariable int page) { + log.debug("searchApplication page={} size={}", page, size); try { return ResponseEntity.ok(applicationService.searchApplication(searchForm, page, size)); } catch (RuntimeException ex) { + log.warn("searchApplication failed page={} size={}: {}", page, size, ex.getMessage()); return ResponseEntity.notFound().build(); } } @@ -205,9 +221,11 @@ public ResponseEntity> searchIntegrationMethods( @RequestBody SearchForm searchForm, @PathVariable int size, @PathVariable int page) { + log.debug("searchIntegrationMethods page={} size={}", page, size); try { return ResponseEntity.ok(applicationService.searchIntegrationMethods(searchForm, page, size)); } catch (RuntimeException ex) { + log.warn("searchIntegrationMethods failed page={} size={}: {}", page, size, ex.getMessage()); return ResponseEntity.notFound().build(); } } @@ -219,6 +237,7 @@ public ResponseEntity> searchIntegrationMethods( }) @GetMapping("/requests/{id}") public ResponseEntity getRequest(@PathVariable Long id) { + log.debug("getRequest id={}", id); return applicationService.getRequest(id) .map(ResponseEntity::ok) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Request not found")); @@ -231,6 +250,7 @@ public ResponseEntity getRequest(@PathVariable Long id) { }) @GetMapping("/applications/{appId}/request") public ResponseEntity getRequestForApplication(@PathVariable UUID appId) { + log.debug("getRequestForApplication appId={}", appId); return applicationService.getRequestForApplication(appId) .map(ResponseEntity::ok) .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Request not found for application")); @@ -244,12 +264,16 @@ public ResponseEntity getRequestForApplication(@PathVariable UUID appId }) @PostMapping("/requests") public ResponseEntity createRequest(@Valid @RequestBody RequestFormDto dto) { + log.info("createRequest"); try { Request created = applicationService.createRequestFromForm(dto); + log.info("createRequest created id={}", created.getId()); return ResponseEntity.status(HttpStatus.CREATED).body(created); } catch (IllegalArgumentException ex) { + log.warn("createRequest rejected: {}", ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (Exception ex) { + log.error("createRequest failed: {}", ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create request: " + ex.getMessage()); } } @@ -262,12 +286,15 @@ public ResponseEntity createRequest(@Valid @RequestBody RequestFormDto }) @DeleteMapping("/requests/{requestId}") public ResponseEntity cancelRequest(@PathVariable Long requestId) { + log.info("cancelRequest requestId={}", requestId); try { applicationService.cancelRequest(requestId); return ResponseEntity.noContent().build(); } catch (IllegalArgumentException ex) { + log.warn("cancelRequest not found requestId={}: {}", requestId, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } catch (Exception ex) { + log.error("cancelRequest failed requestId={}: {}", requestId, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to cancel request: " + ex.getMessage()); } @@ -281,10 +308,12 @@ public ResponseEntity cancelRequest(@PathVariable Long requestId) { }) @PostMapping("/requests/{requestId}/vote") public ResponseEntity submitVote(@PathVariable Long requestId, @RequestParam String voter) { + log.info("submitVote requestId={} voter={}", requestId, voter); try { Vote vote = applicationService.submitVote(requestId, voter); return ResponseEntity.status(HttpStatus.CREATED).body(vote); } catch (IllegalArgumentException ex) { + log.warn("submitVote rejected requestId={} voter={}: {}", requestId, voter, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } } @@ -296,6 +325,7 @@ public ResponseEntity submitVote(@PathVariable Long requestId, @RequestPar }) @GetMapping("/requests/{requestId}/votes/count") public ResponseEntity getVoteCount(@PathVariable Long requestId) { + log.debug("getVoteCount requestId={}", requestId); long count = applicationService.getVoteCount(requestId); return ResponseEntity.ok(count); } @@ -307,6 +337,7 @@ public ResponseEntity getVoteCount(@PathVariable Long requestId) { }) @GetMapping("/requests/{requestId}/votes/check") public ResponseEntity hasUserVoted(@PathVariable Long requestId, @RequestParam String voter) { + log.debug("hasUserVoted requestId={} voter={}", requestId, voter); boolean hasVoted = applicationService.hasUserVoted(requestId, voter); return ResponseEntity.ok(hasVoted); } @@ -318,6 +349,7 @@ public ResponseEntity hasUserVoted(@PathVariable Long requestId, @Reque }) @GetMapping("/categories/counts") public ResponseEntity> getCategoryCounts() { + log.debug("getCategoryCounts"); return ResponseEntity.ok(applicationService.getCategoryCounts()); } @@ -329,6 +361,7 @@ public ResponseEntity> getCategoryCounts() { }) @GetMapping("/applications") public ResponseEntity> getAllApplications() { + log.debug("getAllApplications"); Page page = applicationService.list(Pageable.unpaged(), null, null); return ResponseEntity.ok(page.getContent()); } @@ -340,6 +373,7 @@ public ResponseEntity> getAllApplications() { }) @GetMapping("/connectors/active") public ResponseEntity> getActiveConnectors() { + log.debug("getActiveConnectors"); return ResponseEntity.ok(applicationService.listActiveConnectors()); } @@ -350,6 +384,7 @@ public ResponseEntity> getActiveConnectors() { }) @GetMapping("/connectors/catalog") public ResponseEntity> getCatalogConnectors() { + log.debug("getCatalogConnectors"); return ResponseEntity.ok(applicationService.listCatalogConnectors()); } @@ -360,6 +395,7 @@ public ResponseEntity> getCatalogConnectors() { }) @GetMapping("/capabilities") public ResponseEntity> getCapabilities() { + log.debug("getCapabilities"); return ResponseEntity.ok(applicationService.getCapabilities()); } @@ -370,6 +406,7 @@ public ResponseEntity> getCapabilities() { }) @GetMapping("/statistics/downloads-count") public ResponseEntity getTotalDownloadsCount() { + log.debug("getTotalDownloadsCount"); return ResponseEntity.ok(applicationService.getTotalDownloadsCount()); } @@ -380,6 +417,7 @@ public ResponseEntity getTotalDownloadsCount() { }) @GetMapping("/applications/{applicationId}/downloads-count") public ResponseEntity getApplicationDownloadsCount(@PathVariable UUID applicationId) { + log.debug("getApplicationDownloadsCount applicationId={}", applicationId); return ResponseEntity.ok(applicationService.countDownloadsForApplication(applicationId)); } @@ -392,9 +430,11 @@ public ResponseEntity getApplicationDownloadsCount(@PathVariable UUID appl }) @PostMapping("/upload/verify") public ResponseEntity verify(@RequestBody VerifyBundleInformationForm verifyPayload) { + log.info("verify bundle"); try { return ResponseEntity.status(HttpStatus.OK).body(applicationService.verify(verifyPayload)); } catch (IllegalArgumentException e) { + log.warn("verify rejected: {}", e.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); } } @@ -407,6 +447,7 @@ public ResponseEntity verify(@RequestBody VerifyBundleInformationForm v }) @GetMapping("/applications/{applicationId}/implementations") public ResponseEntity> getIntegrationMethodsByApplicationId(@PathVariable UUID applicationId) { + log.debug("getIntegrationMethodsByApplicationId applicationId={}", applicationId); List items = applicationService.getIntegrationMethodsByApplicationId(applicationId); return ResponseEntity.ok(items); } @@ -418,12 +459,16 @@ public ResponseEntity editIntegrationMethod( @PathVariable UUID methodId, @PathVariable String currentRevision, @RequestBody EditIntegrationMethodDto dto) { + log.info("editIntegrationMethod appId={} methodId={} currentRevision={}", appId, methodId, currentRevision); try { String newRevision = applicationService.editIntegrationMethod(methodId, currentRevision, dto); + log.info("editIntegrationMethod saved methodId={} newRevision={}", methodId, newRevision); return ResponseEntity.ok(newRevision); } catch (IllegalStateException e) { + log.warn("editIntegrationMethod conflict methodId={}: {}", methodId, e.getMessage()); throw new ResponseStatusException(HttpStatus.CONFLICT, e.getMessage()); } catch (RuntimeException e) { + log.warn("editIntegrationMethod not found methodId={}: {}", methodId, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -442,12 +487,15 @@ public ResponseEntity publishIntegrationMethod( @PathVariable UUID methodId, @PathVariable String revision, @RequestHeader(value = "X-User-Name", required = false, defaultValue = "anonymous") String username) { + log.info("publishIntegrationMethod appId={} methodId={} revision={} user={}", appId, methodId, revision, username); try { applicationService.publishIntegrationMethod(methodId, revision, username); return ResponseEntity.ok().build(); } catch (IllegalStateException e) { + log.warn("publishIntegrationMethod conflict methodId={} revision={}: {}", methodId, revision, e.getMessage()); throw new ResponseStatusException(HttpStatus.CONFLICT, e.getMessage()); } catch (RuntimeException e) { + log.warn("publishIntegrationMethod not found methodId={} revision={}: {}", methodId, revision, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -466,12 +514,15 @@ public ResponseEntity rejectIntegrationMethod( @PathVariable UUID methodId, @PathVariable String revision, @RequestHeader(value = "X-User-Name", required = false, defaultValue = "anonymous") String username) { + log.info("rejectIntegrationMethod appId={} methodId={} revision={} user={}", appId, methodId, revision, username); try { applicationService.rejectIntegrationMethod(methodId, revision, username); return ResponseEntity.ok().build(); } catch (IllegalStateException e) { + log.warn("rejectIntegrationMethod conflict methodId={} revision={}: {}", methodId, revision, e.getMessage()); throw new ResponseStatusException(HttpStatus.CONFLICT, e.getMessage()); } catch (RuntimeException e) { + log.warn("rejectIntegrationMethod not found methodId={} revision={}: {}", methodId, revision, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -489,10 +540,12 @@ public ResponseEntity addConnectorToIntegrationMethod( @PathVariable String revision, @RequestBody AddConnectorDto dto, @RequestHeader(value = "X-User-Name", required = false, defaultValue = "anonymous") String username) { + log.info("addConnectorToIntegrationMethod appId={} methodId={} revision={} user={}", appId, methodId, revision, username); try { applicationService.addConnectorToIntegrationMethod(appId, methodId, revision, dto, username); return ResponseEntity.ok().build(); } catch (RuntimeException e) { + log.warn("addConnectorToIntegrationMethod not found methodId={} revision={}: {}", methodId, revision, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -503,6 +556,7 @@ public ResponseEntity> getConnectorsForIntegrati @PathVariable UUID appId, @PathVariable UUID methodId, @PathVariable String revision) { + log.debug("getConnectorsForIntegrationMethod methodId={} revision={}", methodId, revision); return ResponseEntity.ok(applicationService.getConnectorsForIntegrationMethod(methodId, revision)); } @@ -519,10 +573,12 @@ public ResponseEntity updateConnector( @PathVariable String revision, @PathVariable Integer connectorId, @RequestBody EditConnectorDto dto) { + log.info("updateConnector appId={} methodId={} revision={} connectorId={}", appId, methodId, revision, connectorId); try { applicationService.updateConnector(methodId, revision, connectorId, dto); return ResponseEntity.ok().build(); } catch (RuntimeException e) { + log.warn("updateConnector not found methodId={} connectorId={}: {}", methodId, connectorId, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -540,11 +596,13 @@ public ResponseEntity updateConnectorCompatibility( @PathVariable String revision, @PathVariable Integer connectorId, @RequestBody UpdateConnectorCompatibilityDto dto) { + log.info("updateConnectorCompatibility appId={} methodId={} revision={} connectorId={}", appId, methodId, revision, connectorId); try { applicationService.updateConnectorCompatibility(methodId, revision, connectorId, dto.connectorVersionFrom(), dto.connectorVersionTo()); return ResponseEntity.ok().build(); } catch (RuntimeException e) { + log.warn("updateConnectorCompatibility not found methodId={} connectorId={}: {}", methodId, connectorId, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -561,10 +619,12 @@ public ResponseEntity deleteConnectorFromIntegrationMethod( @PathVariable UUID methodId, @PathVariable String revision, @PathVariable Integer connectorId) { + log.info("deleteConnectorFromIntegrationMethod appId={} methodId={} revision={} connectorId={}", appId, methodId, revision, connectorId); try { applicationService.deleteConnectorFromIntegrationMethod(methodId, revision, connectorId); return ResponseEntity.ok().build(); } catch (RuntimeException e) { + log.warn("deleteConnectorFromIntegrationMethod not found methodId={} connectorId={}: {}", methodId, connectorId, e.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); } } @@ -583,19 +643,24 @@ public ResponseEntity deleteConnectorFromIntegrationMethod( public ResponseEntity uploadLogo( @PathVariable UUID id, @RequestParam("file") MultipartFile file) { + log.info("uploadLogo id={} filename={} size={}", id, file.getOriginalFilename(), file.getSize()); try { Application application = applicationService.getApplication(id); logoStorageService.saveLogo(application, file); return ResponseEntity.ok().build(); } catch (IllegalArgumentException ex) { + log.warn("uploadLogo rejected id={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (RuntimeException ex) { if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + log.warn("uploadLogo not found id={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } + log.error("uploadLogo failed id={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to upload logo: " + ex.getMessage(), ex); } catch (IOException ex) { + log.error("uploadLogo file save failed id={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to save logo file: " + ex.getMessage(), ex); } @@ -613,18 +678,23 @@ public ResponseEntity uploadLogo( public ResponseEntity uploadTutorial( @PathVariable UUID id, @RequestParam("file") MultipartFile file) { + log.info("uploadTutorial methodId={} filename={} size={}", id, file.getOriginalFilename(), file.getSize()); try { tutorialStorageService.saveTutorial(id, file); return ResponseEntity.ok().build(); } catch (IllegalArgumentException ex) { + log.warn("uploadTutorial rejected methodId={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (RuntimeException ex) { if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + log.warn("uploadTutorial not found methodId={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } + log.error("uploadTutorial failed methodId={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to upload tutorial: " + ex.getMessage(), ex); } catch (IOException ex) { + log.error("uploadTutorial file save failed methodId={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to save tutorial file: " + ex.getMessage(), ex); } @@ -637,18 +707,24 @@ public ResponseEntity uploadTutorialForRevision( @PathVariable UUID methodId, @PathVariable String revision, @RequestParam("file") MultipartFile file) { + log.info("uploadTutorialForRevision appId={} methodId={} revision={} filename={} size={}", + appId, methodId, revision, file.getOriginalFilename(), file.getSize()); try { tutorialStorageService.saveTutorialForRevision(methodId, revision, file); return ResponseEntity.ok().build(); } catch (IllegalArgumentException ex) { + log.warn("uploadTutorialForRevision rejected methodId={} revision={}: {}", methodId, revision, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (RuntimeException ex) { if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + log.warn("uploadTutorialForRevision not found methodId={} revision={}: {}", methodId, revision, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } + log.error("uploadTutorialForRevision failed methodId={} revision={}: {}", methodId, revision, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to upload tutorial: " + ex.getMessage(), ex); } catch (IOException ex) { + log.error("uploadTutorialForRevision file save failed methodId={} revision={}: {}", methodId, revision, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to save tutorial file: " + ex.getMessage(), ex); } @@ -660,6 +736,7 @@ public ResponseEntity> listTutorialFiles( @PathVariable UUID appId, @PathVariable UUID methodId, @PathVariable String revision) { + log.debug("listTutorialFiles methodId={} revision={}", methodId, revision); return ResponseEntity.ok(tutorialStorageService.listTutorialFiles(methodId, revision)); } @@ -670,6 +747,7 @@ public ResponseEntity downloadTutorialFile( @PathVariable UUID methodId, @PathVariable String revision, @RequestParam("name") String name) { + log.debug("downloadTutorialFile methodId={} revision={} name={}", methodId, revision, name); try { Path file = tutorialStorageService.resolveTutorialFile(methodId, revision, name); byte[] bytes = Files.readAllBytes(file); @@ -679,10 +757,13 @@ public ResponseEntity downloadTutorialFile( .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + name + "\"") .body(bytes); } catch (IllegalArgumentException ex) { + log.warn("downloadTutorialFile rejected methodId={} name={}: {}", methodId, name, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (RuntimeException ex) { + log.warn("downloadTutorialFile not found methodId={} name={}: {}", methodId, name, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } catch (IOException ex) { + log.error("downloadTutorialFile read failed methodId={} name={}: {}", methodId, name, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to read tutorial file: " + ex.getMessage(), ex); } @@ -701,6 +782,7 @@ public ResponseEntity downloadBundle( @PathVariable UUID methodId, @PathVariable String revision, HttpServletRequest request) { + log.info("downloadBundle appId={} methodId={} revision={}", appId, methodId, revision); try { BundleService.Bundle bundle = bundleService.buildBundle(methodId, revision); try { @@ -717,8 +799,10 @@ public ResponseEntity downloadBundle( } return responseBuilder.body(bundle.data()); } catch (IllegalArgumentException ex) { + log.warn("downloadBundle not found methodId={} revision={}: {}", methodId, revision, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } catch (IOException ex) { + log.error("downloadBundle build failed methodId={} revision={}: {}", methodId, revision, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to build bundle: " + ex.getMessage(), ex); } @@ -731,12 +815,15 @@ public ResponseEntity deleteTutorialFile( @PathVariable UUID methodId, @PathVariable String revision, @RequestParam("name") String name) { + log.info("deleteTutorialFile methodId={} revision={} name={}", methodId, revision, name); try { tutorialStorageService.deleteTutorialFile(methodId, revision, name); return ResponseEntity.ok().build(); } catch (IllegalArgumentException ex) { + log.warn("deleteTutorialFile rejected methodId={} name={}: {}", methodId, name, ex.getMessage()); throw new ResponseStatusException(HttpStatus.BAD_REQUEST, ex.getMessage()); } catch (IOException ex) { + log.error("deleteTutorialFile failed methodId={} name={}: {}", methodId, name, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to delete tutorial file: " + ex.getMessage(), ex); } @@ -753,10 +840,12 @@ public ResponseEntity deleteTutorialFile( public ResponseEntity getLogo( @PathVariable UUID id, @RequestHeader(value = HttpHeaders.IF_NONE_MATCH, required = false) String ifNoneMatch) { + log.debug("getLogo id={}", id); Application application; try { application = applicationService.getApplication(id); } catch (RuntimeException ex) { + log.warn("getLogo application not found id={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Application not found"); } @@ -802,14 +891,17 @@ private String detectContentType(String logoPath) { }) @DeleteMapping("/applications/{id}/logo") public ResponseEntity deleteLogo(@PathVariable UUID id) { + log.info("deleteLogo id={}", id); try { Application application = applicationService.getApplication(id); logoStorageService.deleteLogo(application); return ResponseEntity.noContent().build(); } catch (RuntimeException ex) { if (ex.getMessage() != null && ex.getMessage().contains("not found")) { + log.warn("deleteLogo not found id={}: {}", id, ex.getMessage()); throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage()); } + log.error("deleteLogo failed id={}: {}", id, ex.getMessage(), ex); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to delete logo: " + ex.getMessage(), ex); } @@ -824,6 +916,7 @@ public ResponseEntity deleteLogo(@PathVariable UUID id) { }) @GetMapping("/recently-used") public ResponseEntity> getRecentlyUsed() { + log.debug("getRecentlyUsed"); return ResponseEntity.ok(applicationService.getRecentlyUsedApplications()); } @@ -836,6 +929,7 @@ public ResponseEntity> getRecentlyUsed() { public ResponseEntity recordRecentlyUsed( @PathVariable UUID applicationId, @RequestHeader(value = "X-User-Name", required = false, defaultValue = "anonymous") String username) { + log.info("recordRecentlyUsed applicationId={} user={}", applicationId, username); applicationService.recordRecentlyUsed(applicationId, username); return ResponseEntity.noContent().build(); } diff --git a/src/main/java/com/evolveum/midpoint/integration/catalog/service/ApplicationService.java b/src/main/java/com/evolveum/midpoint/integration/catalog/service/ApplicationService.java index 80035751..284d44cd 100644 --- a/src/main/java/com/evolveum/midpoint/integration/catalog/service/ApplicationService.java +++ b/src/main/java/com/evolveum/midpoint/integration/catalog/service/ApplicationService.java @@ -18,7 +18,9 @@ import com.evolveum.midpoint.integration.catalog.repository.*; import com.evolveum.midpoint.integration.catalog.repository.adapter.ApplicationReadPort; +import ch.qos.logback.classic.Logger; import lombok.extern.slf4j.Slf4j; +import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -42,6 +44,9 @@ @Service public class ApplicationService { + private static final Logger LOG + = (Logger) LoggerFactory.getLogger(ApplicationService.class); + private final ApplicationRepository applicationRepository; private final ApplicationTagRepository applicationTagRepository; private final CountryOfOriginRepository countryOfOriginRepository; @@ -169,43 +174,52 @@ public List getCapabilities() { @Transactional public String uploadConnector(UploadImplementationDto dto, String username) { + LOG.info("uploadConnector user={}", username); return connectorUploadService.uploadConnector(dto, username); } @Transactional public String editIntegrationMethod(UUID methodId, String currentRevision, EditIntegrationMethodDto dto) { + LOG.info("editIntegrationMethod methodId={} currentRevision={}", methodId, currentRevision); return connectorUploadService.editIntegrationMethod(methodId, currentRevision, dto); } @Transactional public void publishIntegrationMethod(UUID methodId, String revision, String username) { + LOG.info("publishIntegrationMethod methodId={} revision={} user={}", methodId, revision, username); connectorUploadService.publishIntegrationMethod(methodId, revision, username); } @Transactional public void rejectIntegrationMethod(UUID methodId, String revision, String username) { + LOG.info("rejectIntegrationMethod methodId={} revision={} user={}", methodId, revision, username); connectorUploadService.rejectIntegrationMethod(methodId, revision, username); } @Transactional public void addConnectorToIntegrationMethod(UUID appId, UUID methodId, String revision, AddConnectorDto dto, String username) { + LOG.info("addConnectorToIntegrationMethod appId={} methodId={} revision={} user={}", appId, methodId, revision, username); connectorUploadService.addConnectorToIntegrationMethod(appId, methodId, revision, dto, username); } @Transactional public void updateConnector(UUID methodId, String revision, Integer connectorId, EditConnectorDto dto) { + LOG.info("updateConnector methodId={} revision={} connectorId={}", methodId, revision, connectorId); connectorUploadService.updateConnector(methodId, revision, connectorId, dto); } @Transactional public void deleteConnectorFromIntegrationMethod(UUID methodId, String revision, Integer connectorId) { + LOG.info("deleteConnectorFromIntegrationMethod methodId={} revision={} connectorId={}", methodId, revision, connectorId); connectorUploadService.deleteConnectorFromIntegrationMethod(methodId, revision, connectorId); } @Transactional public void updateConnectorCompatibility(UUID methodId, String revision, Integer connectorId, String connectorVersionFrom, String connectorVersionTo) { + LOG.info("updateConnectorCompatibility methodId={} revision={} connectorId={} from={} to={}", + methodId, revision, connectorId, connectorVersionFrom, connectorVersionTo); connectorUploadService.updateConnectorCompatibility(methodId, revision, connectorId, connectorVersionFrom, connectorVersionTo); } @@ -219,11 +233,13 @@ public List getConnectorsForIntegrationMethod(UUID me @Transactional public void successBuild(UUID oid, ContinueForm continueForm) { + LOG.info("successBuild oid={}", oid); buildCallbackService.successBuild(oid, continueForm); } @Transactional public void failBuild(UUID oid, FailForm failForm) { + LOG.info("failBuild oid={}", oid); buildCallbackService.failBuild(oid, failForm); } @@ -281,6 +297,7 @@ public List getRequests() { @Transactional public Request createRequestFromForm(RequestFormDto dto) { + LOG.info("createRequestFromForm"); return requestVotingService.createRequestFromForm(dto); } @@ -293,6 +310,7 @@ public Optional getRequestForApplication(UUID appId) { } public Vote submitVote(Long requestId, String voter) { + LOG.info("submitVote requestId={} voter={}", requestId, voter); return requestVotingService.submitVote(requestId, voter); } @@ -305,10 +323,12 @@ public boolean hasUserVoted(Long requestId, String voter) { } public void cancelRequest(Long requestId) { + LOG.info("cancelRequest requestId={}", requestId); requestVotingService.cancelRequest(requestId); } public void recordMethodDownload(UUID methodId, String revision, String ip, String userAgent) { + LOG.info("recordMethodDownload methodId={} revision={} ip={}", methodId, revision, ip); connectorDownloadService.recordMethodDownload(methodId, revision, ip, userAgent); } @@ -410,6 +430,7 @@ public List getRecentlyUsedApplications() { @Transactional public void recordRecentlyUsed(UUID applicationId, String userId) { + LOG.info("recordRecentlyUsed applicationId={} user={}", applicationId, userId); recentlyUsedApplicationRepository.deleteByUserIdAndApplicationId(userId, applicationId); recentlyUsedApplicationRepository.flush(); RecentlyUsedApplication entry = new RecentlyUsedApplication() diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 07c4d4b0..36aa0d18 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,12 +18,13 @@ spring.jpa.show-sql=false spring.jpa.properties.hibernate.format_sql=false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect -# Set logging level -logging.level.root=INFO -logging.level.com.example.service=INFO -logging.level.org.hibernate.SQL=INFO -logging.level.org.hibernate.orm.jdbc.bind=INFO -logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %logger{36} - %msg%n +# Logging configuration file (Logback). Levels/appenders/patterns live in log4j.xml. +logging.config=classpath:log4j.xml + +# Render the startup banner (banner.txt) through the logging system instead of +# straight to stdout, so it is written at the top of the log file. It still shows +# in the terminal too, because the console appender is attached during startup. +spring.main.banner-mode=log # GitHub client github.apiToken= diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index ce8c8ad3..66b5b678 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -9,7 +9,7 @@ |___/ |___/ by Evolveum and partners Licensed under the EUPL-1.2 or later. - Version: ${project.version} - Sources: https://github.com/Evolveum/integration-catalog, branch: ${git.branch}, build: ${git.describe} - Bug reporting system: ${project.issueManagement.url} + Version: @project.version@ + Sources: https://github.com/Evolveum/integration-catalog, branch: @git.branch@, build: @git.commit.id.describe@ + Bug reporting system: @project.issueManagement.url@ Product information: https://midpoint.evolveum.com diff --git a/src/main/resources/log4j.xml b/src/main/resources/log4j.xml index 80098301..b8520b62 100644 --- a/src/main/resources/log4j.xml +++ b/src/main/resources/log4j.xml @@ -1,35 +1,37 @@ - - - %d{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%M %L - %msg%n - - + + + %d{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%M %L - %msg%n + + - integration-catalog.log + ./integration-catalog/logs/integration-catalog.log - app.%d{yyyy-MM-dd-mm}.%i.log.zip - 20 - - + ./integration-catalog/logs/app.%d{yyyy-MM-dd-HH}.%i.log.zip 10MB - + 24 + %d{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%M %L - %msg%n - - + - + - - - + + - \ No newline at end of file +