diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8438984..b3db723 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '8' + java-version: '11' - name: Build with Gradle run: ./gradlew build diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml new file mode 100644 index 0000000..6aa6ca1 --- /dev/null +++ b/.github/workflows/maven-publish.yml @@ -0,0 +1,33 @@ +# Copyright 2025 EPAM Systems +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Publish to Maven Central + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 5.4.1)' + required: true + type: string + +jobs: + publish: + uses: reportportal/.github/.github/workflows/maven-publish.yml@main + with: + version: ${{ github.event.inputs.version }} + artifact: logger-java-logback + package_suffixes: '-javadoc.jar,-javadoc.jar.asc,-javadoc.jar.md5,-javadoc.jar.sha1,-javadoc.jar.sha256,-javadoc.jar.sha512,-sources.jar,-sources.jar.asc,-sources.jar.md5,-sources.jar.sha1,-sources.jar.sha256,-sources.jar.sha512,.jar,.jar.asc,.jar.md5,.jar.sha1,.jar.sha256,.jar.sha512,.pom,.pom.asc,.pom.md5,.pom.sha1,.pom.sha256,.pom.sha512,.module,.module.asc,.module.md5,.module.sha1,.module.sha256,.module.sha512' + secrets: + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml deleted file mode 100644 index b611473..0000000 --- a/.github/workflows/promote.yml +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2021 EPAM Systems -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -name: Promote - -on: - workflow_dispatch: - inputs: - version: - description: 'Release version' - required: true - -env: - REPOSITORY_URL: 'https://maven.pkg.github.com' - UPSTREAM_REPOSITORY_URL: 'https://oss.sonatype.org' - PACKAGE_SUFFIXES: '-javadoc.jar,-javadoc.jar.asc,-sources.jar,-sources.jar.asc,.jar,.jar.asc,.pom,.pom.asc' - PACKAGE: 'com.epam.reportportal' - - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Get variables - run: | - echo "ARTIFACT=`echo ${{ github.repository }} | cut -d/ -f2- | awk '{print tolower($0)}'`" >> $GITHUB_ENV - echo "PACKAGE_PATH=`echo ${{ env.PACKAGE }} | sed 's/\./\//g'`" >> $GITHUB_ENV - - - name: Upload package - run: | - IFS=',' read -a files <<< '${{ env.PACKAGE_SUFFIXES }}' - for f in ${files[@]}; do - export URL="${{ env.REPOSITORY_URL }}/${{ github.repository }}/${PACKAGE_PATH}/${ARTIFACT}/${{ github.event.inputs.version }}/${ARTIFACT}-${{ github.event.inputs.version }}${f}" - echo "Downloading artifact: ${URL}" - curl -f -u ${{ github.actor }}:${{ secrets.GITHUB_TOKEN }} -s -O -L "${URL}" - done - files=($(ls)) - echo 'Files downloaded:' - echo "${files[@]}" - - echo 'Bundle generation' - export BUNDLE_FILE="bundle.jar" - jar -cvf ${BUNDLE_FILE} "${files[@]}" - - echo 'Bundle upload' - curl -f -u ${{ secrets.SONATYPE_USER }}:${{ secrets.SONATYPE_PASSWORD }} -L \ - --request POST '${{ env.UPSTREAM_REPOSITORY_URL }}/service/local/staging/bundle_upload' \ - --form "file=@${BUNDLE_FILE}" >response.json - - response_type=`jq type response.json || echo ''` - if [ -z "$response_type" ]; then - echo 'ERROR: Response is not JSON!' 1>&2 - cat response.json 1>&2 - exit 1 - fi - - repo=`jq -r '.repositoryUris[0]' response.json` - if [ -z "$repo" ]; then - echo 'Unable to upload bundle' 1>&2 - cat response.json 1>&2 - exit 1 - fi - - echo "NEXUS_REPOSITORY=${repo}" >> $GITHUB_ENV - - - name: Get repository variables - run: | - echo "NEXUS_REPOSITORY_NAME=`echo ${NEXUS_REPOSITORY} | sed -E 's/(.+)\/([^\/]+)$/\2/'`" >> $GITHUB_ENV - - - name: Promote package - env: - ATTEMPTS: 60 - SLEEP_TIME: 10 - run: | - verified=false - for i in `seq 0 ${ATTEMPTS}`; do - sleep $SLEEP_TIME - curl -f -s -u ${{ secrets.SONATYPE_USER }}:${{ secrets.SONATYPE_PASSWORD }} -L \ - --header 'Accept: application/json' \ - ${{ env.UPSTREAM_REPOSITORY_URL }}/service/local/staging/repository/${NEXUS_REPOSITORY_NAME} >result.json - - is_closed=`jq -r '.type' result.json` - is_transitioning=`jq -r '.transitioning' result.json` - echo "Current repository status: $is_closed; transitioning: $is_transitioning" - - if [[ "$is_closed" == "closed" && "$is_transitioning" == "false" ]]; then - verified=true - break - fi - done - if $verified; then - echo "A bundle was verified, releasing" - curl -f -u ${{ secrets.SONATYPE_USER }}:${{ secrets.SONATYPE_PASSWORD }} -L \ - --header 'Content-Type: application/json' \ - --data-raw "{\"data\":{\"stagedRepositoryIds\":[\"${NEXUS_REPOSITORY_NAME}\"], \"description\":\"Releasing ${{ github.event.inputs.version }}\"}}" \ - --request POST ${{ env.UPSTREAM_REPOSITORY_URL }}/service/local/staging/bulk/promote - else - echo 'Verification failed, please check the bundle' 1>&2 - exit 1 - fi diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f152491..92a60c0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '8' + java-version: '11' - name: Setup git credentials uses: oleksiyrudenko/gha-git-credentials@v2-latest diff --git a/CHANGELOG.md b/CHANGELOG.md index d3d5605..d9fd60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog ## [Unreleased] +### Changed +- Client version updated to [5.4.1](https://github.com/reportportal/client-java/releases/tag/5.4.1), by @HardNorth +- Logback version updated to 1.5.18, by @HardNorth +- Switch on use of `Instant` class instead of `Date` to get more timestamp precision, by @HardNorth +### Removed +- Java 8, 9, 10 support, by @HardNorth ## [5.3.0] ### Changed diff --git a/build.gradle b/build.gradle index 041b40d..6054758 100644 --- a/build.gradle +++ b/build.gradle @@ -29,8 +29,8 @@ project.ext.limits = [ 'class' : 90 ] -sourceCompatibility = JavaVersion.VERSION_1_8 -targetCompatibility = JavaVersion.VERSION_1_8 +sourceCompatibility = JavaVersion.VERSION_11 +targetCompatibility = JavaVersion.VERSION_11 repositories { mavenCentral() @@ -42,7 +42,7 @@ dependencies { testImplementation "com.epam.reportportal:client-java:${client_version}" testImplementation "ch.qos.logback:logback-classic:${logback_version}" - testImplementation 'com.epam.reportportal:agent-java-test-utils:0.0.12' + testImplementation 'com.epam.reportportal:agent-java-test-utils:0.1.0' testImplementation('org.awaitility:awaitility:4.0.2') { exclude group: 'org.hamcrest' } diff --git a/gradle.properties b/gradle.properties index dc4f0b6..1fce142 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,9 @@ -version=5.3.1-SNAPSHOT +version=5.4.0-SNAPSHOT description=EPAM Report portal. Logback Integration scripts_url=https://raw.githubusercontent.com/reportportal/gradle-scripts scripts_branch=master -client_version=5.3.14 -logback_version=1.3.15 +client_version=5.4.1 +logback_version=1.5.18 junit_version=5.6.3 junit_runner_version=1.6.3 mockito_version=3.3.3 diff --git a/src/main/java/com/epam/reportportal/logback/appender/ReportPortalAppender.java b/src/main/java/com/epam/reportportal/logback/appender/ReportPortalAppender.java index 145a4ef..e4fef49 100644 --- a/src/main/java/com/epam/reportportal/logback/appender/ReportPortalAppender.java +++ b/src/main/java/com/epam/reportportal/logback/appender/ReportPortalAppender.java @@ -1,11 +1,11 @@ /* - * Copyright (C) 2019 EPAM Systems + * Copyright 2025 EPAM Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.epam.reportportal.logback.appender; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; @@ -24,8 +25,8 @@ import com.epam.reportportal.message.TypeAwareByteSource; import com.epam.ta.reportportal.ws.model.log.SaveLogRQ; +import java.time.Instant; import java.util.Collections; -import java.util.Date; import java.util.List; import java.util.UUID; @@ -36,67 +37,66 @@ */ public class ReportPortalAppender extends AppenderBase { - private static final MessageParser MESSAGE_PARSER = new HashMarkSeparatedMessageParser(); - private static final List LOGGING_ISSUE = - Collections.singletonList("com.epam.reportportal.service.logs.LoggingSubscriber"); - private PatternLayoutEncoder encoder; + private static final MessageParser MESSAGE_PARSER = new HashMarkSeparatedMessageParser(); + private static final List LOGGING_ISSUE = Collections.singletonList("com.epam.reportportal.service.logs.LoggingSubscriber"); + private PatternLayoutEncoder encoder; - @Override - protected void append(final ILoggingEvent event) { - String loggerName = event.getLoggerName(); - for (String packagePrefix : LOGGING_ISSUE) { - if (loggerName.startsWith(packagePrefix)) { - return; - } - } + @Override + protected void append(final ILoggingEvent event) { + String loggerName = event.getLoggerName(); + for (String packagePrefix : LOGGING_ISSUE) { + if (loggerName.startsWith(packagePrefix)) { + return; + } + } - emitLog(itemUuid -> { - final String message = event.getFormattedMessage(); - final String level = event.getLevel().toString(); - final Date time = new Date(event.getTimeStamp()); + emitLog(itemUuid -> { + final String message = event.getFormattedMessage(); + final String level = event.getLevel().toString(); + final Instant time = event.getInstant(); - SaveLogRQ request = new SaveLogRQ(); - request.setLevel(level); - request.setLogTime(time); - request.setItemUuid(itemUuid); + SaveLogRQ request = new SaveLogRQ(); + request.setLevel(level); + request.setLogTime(time); + request.setItemUuid(itemUuid); - try { - if (MESSAGE_PARSER.supports(message)) { - ReportPortalMessage rpMessage = MESSAGE_PARSER.parse(message); - TypeAwareByteSource data = rpMessage.getData(); - SaveLogRQ.File file = new SaveLogRQ.File(); - file.setContent(data.read()); - file.setContentType(data.getMediaType()); - file.setName(UUID.randomUUID().toString()); + try { + if (MESSAGE_PARSER.supports(message)) { + ReportPortalMessage rpMessage = MESSAGE_PARSER.parse(message); + TypeAwareByteSource data = rpMessage.getData(); + SaveLogRQ.File file = new SaveLogRQ.File(); + file.setContent(data.read()); + file.setContentType(data.getMediaType()); + file.setName(UUID.randomUUID().toString()); - request.setFile(file); - request.setMessage(rpMessage.getMessage()); - } else { - request.setMessage(encoder.getLayout().doLayout(event)); - } - } catch (Exception e) { - //skip - } - return request; - }); - } + request.setFile(file); + request.setMessage(rpMessage.getMessage()); + } else { + request.setMessage(encoder.getLayout().doLayout(event)); + } + } catch (Exception e) { + //skip + } + return request; + }); + } - @Override - public void start() { - if (this.encoder == null) { - addError("No encoder set for the appender named [" + name + "]."); - return; - } - this.encoder.start(); - super.start(); - } + @Override + public void start() { + if (this.encoder == null) { + addError("No encoder set for the appender named [" + name + "]."); + return; + } + this.encoder.start(); + super.start(); + } - @SuppressWarnings("unused") - public PatternLayoutEncoder getEncoder() { - return encoder; - } + @SuppressWarnings("unused") + public PatternLayoutEncoder getEncoder() { + return encoder; + } - public void setEncoder(PatternLayoutEncoder encoder) { - this.encoder = encoder; - } + public void setEncoder(PatternLayoutEncoder encoder) { + this.encoder = encoder; + } } diff --git a/src/test/java/com/epam/reportportal/logback/appender/ReportPortalAppenderTest.java b/src/test/java/com/epam/reportportal/logback/appender/ReportPortalAppenderTest.java index d63e239..5f51e55 100644 --- a/src/test/java/com/epam/reportportal/logback/appender/ReportPortalAppenderTest.java +++ b/src/test/java/com/epam/reportportal/logback/appender/ReportPortalAppenderTest.java @@ -115,7 +115,7 @@ public void test_logger_append() { logger.info("test message"); launch.finishTestItem(id, new FinishTestItemRQ()); launch.finish(new FinishExecutionRQ()); - verify(client).log(any(List.class)); + verify(client, times(2)).log(any(List.class)); } @Test @@ -124,7 +124,7 @@ public void test_logger_skip() { Logger logger = createLoggerFor(LoggingSubscriber.class); launch.finishTestItem(id, new FinishTestItemRQ()); launch.finish(new FinishExecutionRQ()); - verify(client, timeout(100).times(0)).log(any(List.class)); + verify(client, timeout(100).times(1)).log(any(List.class)); } @Test @@ -143,9 +143,9 @@ public void test_binary_file_message_encoding() throws IOException { launch.finish(new FinishExecutionRQ()); ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); - verify(client).log(captor.capture()); + verify(client, times(2)).log(captor.capture()); - List request = captor.getValue(); + List request = captor.getAllValues().get(0); assertThat(request, hasSize(2)); RequestBody jsonPart = request.get(0).body();