├── integration-tests
├── src
│ ├── main
│ │ ├── resources
│ │ │ ├── application.properties
│ │ │ └── logback.xml
│ │ └── java
│ │ │ └── io
│ │ │ └── quarkiverse
│ │ │ └── logging
│ │ │ └── logback
│ │ │ └── it
│ │ │ └── LoggingLogbackResource.java
│ └── test
│ │ └── java
│ │ └── io
│ │ └── quarkiverse
│ │ └── logging
│ │ └── logback
│ │ └── it
│ │ ├── NativeLoggingLogbackResourceIT.java
│ │ └── LoggingLogbackResourceTest.java
└── pom.xml
├── docs
├── modules
│ └── ROOT
│ │ ├── nav.adoc
│ │ └── pages
│ │ ├── index.adoc
│ │ └── config.adoc
└── antora.yml
├── impl
├── deployment
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ └── quarkus-logback-version.txt
│ │ │ └── java
│ │ │ └── io
│ │ │ └── quarkiverse
│ │ │ └── logging
│ │ │ └── logback
│ │ │ └── deployment
│ │ │ └── LoggingLogbackProcessor.java
│ └── pom.xml
├── runtime
│ ├── src
│ │ └── main
│ │ │ ├── resources
│ │ │ └── META-INF
│ │ │ │ └── quarkus-extension.yaml
│ │ │ └── java
│ │ │ └── io
│ │ │ └── quarkiverse
│ │ │ └── logback
│ │ │ └── runtime
│ │ │ ├── DelayedStart.java
│ │ │ ├── events
│ │ │ ├── EndSub.java
│ │ │ ├── BodySub.java
│ │ │ ├── EventSub.java
│ │ │ ├── StartSub.java
│ │ │ ├── LocatorImpl.java
│ │ │ ├── EventSubstitution.java
│ │ │ └── AttributesImpl.java
│ │ │ ├── PackagingDataSubstitutions.java
│ │ │ ├── LoggingEventWrapper.java
│ │ │ └── LogbackRecorder.java
│ └── pom.xml
└── pom.xml
├── .github
├── project.yml
├── dependabot.yml
├── CODEOWNERS
└── workflows
│ ├── pre-release.yml
│ ├── build.yml
│ ├── quarkus-snapshot.yaml
│ └── release.yml
├── runtime
├── src
│ └── main
│ │ └── resources
│ │ └── META-INF
│ │ └── quarkus-extension.yaml
└── pom.xml
├── deployment
├── src
│ └── test
│ │ ├── resources
│ │ ├── quarkus-logback.xml
│ │ ├── quarkus-logback-expressions.xml
│ │ ├── quarkus-logback-build-system.xml
│ │ └── quarkus-logback-TimeBasedRollingPolicy.xml
│ │ └── java
│ │ └── io
│ │ └── quarkiverse
│ │ └── logging
│ │ └── logback
│ │ └── test
│ │ ├── LoggingLogbackTest.java
│ │ ├── LoggingLogbackTimeBasedRollingPolicyTest.java
│ │ ├── LoggingLogbackDevModeTest.java
│ │ ├── LoggingLogbackExpressionsDevModeTest.java
│ │ └── BuildSystemPropertiesTest.java
└── pom.xml
├── .gitignore
├── .all-contributorsrc
├── README.md
├── pom.xml
└── LICENSE
/integration-tests/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/nav.adoc:
--------------------------------------------------------------------------------
1 | * xref:index.adoc[Quarkus - Logging Logback]
2 |
--------------------------------------------------------------------------------
/impl/deployment/src/main/resources/quarkus-logback-version.txt:
--------------------------------------------------------------------------------
1 | ${logback.version}
--------------------------------------------------------------------------------
/.github/project.yml:
--------------------------------------------------------------------------------
1 | release:
2 | current-version: 1.1.2
3 | next-version: 999-SNAPSHOT
4 |
5 |
--------------------------------------------------------------------------------
/docs/antora.yml:
--------------------------------------------------------------------------------
1 | name: quarkus-logging-logback
2 | title: Logging Logback
3 | version: dev
4 | nav:
5 | - modules/ROOT/nav.adoc
6 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/resources/META-INF/quarkus-extension.yaml:
--------------------------------------------------------------------------------
1 | artifact: ${project.groupId}:${project.artifactId}:${project.version}
2 | name: Logging Logback Implementation
3 | metadata:
4 | unlisted: true
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/DelayedStart.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime;
2 |
3 | public interface DelayedStart {
4 |
5 | void doQuarkusDelayedStart();
6 | }
7 |
--------------------------------------------------------------------------------
/integration-tests/src/test/java/io/quarkiverse/logging/logback/it/NativeLoggingLogbackResourceIT.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.it;
2 |
3 | import io.quarkus.test.junit.QuarkusIntegrationTest;
4 |
5 | @QuarkusIntegrationTest
6 | public class NativeLoggingLogbackResourceIT extends LoggingLogbackResourceTest {
7 | }
8 |
--------------------------------------------------------------------------------
/runtime/src/main/resources/META-INF/quarkus-extension.yaml:
--------------------------------------------------------------------------------
1 | artifact: ${project.groupId}:${project.artifactId}:${project.version}
2 | name: Logging Logback
3 | description: logback.xml support for logback appenders
4 | metadata:
5 | keywords:
6 | - logging
7 | - logback
8 | categories:
9 | - "logging"
10 | status: "experimental"
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/EndSub.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import org.xml.sax.Locator;
4 |
5 | public class EndSub extends EventSub {
6 | public EndSub(String namespaceURI, String localName, String qName, Locator locator) {
7 | super(namespaceURI, localName, qName, locator);
8 | }
9 |
10 | public EndSub() {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/deployment/src/test/resources/quarkus-logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | target/tests.log
5 | false
6 |
7 | LOGBACK- %-5level %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/integration-tests/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | target/tests.log
5 | false
6 |
7 | LOGBACK- %-5level %msg %n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/deployment/src/test/resources/quarkus-logback-expressions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | target/tests.log
5 | false
6 |
7 | ${lg.pre}- %-5level %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/deployment/src/test/resources/quarkus-logback-build-system.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | target/tests.log
5 | false
6 |
7 | ${project.version}- %-5level %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/BodySub.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import org.xml.sax.Locator;
4 |
5 | public class BodySub extends EventSub {
6 | public String text;
7 |
8 | public BodySub(String namespaceURI, String localName, String qName, Locator locator, String text) {
9 | super(namespaceURI, localName, qName, locator);
10 | this.text = text;
11 | }
12 |
13 | public BodySub() {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "maven"
9 | directory: "/"
10 | schedule:
11 | interval: "daily"
12 | ignore:
13 | - dependency-name: "org.apache.maven.plugins:maven-compiler-plugin"
14 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/PackagingDataSubstitutions.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime;
2 |
3 | import com.oracle.svm.core.annotate.Substitute;
4 | import com.oracle.svm.core.annotate.TargetClass;
5 |
6 | import ch.qos.logback.classic.spi.PackagingDataCalculator;
7 | import ch.qos.logback.classic.spi.StackTraceElementProxy;
8 |
9 | @TargetClass(PackagingDataCalculator.class)
10 | public final class PackagingDataSubstitutions {
11 |
12 | @Substitute
13 | void populateFrames(StackTraceElementProxy[] stepArray) {
14 |
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/EventSub.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import org.xml.sax.Locator;
4 |
5 | public class EventSub {
6 |
7 | public String namespaceURI;
8 | public String localName;
9 | public String qName;
10 | public Locator locator;
11 |
12 | public EventSub(String namespaceURI, String localName, String qName, Locator locator) {
13 | this.namespaceURI = namespaceURI;
14 | this.localName = localName;
15 | this.qName = qName;
16 | this.locator = locator;
17 | }
18 |
19 | public EventSub() {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Lines starting with '#' are comments.
2 | # Each line is a file pattern followed by one or more owners.
3 |
4 | # More details are here: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
5 |
6 | # The '*' pattern is global owners.
7 |
8 | # Order is important. The last matching pattern has the most precedence.
9 | # The folders are ordered as follows:
10 |
11 | # In each subsection folders are ordered first by depth, then alphabetically.
12 | # This should make it easy to add new rules without breaking existing ones.
13 |
14 | * @quarkiverse/quarkiverse-logging-logback
15 |
--------------------------------------------------------------------------------
/.github/workflows/pre-release.yml:
--------------------------------------------------------------------------------
1 | name: Quarkiverse Pre Release
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - '.github/project.yml'
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | name: pre release
12 |
13 | steps:
14 | - uses: radcortez/project-metadata-action@master
15 | name: retrieve project metadata
16 | id: metadata
17 | with:
18 | github-token: ${{secrets.GITHUB_TOKEN}}
19 | metadata-file-path: '.github/project.yml'
20 |
21 | - name: Validate version
22 | if: contains(steps.metadata.outputs.current-version, 'SNAPSHOT')
23 | run: |
24 | echo '::error::Cannot release a SNAPSHOT version.'
25 | exit 1
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/StartSub.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import org.xml.sax.Locator;
7 |
8 | public class StartSub extends EventSub {
9 | public ArrayList partList;
10 | public AttributesImpl attributes;
11 |
12 | public StartSub(String namespaceURI, String localName, String qName, Locator locator, AttributesImpl attributes,
13 | List partList) {
14 | super(namespaceURI, localName, qName, locator);
15 | this.attributes = attributes;
16 | this.partList = new ArrayList<>(partList);
17 | }
18 |
19 | public StartSub() {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/impl/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback-impl-parent
10 | 999-SNAPSHOT
11 | pom
12 | Quarkus - Logging Logback Impl- Parent
13 |
14 | deployment
15 | runtime
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
25 | # Eclipse
26 | .project
27 | .classpath
28 | .settings/
29 | bin/
30 |
31 | # IntelliJ
32 | .idea
33 | *.ipr
34 | *.iml
35 | *.iws
36 |
37 | # NetBeans
38 | nb-configuration.xml
39 |
40 | # Visual Studio Code
41 | .vscode
42 | .factorypath
43 |
44 | # OSX
45 | .DS_Store
46 |
47 | # Vim
48 | *.swp
49 | *.swo
50 |
51 | # patch
52 | *.orig
53 | *.rej
54 |
55 | # Gradle
56 | .gradle/
57 | build/
58 |
59 | # Maven
60 | target/
61 | pom.xml.tag
62 | pom.xml.releaseBackup
63 | pom.xml.versionsBackup
64 | release.properties
--------------------------------------------------------------------------------
/.all-contributorsrc:
--------------------------------------------------------------------------------
1 | {
2 | "files": [
3 | "README.md"
4 | ],
5 | "imageSize": 100,
6 | "commit": false,
7 | "contributors": [
8 | {
9 | "login": "stuartwdouglas",
10 | "name": "Stuart Douglas",
11 | "avatar_url": "https://avatars.githubusercontent.com/u/328571?v=4",
12 | "profile": "https://github.com/stuartwdouglas",
13 | "contributions": [
14 | "code",
15 | "maintenance"
16 | ]
17 | },
18 | {
19 | "login": "gsmet",
20 | "name": "Guillaume Smet",
21 | "avatar_url": "https://avatars.githubusercontent.com/u/1279749?v=4",
22 | "profile": "https://lesincroyableslivres.fr/",
23 | "contributions": [
24 | "code"
25 | ]
26 | }
27 | ],
28 | "contributorsPerLine": 7,
29 | "projectName": "quarkus-logging-logback",
30 | "projectOwner": "quarkiverse",
31 | "repoType": "github",
32 | "repoHost": "https://github.com",
33 | "skipCi": true,
34 | "commitConvention": "angular"
35 | }
36 |
--------------------------------------------------------------------------------
/integration-tests/src/test/java/io/quarkiverse/logging/logback/it/LoggingLogbackResourceTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.it;
2 |
3 | import static io.restassured.RestAssured.given;
4 | import static org.hamcrest.Matchers.is;
5 |
6 | import java.io.IOException;
7 | import java.nio.file.Files;
8 | import java.nio.file.Paths;
9 |
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.Test;
12 |
13 | import io.quarkus.test.junit.QuarkusTest;
14 |
15 | @QuarkusTest
16 | public class LoggingLogbackResourceTest {
17 |
18 | @Test
19 | public void testHelloEndpoint() throws IOException {
20 | //test that the app is working properly
21 | given()
22 | .when().get("/logging-logback")
23 | .then()
24 | .statusCode(200)
25 | .body(is("Hello logging-logback"));
26 |
27 | //now test that logging config has been picked up
28 | String line = Files.readAllLines(Paths.get("target/tests.log")).get(0);
29 | Assertions.assertTrue(line.startsWith("LOGBACK"));
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/src/test/java/io/quarkiverse/logging/logback/test/LoggingLogbackTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.test;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Paths;
6 | import java.util.List;
7 |
8 | import org.jboss.shrinkwrap.api.ShrinkWrap;
9 | import org.jboss.shrinkwrap.api.spec.JavaArchive;
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.Test;
12 | import org.junit.jupiter.api.extension.RegisterExtension;
13 |
14 | import io.quarkus.test.QuarkusUnitTest;
15 |
16 | public class LoggingLogbackTest {
17 |
18 | // Start unit test with your extension loaded
19 | @RegisterExtension
20 | static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
21 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
22 | .addAsResource("quarkus-logback.xml", "logback.xml"));
23 |
24 | @Test
25 | public void testLogFile() throws IOException {
26 | List strings = Files.readAllLines(Paths.get("target/tests.log"));
27 | Assertions.assertFalse(strings.isEmpty());
28 | for (String line : strings) {
29 | Assertions.assertTrue(line.startsWith("LOGBACK"));
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 | paths-ignore:
8 | - '.gitignore'
9 | - 'CODEOWNERS'
10 | - 'LICENSE'
11 | - '*.md'
12 | - '*.adoc'
13 | - '*.txt'
14 | - '.all-contributorsrc'
15 | pull_request:
16 | paths-ignore:
17 | - '.gitignore'
18 | - 'CODEOWNERS'
19 | - 'LICENSE'
20 | - '*.md'
21 | - '*.adoc'
22 | - '*.txt'
23 | - '.all-contributorsrc'
24 |
25 | jobs:
26 | build:
27 |
28 | runs-on: ubuntu-latest
29 |
30 | steps:
31 | - uses: actions/checkout@v2
32 |
33 | - name: Set up JDK 17
34 | uses: actions/setup-java@v1
35 | with:
36 | java-version: 17
37 |
38 | - name: Get Date
39 | id: get-date
40 | run: |
41 | echo "::set-output name=date::$(/bin/date -u "+%Y-%m")"
42 | shell: bash
43 | - name: Cache Maven Repository
44 | id: cache-maven
45 | uses: actions/cache@v2
46 | with:
47 | path: ~/.m2/repository
48 | # refresh cache every month to avoid unlimited growth
49 | key: maven-repo-${{ runner.os }}-${{ steps.get-date.outputs.date }}
50 |
51 | - name: Build with Maven
52 | run: mvn -B formatter:validate verify --file pom.xml
53 |
54 |
--------------------------------------------------------------------------------
/integration-tests/src/main/java/io/quarkiverse/logging/logback/it/LoggingLogbackResource.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 | package io.quarkiverse.logging.logback.it;
18 |
19 | import jakarta.enterprise.context.ApplicationScoped;
20 | import jakarta.ws.rs.GET;
21 | import jakarta.ws.rs.Path;
22 |
23 | @Path("/logging-logback")
24 | @ApplicationScoped
25 | public class LoggingLogbackResource {
26 |
27 | @GET
28 | public String hello() {
29 | return "Hello logging-logback";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/src/test/java/io/quarkiverse/logging/logback/test/LoggingLogbackTimeBasedRollingPolicyTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.test;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Paths;
6 | import java.util.List;
7 |
8 | import org.jboss.shrinkwrap.api.ShrinkWrap;
9 | import org.jboss.shrinkwrap.api.spec.JavaArchive;
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.Test;
12 | import org.junit.jupiter.api.extension.RegisterExtension;
13 |
14 | import io.quarkus.test.QuarkusUnitTest;
15 |
16 | public class LoggingLogbackTimeBasedRollingPolicyTest {
17 |
18 | @RegisterExtension
19 | static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
20 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
21 | .addAsResource("quarkus-logback-TimeBasedRollingPolicy.xml", "logback.xml"));
22 |
23 | @Test
24 | public void testLogFile() throws IOException {
25 | List strings = Files.readAllLines(Paths.get("target/tests-TimeBasedRollingPolicy.log"));
26 | Assertions.assertFalse(strings.isEmpty());
27 | for (String line : strings) {
28 | Assertions.assertTrue(line.startsWith("LOGBACK TimeBasedRollingPolicy"));
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/deployment/src/test/resources/quarkus-logback-TimeBasedRollingPolicy.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [%d{dd-MM-yyyy HH:mm:ss:SSS}] [%.20thread] [%level] [%logger{0}] %msg%n %xException{short}
5 |
6 |
7 |
8 |
9 | target/tests-TimeBasedRollingPolicy.log
10 |
11 | target/tests-TimeBasedRollingPolicy-%d{yyyy-MM-dd}.%i.log
12 |
13 |
14 | 10MB
15 |
16 |
17 | 5
18 |
19 |
20 | TRACE
21 |
22 |
23 | LOGBACK TimeBasedRollingPolicy [%level] [%d{dd-MM-yyyy HH:mm:ss:SSS}] %msg%n
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/LocatorImpl.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import org.xml.sax.Locator;
4 |
5 | import io.quarkus.runtime.annotations.RecordableConstructor;
6 |
7 | public class LocatorImpl implements Locator {
8 |
9 | public final String publicId;
10 | public final String systemId;
11 | public final int lineNumber;
12 | public final int columnNumber;
13 |
14 | @RecordableConstructor
15 | public LocatorImpl(String publicId, String systemId, int lineNumber, int columnNumber) {
16 | this.publicId = publicId;
17 | this.systemId = systemId;
18 | this.lineNumber = lineNumber;
19 | this.columnNumber = columnNumber;
20 | }
21 |
22 | public LocatorImpl(Locator locator) {
23 | this.publicId = locator.getPublicId();
24 | this.systemId = locator.getSystemId();
25 | this.lineNumber = locator.getLineNumber();
26 | this.columnNumber = locator.getColumnNumber();
27 | }
28 |
29 | @Override
30 | public String getPublicId() {
31 | return publicId;
32 | }
33 |
34 | @Override
35 | public String getSystemId() {
36 | return systemId;
37 | }
38 |
39 | @Override
40 | public int getLineNumber() {
41 | return lineNumber;
42 | }
43 |
44 | @Override
45 | public int getColumnNumber() {
46 | return columnNumber;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/.github/workflows/quarkus-snapshot.yaml:
--------------------------------------------------------------------------------
1 | name: "Quarkus ecosystem CI"
2 | on:
3 | workflow_dispatch:
4 | watch:
5 | types: [started]
6 |
7 | # For this CI to work, ECOSYSTEM_CI_TOKEN needs to contain a GitHub with rights to close the Quarkus issue that the user/bot has opened,
8 | # while 'ECOSYSTEM_CI_REPO_PATH' needs to be set to the corresponding path in the 'quarkusio/quarkus-ecosystem-ci' repository
9 |
10 | env:
11 | ECOSYSTEM_CI_REPO: quarkusio/quarkus-ecosystem-ci
12 | ECOSYSTEM_CI_REPO_FILE: context.yaml
13 | JAVA_VERSION: 17
14 |
15 | #########################
16 | # Repo specific setting #
17 | #########################
18 |
19 | ECOSYSTEM_CI_REPO_PATH: quarkiverse-logging-logback
20 |
21 | jobs:
22 | build:
23 | name: "Build against latest Quarkus snapshot"
24 | runs-on: ubuntu-latest
25 | # Allow to manually launch the ecosystem CI in addition to the bots
26 | if: github.actor == 'quarkusbot' || github.actor == 'quarkiversebot' || github.actor == ''
27 |
28 | steps:
29 | - name: Set up Java
30 | uses: actions/setup-java@v1
31 | with:
32 | java-version: ${{ env.JAVA_VERSION }}
33 |
34 | - name: Checkout repo
35 | uses: actions/checkout@v2
36 | with:
37 | path: current-repo
38 |
39 | - name: Checkout Ecosystem
40 | uses: actions/checkout@v2
41 | with:
42 | repository: ${{ env.ECOSYSTEM_CI_REPO }}
43 | path: ecosystem-ci
44 |
45 | - name: Setup and Run Tests
46 | run: ./ecosystem-ci/setup-and-test
47 | env:
48 | ECOSYSTEM_CI_TOKEN: ${{ secrets.ECOSYSTEM_CI_TOKEN }}
49 |
--------------------------------------------------------------------------------
/deployment/src/test/java/io/quarkiverse/logging/logback/test/LoggingLogbackDevModeTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.test;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Paths;
6 |
7 | import org.jboss.shrinkwrap.api.ShrinkWrap;
8 | import org.jboss.shrinkwrap.api.spec.JavaArchive;
9 | import org.junit.jupiter.api.Assertions;
10 | import org.junit.jupiter.api.Test;
11 | import org.junit.jupiter.api.extension.RegisterExtension;
12 |
13 | import io.quarkus.test.QuarkusDevModeTest;
14 | import io.restassured.RestAssured;
15 |
16 | public class LoggingLogbackDevModeTest {
17 |
18 | // Start hot reload (DevMode) test with your extension loaded
19 | @RegisterExtension
20 | static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest()
21 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
22 | .addAsResource("quarkus-logback.xml", "logback.xml"));
23 |
24 | @Test
25 | public void testLogbackXmlChanges() throws IOException {
26 | for (String line : Files.readAllLines(Paths.get("target/tests.log"))) {
27 | Assertions.assertTrue(line.startsWith("LOGBACK"));
28 | }
29 | devModeTest.modifyResourceFile("logback.xml", s -> s.replaceAll("LOGBACK", "MODIFIED"));
30 | RestAssured.get(); //trigger hot reload
31 | boolean foundMod = false;
32 | for (String line : Files.readAllLines(Paths.get("target/tests.log"))) {
33 | if (line.startsWith("MODIFIED")) {
34 | foundMod = true;
35 | break;
36 | }
37 | }
38 | Assertions.assertTrue(foundMod, "Log changes did not take effect");
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/deployment/src/test/java/io/quarkiverse/logging/logback/test/LoggingLogbackExpressionsDevModeTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.test;
2 |
3 | import java.io.IOException;
4 | import java.nio.file.Files;
5 | import java.nio.file.Paths;
6 |
7 | import org.jboss.shrinkwrap.api.ShrinkWrap;
8 | import org.jboss.shrinkwrap.api.asset.StringAsset;
9 | import org.jboss.shrinkwrap.api.spec.JavaArchive;
10 | import org.junit.jupiter.api.Assertions;
11 | import org.junit.jupiter.api.Test;
12 | import org.junit.jupiter.api.extension.RegisterExtension;
13 |
14 | import io.quarkus.test.QuarkusDevModeTest;
15 | import io.restassured.RestAssured;
16 |
17 | public class LoggingLogbackExpressionsDevModeTest {
18 |
19 | // Start hot reload (DevMode) test with your extension loaded
20 | @RegisterExtension
21 | static final QuarkusDevModeTest devModeTest = new QuarkusDevModeTest()
22 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
23 | .addAsResource(new StringAsset("lg.pre=EXP"), "application.properties")
24 | .addAsResource("quarkus-logback-expressions.xml", "logback.xml"));
25 |
26 | @Test
27 | public void testLogbackXmlChanges() throws IOException {
28 | for (String line : Files.readAllLines(Paths.get("target/tests.log"))) {
29 | Assertions.assertTrue(line.startsWith("EXP"));
30 | }
31 | devModeTest.modifyResourceFile("application.properties", s -> s.replaceAll("EXP", "MODIFIED"));
32 | RestAssured.get(); //trigger hot reload
33 | boolean foundMod = false;
34 | for (String line : Files.readAllLines(Paths.get("target/tests.log"))) {
35 | if (line.startsWith("MODIFIED")) {
36 | foundMod = true;
37 | break;
38 | }
39 | }
40 | Assertions.assertTrue(foundMod, "Log changes did not take effect");
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/impl/deployment/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-impl-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback-impl-deployment
10 | Quarkus - Logging Logback Impl - Deployment
11 | The deployment module of the implementation part of the Logback extension
12 |
13 |
14 | io.quarkus
15 | quarkus-arc-deployment
16 |
17 |
18 | io.smallrye.common
19 | smallrye-common-version
20 |
21 |
22 | io.quarkiverse.logging.logback
23 | quarkus-logging-logback-impl
24 |
25 |
26 | io.smallrye.common
27 | smallrye-common-version
28 |
29 |
30 |
31 |
32 |
33 | src/main/resources
34 | true
35 |
36 |
37 |
38 |
39 | maven-compiler-plugin
40 |
41 |
42 |
43 | io.quarkus
44 | quarkus-extension-processor
45 | ${quarkus.version}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Quarkus - Logging Logback
2 |
3 | [](#contributors-)
4 |
5 |
6 | [](https://search.maven.org/artifact/io.quarkiverse.logging.logback/quarkus-logging-logback)
7 |
8 | This extension allows you to use `logback.xml` to configure logback appenders and control log levels.
9 |
10 | Documentation is at https://quarkiverse.github.io/quarkiverse-docs/quarkus-logging-logback/dev/index.html
11 | ## Contributors ✨
12 |
13 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
14 |
15 |
16 |
17 |
18 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
33 |
--------------------------------------------------------------------------------
/deployment/src/test/java/io/quarkiverse/logging/logback/test/BuildSystemPropertiesTest.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.test;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.file.Files;
6 | import java.nio.file.Paths;
7 | import java.util.List;
8 | import java.util.regex.Matcher;
9 | import java.util.regex.Pattern;
10 |
11 | import org.jboss.shrinkwrap.api.ShrinkWrap;
12 | import org.jboss.shrinkwrap.api.spec.JavaArchive;
13 | import org.junit.jupiter.api.Assertions;
14 | import org.junit.jupiter.api.Test;
15 | import org.junit.jupiter.api.extension.RegisterExtension;
16 |
17 | import io.quarkus.test.QuarkusUnitTest;
18 |
19 | public class BuildSystemPropertiesTest {
20 |
21 | // Start unit test with your extension loaded
22 | @RegisterExtension
23 | static final QuarkusUnitTest unitTest = new QuarkusUnitTest()
24 | .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
25 | .addAsResource("quarkus-logback-build-system.xml", "logback.xml"));
26 |
27 | @Test
28 | public void testProjectVersion() throws IOException {
29 | //bit of a hack, grab the current version from the pom
30 | File f = new File(".");
31 | String version = null;
32 | for (;;) {
33 | File pom = new File(f, "pom.xml");
34 | if (pom.exists()) {
35 | Matcher m = Pattern.compile("(.*?)").matcher(Files.readString(pom.toPath()));
36 | if (!m.find()) {
37 | throw new RuntimeException("Could not resolve project version");
38 | }
39 | version = m.group(1);
40 | break;
41 | }
42 | f = f.getParentFile();
43 | if (f == null) {
44 | throw new RuntimeException("Could not resolve project pom");
45 | }
46 | }
47 | List strings = Files.readAllLines(Paths.get("target/tests.log"));
48 | Assertions.assertFalse(strings.isEmpty());
49 | for (String line : strings) {
50 | Assertions.assertTrue(line.startsWith(version));
51 | }
52 |
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/runtime/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback
10 | Quarkus - Logging Logback - Runtime
11 | Logback support for Quarkus
12 |
13 |
14 | io.quarkus
15 | quarkus-core
16 |
17 |
18 | io.quarkiverse.logging.logback
19 | quarkus-logging-logback-impl
20 | true
21 |
22 |
23 |
24 |
25 |
26 | io.quarkus
27 | quarkus-extension-maven-plugin
28 | ${quarkus.version}
29 |
30 |
31 | compile
32 |
33 | extension-descriptor
34 |
35 |
36 | ${project.groupId}:${project.artifactId}-deployment:${project.version}
37 |
38 |
39 |
40 |
41 |
42 | maven-compiler-plugin
43 |
44 |
45 |
46 | io.quarkus
47 | quarkus-extension-processor
48 | ${quarkus.version}
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/deployment/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback-deployment
10 | Quarkus - Logging Logback - Deployment
11 | Logback support for Quarkus, deployment module
12 |
13 |
14 | io.quarkiverse.logging.logback
15 | quarkus-logging-logback
16 |
17 |
18 | io.quarkus
19 | quarkus-core-deployment
20 |
21 |
22 | io.quarkiverse.logging.logback
23 | quarkus-logging-logback-impl-deployment
24 | true
25 |
26 |
27 | io.quarkus
28 | quarkus-junit5-internal
29 | test
30 |
31 |
32 | io.quarkus
33 | quarkus-vertx-http-deployment
34 | test
35 |
36 |
37 | io.rest-assured
38 | rest-assured
39 | test
40 |
41 |
42 |
43 |
44 |
45 | maven-compiler-plugin
46 |
47 |
48 |
49 | io.quarkus
50 | quarkus-extension-processor
51 | ${quarkus.version}
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/impl/runtime/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-impl-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback-impl
10 | Quarkus - Logging Logback Impl - Runtime
11 | The implementation part of the Logback extension
12 |
13 |
14 | io.quarkus
15 | quarkus-arc
16 |
17 |
18 | org.graalvm.sdk
19 | graal-sdk
20 | provided
21 |
22 |
23 | ch.qos.logback
24 | logback-classic
25 |
26 |
27 |
28 |
29 |
30 | io.quarkus
31 | quarkus-extension-maven-plugin
32 | ${quarkus.version}
33 |
34 |
35 | compile
36 |
37 | extension-descriptor
38 |
39 |
40 | ${project.groupId}:${project.artifactId}-deployment:${project.version}
41 |
42 | io.quarkus:quarkus-core
43 |
44 |
45 |
46 |
47 |
48 |
49 | maven-compiler-plugin
50 |
51 |
52 |
53 | io.quarkus
54 | quarkus-extension-processor
55 | ${quarkus.version}
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/index.adoc:
--------------------------------------------------------------------------------
1 | = Quarkus - Logging Logback
2 | :extension-status: preview
3 |
4 | This extension allows you to use Logback to configure Quarkus logging via `logback.xml`. This extension parses the config
5 | file at build time, and then bridges the resulting config with jboss-logmanager.
6 |
7 | == Installation
8 |
9 | If you want to use this extension, you need to add the `io.quarkiverse.logging.logback:quarkus-logging-logback` extension first.
10 | In your `pom.xml` file, add:
11 |
12 | [source,xml]
13 | ----
14 |
15 | io.quarkiverse.logging.logback
16 | quarkus-logging-logback
17 |
18 | ----
19 |
20 | == Technical Details
21 |
22 | This extension does not actually replace jboss-logmanager as the default logging engine, instead it bridges to the two,
23 | to allow them to co-exist. JBoss Logging is still the core logmanager for Quarkus, however any logging events it receives
24 | are also sent to the logback and will be sent through any logback appenders.
25 |
26 | Log levels for loggers are also parsed, and are used to set the default log level for the corresponding jboss-logmanager
27 | loggers. These defaults can still be overriden however, so if you set a log level in `application.properties` it will take
28 | precidence.
29 |
30 | If a console appender is detected in `logback.xml` then the standard Quarkus console logging is disabled, to prevent double
31 | logging of messages, otherwise it is left enabled and will work as normal.
32 |
33 | == Common Problems
34 |
35 | === Duplicate SLF4J bindings
36 |
37 | You may see an error similar to below:
38 |
39 | ```
40 | SLF4J: Class path contains multiple SLF4J bindings.
41 | SLF4J: Found binding in [jar:file:/home/stuart/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
42 | SLF4J: Found binding in [jar:file:/home/stuart/.m2/repository/org/jboss/slf4j/slf4j-jboss-logmanager/1.1.0.Final/slf4j-jboss-logmanager-1.1.0.Final.jar!/org/slf4j/impl/StaticLoggerBinder.class]
43 | SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
44 | SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
45 |
46 | ```
47 |
48 | To get around this you need to exclude the `ch.qos.logback:logback-classic` artifact from your project. Quarkus will automatically
49 | bring in a modified version at runtime that has had the SLF4J binding removed. Run `mvn dependency:tree` to figure out what is
50 | bringing in the artifact, and then exclude it from the `pom.xml`.
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Quarkiverse Release
2 |
3 | on:
4 | pull_request:
5 | types: [closed]
6 | paths:
7 | - '.github/project.yml'
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref }}
11 | cancel-in-progress: true
12 |
13 | defaults:
14 | run:
15 | shell: bash
16 |
17 | jobs:
18 | release:
19 | runs-on: ubuntu-latest
20 | name: release
21 | if: ${{github.event.pull_request.merged == true}}
22 |
23 | steps:
24 | - uses: radcortez/project-metadata-action@main
25 | name: Retrieve project metadata
26 | id: metadata
27 | with:
28 | github-token: ${{secrets.GITHUB_TOKEN}}
29 | metadata-file-path: '.github/project.yml'
30 |
31 | - uses: actions/checkout@v4
32 |
33 | - name: Set up JDK 11
34 | uses: actions/setup-java@v4
35 | with:
36 | distribution: temurin
37 | java-version: 17
38 | cache: 'maven'
39 | server-id: ossrh
40 | server-username: MAVEN_USERNAME
41 | server-password: MAVEN_PASSWORD
42 | gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
43 | gpg-passphrase: MAVEN_GPG_PASSPHRASE
44 |
45 | - name: Configure Git author
46 | run: |
47 | git config --local user.email "action@github.com"
48 | git config --local user.name "GitHub Action"
49 |
50 | - name: Update latest release version in docs
51 | run: |
52 | mvn -B -ntp -pl docs -am generate-resources -Denforcer.skip -Dformatter.skip -Dimpsort.skip
53 | if ! git diff --quiet docs/modules/ROOT/pages/includes/attributes.adoc; then
54 | git add docs/modules/ROOT/pages/includes/attributes.adoc
55 | git commit -m "Update the latest release version ${{steps.metadata.outputs.current-version}} in documentation"
56 | fi
57 |
58 | - name: Maven release ${{steps.metadata.outputs.current-version}}
59 | run: |
60 | mvn -B release:prepare -Prelease -DreleaseVersion=${{steps.metadata.outputs.current-version}} -DdevelopmentVersion=${{steps.metadata.outputs.next-version}}
61 | mvn -B release:perform -Darguments=-DperformRelease -DperformRelease -Prelease
62 | env:
63 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
64 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
65 | MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
66 |
67 | - name: Push changes to ${{github.base_ref}} branch
68 | run: |
69 | git push
70 | git push origin ${{steps.metadata.outputs.current-version}}
71 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/EventSubstitution.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import java.lang.reflect.Constructor;
4 |
5 | import org.xml.sax.Attributes;
6 | import org.xml.sax.Locator;
7 |
8 | import ch.qos.logback.core.joran.event.BodyEvent;
9 | import ch.qos.logback.core.joran.event.EndEvent;
10 | import ch.qos.logback.core.joran.event.SaxEvent;
11 | import ch.qos.logback.core.joran.event.StartEvent;
12 | import ch.qos.logback.core.joran.spi.ElementPath;
13 | import io.quarkus.runtime.ObjectSubstitution;
14 |
15 | public class EventSubstitution implements ObjectSubstitution {
16 |
17 | static final Constructor START;
18 | static final Constructor BODY;
19 | static final Constructor END;
20 |
21 | static {
22 | try {
23 | START = StartEvent.class.getDeclaredConstructor(ElementPath.class, String.class, String.class, String.class,
24 | Attributes.class, Locator.class);
25 | BODY = BodyEvent.class.getDeclaredConstructor(String.class, Locator.class);
26 | END = EndEvent.class.getDeclaredConstructor(String.class, String.class, String.class, Locator.class);
27 | START.setAccessible(true);
28 | BODY.setAccessible(true);
29 | END.setAccessible(true);
30 | } catch (NoSuchMethodException e) {
31 | throw new RuntimeException(e);
32 | }
33 | }
34 |
35 | @Override
36 | public EventSub serialize(SaxEvent obj) {
37 | if (obj instanceof StartEvent) {
38 | StartEvent s = (StartEvent) obj;
39 | return new StartSub(obj.namespaceURI, obj.localName, obj.qName, new LocatorImpl(obj.locator),
40 | new AttributesImpl(s.attributes), s.elementPath.getCopyOfPartList());
41 | } else if (obj instanceof BodyEvent) {
42 | return new BodySub(obj.namespaceURI, obj.localName, obj.qName, new LocatorImpl(obj.locator),
43 | ((BodyEvent) obj).getText());
44 | } else if (obj instanceof EndEvent) {
45 | return new EndSub(obj.namespaceURI, obj.localName, obj.qName, new LocatorImpl(obj.locator));
46 | }
47 | throw new RuntimeException("Unknown event type");
48 | }
49 |
50 | @Override
51 | public SaxEvent deserialize(EventSub obj) {
52 | try {
53 | if (obj instanceof StartSub) {
54 | StartSub s = (StartSub) obj;
55 | return START.newInstance(new ElementPath(s.partList), s.namespaceURI, s.localName, s.qName, s.attributes,
56 | s.locator);
57 | } else if (obj instanceof BodySub) {
58 | return BODY.newInstance(((BodySub) obj).text, obj.locator);
59 | } else if (obj instanceof EndSub) {
60 | EndSub e = (EndSub) obj;
61 | return END.newInstance(e.namespaceURI, e.localName, e.qName, e.locator);
62 | }
63 | throw new RuntimeException("Unknown event type");
64 | } catch (Exception e) {
65 | throw new RuntimeException(e);
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/integration-tests/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse.logging.logback
6 | quarkus-logging-logback-parent
7 | 999-SNAPSHOT
8 |
9 | quarkus-logging-logback-integration-tests
10 | Quarkus - Logging Logback - Integration Tests
11 |
12 |
13 | io.quarkus
14 | quarkus-resteasy
15 |
16 |
17 | io.quarkiverse.logging.logback
18 | quarkus-logging-logback
19 | ${project.version}
20 |
21 |
22 | io.quarkus
23 | quarkus-junit5
24 | test
25 |
26 |
27 | io.rest-assured
28 | rest-assured
29 | test
30 |
31 |
32 |
33 |
34 |
35 | io.quarkus
36 | quarkus-maven-plugin
37 |
38 |
39 |
40 | build
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | native-image
50 |
51 |
52 | native
53 |
54 |
55 |
56 |
57 |
58 | maven-surefire-plugin
59 |
60 | ${native.surefire.skip}
61 |
62 |
63 |
64 | maven-failsafe-plugin
65 |
66 |
67 |
68 | integration-test
69 | verify
70 |
71 |
72 |
73 | ${project.build.directory}/${project.build.finalName}-runner
74 | org.jboss.logmanager.LogManager
75 | ${maven.home}
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | native
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/events/AttributesImpl.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime.events;
2 |
3 | import java.util.Objects;
4 |
5 | import org.xml.sax.Attributes;
6 |
7 | import io.quarkus.runtime.annotations.RecordableConstructor;
8 |
9 | public class AttributesImpl implements Attributes {
10 |
11 | public Attribute[] attributes;
12 |
13 | public AttributesImpl(Attributes at) {
14 | attributes = new Attribute[at.getLength()];
15 | for (int i = 0; i < at.getLength(); ++i) {
16 | attributes[i] = new Attribute(at.getLocalName(i), at.getValue(i), at.getType(i), at.getURI(i), at.getQName(i));
17 | }
18 | }
19 |
20 | public AttributesImpl() {
21 |
22 | }
23 |
24 | @Override
25 | public int getLength() {
26 | return attributes.length;
27 | }
28 |
29 | @Override
30 | public String getURI(int index) {
31 | return attributes[index].uri;
32 | }
33 |
34 | @Override
35 | public String getLocalName(int index) {
36 | return attributes[index].localName;
37 | }
38 |
39 | @Override
40 | public String getQName(int index) {
41 | return attributes[index].qName;
42 | }
43 |
44 | @Override
45 | public String getType(int index) {
46 | return attributes[index].type;
47 | }
48 |
49 | @Override
50 | public String getValue(int index) {
51 | return attributes[index].value;
52 | }
53 |
54 | @Override
55 | public int getIndex(String uri, String localName) {
56 | for (int i = 0; i < attributes.length; ++i) {
57 | Attribute at = attributes[i];
58 | if (Objects.equals(uri, at.uri) && Objects.equals(localName, at.localName)) {
59 | return i;
60 | }
61 | }
62 | return -1;
63 | }
64 |
65 | @Override
66 | public int getIndex(String qName) {
67 | for (int i = 0; i < attributes.length; ++i) {
68 | Attribute at = attributes[i];
69 | if (Objects.equals(qName, at.qName)) {
70 | return i;
71 | }
72 | }
73 | return -1;
74 | }
75 |
76 | @Override
77 | public String getType(String uri, String localName) {
78 | int index = getIndex(uri, localName);
79 | if (index == -1) {
80 | return null;
81 | }
82 | return attributes[index].type;
83 | }
84 |
85 | @Override
86 | public String getType(String qName) {
87 | int index = getIndex(qName);
88 | if (index == -1) {
89 | return null;
90 | }
91 | return attributes[index].type;
92 | }
93 |
94 | @Override
95 | public String getValue(String uri, String localName) {
96 | int index = getIndex(uri, localName);
97 | if (index == -1) {
98 | return null;
99 | }
100 | return attributes[index].value;
101 | }
102 |
103 | @Override
104 | public String getValue(String qName) {
105 | int index = getIndex(qName);
106 | if (index == -1) {
107 | return null;
108 | }
109 | return attributes[index].value;
110 | }
111 |
112 | public static class Attribute {
113 | public String localName;
114 | public String value;
115 | public String type;
116 | public String uri;
117 | public String qName;
118 |
119 | @RecordableConstructor
120 | public Attribute(String localName, String value, String type, String uri, String qName) {
121 | this.localName = localName;
122 | this.value = value;
123 | this.type = type;
124 | this.uri = uri;
125 | this.qName = qName;
126 | }
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | io.quarkiverse
6 | quarkiverse-parent
7 | 18
8 |
9 | io.quarkiverse.logging.logback
10 | quarkus-logging-logback-parent
11 | 999-SNAPSHOT
12 | pom
13 | Quarkus - Logging Logback - Parent
14 |
15 | impl
16 | runtime
17 | deployment
18 |
19 |
20 | scm:git:git@github.com:quarkiverse/quarkus-logging-logback.git
21 | scm:git:git@github.com:quarkiverse/quarkus-logging-logback.git
22 | https://github.com/quarkiverse/quarkus-logging-logback
23 | HEAD
24 |
25 |
26 | 3.11.0
27 | true
28 | 11
29 | 11
30 | UTF-8
31 | UTF-8
32 | 3.6.6
33 | 1.4.12
34 |
35 |
36 |
37 |
38 | io.quarkus
39 | quarkus-bom
40 | ${quarkus.version}
41 | pom
42 | import
43 |
44 |
45 | ch.qos.logback
46 | logback-classic
47 | ${logback.version}
48 |
49 |
50 | io.quarkiverse.logging.logback
51 | quarkus-logging-logback
52 | ${project.version}
53 |
54 |
55 | io.quarkiverse.logging.logback
56 | quarkus-logging-logback-deployment
57 | ${project.version}
58 |
59 |
60 | io.quarkiverse.logging.logback
61 | quarkus-logging-logback-impl
62 | ${project.version}
63 |
64 |
65 | io.quarkiverse.logging.logback
66 | quarkus-logging-logback-impl-deployment
67 | ${project.version}
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | io.quarkus
76 | quarkus-maven-plugin
77 | ${quarkus.version}
78 |
79 |
80 | maven-compiler-plugin
81 | ${compiler-plugin.version}
82 |
83 |
84 |
85 |
86 |
87 | src/main/resources
88 | true
89 |
90 |
91 |
92 |
93 |
94 | it
95 |
96 |
97 | performRelease
98 | !true
99 |
100 |
101 |
102 | integration-tests
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/docs/modules/ROOT/pages/config.adoc:
--------------------------------------------------------------------------------
1 | //
2 | // This content is generated using mvn compile and copied manually to here
3 | //
4 | [.configuration-legend]
5 | icon:lock[title=Fixed at build time] Configuration property fixed at build time - All other configuration properties are overridable at runtime
6 | [.configuration-reference.searchable, cols="80,.^10,.^10"]
7 | |===
8 |
9 | h|[[quarkus-freemarker_configuration]]link:#quarkus-freemarker_configuration[Configuration property]
10 |
11 | h|Type
12 | h|Default
13 |
14 | a|icon:lock[title=Fixed at build time] [[quarkus-freemarker_quarkus.freemarker.resource-paths]]`link:#quarkus-freemarker_quarkus.freemarker.resource-paths[quarkus.freemarker.resource-paths]`
15 |
16 | [.description]
17 | --
18 | Comma-separated list of absolute resource paths to scan recursively for templates. All tree folder from 'resource-paths' will be added as a resource. Unprefixed locations or locations starting with classpath will be processed in the same way.
19 | --|list of string
20 | |`freemarker/templates`
21 |
22 |
23 | a| [[quarkus-freemarker_quarkus.freemarker.file-paths]]`link:#quarkus-freemarker_quarkus.freemarker.file-paths[quarkus.freemarker.file-paths]`
24 |
25 | [.description]
26 | --
27 | Comma-separated of file system paths where freemarker templates are located
28 | --|list of string
29 | |
30 |
31 |
32 | a| [[quarkus-freemarker_quarkus.freemarker.default-encoding]]`link:#quarkus-freemarker_quarkus.freemarker.default-encoding[quarkus.freemarker.default-encoding]`
33 |
34 | [.description]
35 | --
36 | Set the preferred charset template files are stored in.
37 | --|string
38 | |
39 |
40 |
41 | a| [[quarkus-freemarker_quarkus.freemarker.template-exception-handler]]`link:#quarkus-freemarker_quarkus.freemarker.template-exception-handler[quarkus.freemarker.template-exception-handler]`
42 |
43 | [.description]
44 | --
45 | Sets how errors will appear. rethrow, debug, html-debug, ignore.
46 | --|string
47 | |
48 |
49 |
50 | a| [[quarkus-freemarker_quarkus.freemarker.log-template-exceptions]]`link:#quarkus-freemarker_quarkus.freemarker.log-template-exceptions[quarkus.freemarker.log-template-exceptions]`
51 |
52 | [.description]
53 | --
54 | If false, don't log exceptions inside FreeMarker that it will be thrown at you anyway.
55 | --|boolean
56 | |
57 |
58 |
59 | a| [[quarkus-freemarker_quarkus.freemarker.wrap-unchecked-exceptions]]`link:#quarkus-freemarker_quarkus.freemarker.wrap-unchecked-exceptions[quarkus.freemarker.wrap-unchecked-exceptions]`
60 |
61 | [.description]
62 | --
63 | Wrap unchecked exceptions thrown during template processing into TemplateException-s.
64 | --|boolean
65 | |
66 |
67 |
68 | a| [[quarkus-freemarker_quarkus.freemarker.fallback-on-null-loop-variable]]`link:#quarkus-freemarker_quarkus.freemarker.fallback-on-null-loop-variable[quarkus.freemarker.fallback-on-null-loop-variable]`
69 |
70 | [.description]
71 | --
72 | If false, do not fall back to higher scopes when reading a null loop variable.
73 | --|boolean
74 | |
75 |
76 |
77 | a| [[quarkus-freemarker_quarkus.freemarker.boolean-format]]`link:#quarkus-freemarker_quarkus.freemarker.boolean-format[quarkus.freemarker.boolean-format]`
78 |
79 | [.description]
80 | --
81 | The string value for the boolean `true` and `false` values, usually intended for human consumption (not for a computer language), separated with comma.
82 | --|string
83 | |
84 |
85 |
86 | a| [[quarkus-freemarker_quarkus.freemarker.number-format]]`link:#quarkus-freemarker_quarkus.freemarker.number-format[quarkus.freemarker.number-format]`
87 |
88 | [.description]
89 | --
90 | Sets the default number format used to convert numbers to strings.
91 | --|string
92 | |
93 |
94 |
95 | a| [[quarkus-freemarker_quarkus.freemarker.object-wrapper-expose-fields]]`link:#quarkus-freemarker_quarkus.freemarker.object-wrapper-expose-fields[quarkus.freemarker.object-wrapper-expose-fields]`
96 |
97 | [.description]
98 | --
99 | If true, the object wrapper will be configured to expose fields.
100 | --|boolean
101 | |
102 |
103 |
104 | a|icon:lock[title=Fixed at build time] [[quarkus-freemarker_quarkus.freemarker.directive-directive]]`link:#quarkus-freemarker_quarkus.freemarker.directive-directive[quarkus.freemarker.directive]`
105 |
106 | [.description]
107 | --
108 | List of directives to register with format name=classname
109 | --|`Map`
110 | |
111 |
112 | |===
113 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/LoggingEventWrapper.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import java.util.logging.Formatter;
6 |
7 | import org.jboss.logmanager.ExtLogRecord;
8 | import org.slf4j.Marker;
9 | import org.slf4j.event.KeyValuePair;
10 |
11 | import ch.qos.logback.classic.Level;
12 | import ch.qos.logback.classic.spi.ILoggingEvent;
13 | import ch.qos.logback.classic.spi.IThrowableProxy;
14 | import ch.qos.logback.classic.spi.LoggerContextVO;
15 | import ch.qos.logback.classic.spi.ThrowableProxy;
16 |
17 | public class LoggingEventWrapper implements ILoggingEvent {
18 |
19 | private StackTraceElement[] callerData;
20 | final ExtLogRecord logRecord;
21 | final Formatter formatter;
22 |
23 | public LoggingEventWrapper(ExtLogRecord logRecord, Formatter formatter) {
24 | this.logRecord = logRecord;
25 | this.formatter = formatter;
26 | }
27 |
28 | @Override
29 | public String getThreadName() {
30 | return logRecord.getThreadName();
31 | }
32 |
33 | @Override
34 | public Level getLevel() {
35 | if (logRecord.getLevel().intValue() >= org.jboss.logmanager.Level.ERROR.intValue()) {
36 | return Level.ERROR;
37 | } else if (logRecord.getLevel().intValue() >= org.jboss.logmanager.Level.WARNING.intValue()) {
38 | return Level.WARN;
39 | } else if (logRecord.getLevel().intValue() >= org.jboss.logmanager.Level.INFO.intValue()) {
40 | return Level.INFO;
41 | } else if (logRecord.getLevel().intValue() >= org.jboss.logmanager.Level.DEBUG.intValue()) {
42 | return Level.DEBUG;
43 | } else if (logRecord.getLevel().intValue() >= org.jboss.logmanager.Level.TRACE.intValue()) {
44 | return Level.TRACE;
45 | }
46 | return Level.OFF;
47 | }
48 |
49 | @Override
50 | public String getMessage() {
51 | return logRecord.getMessage();
52 | }
53 |
54 | @Override
55 | public Object[] getArgumentArray() {
56 | return logRecord.getParameters();
57 | }
58 |
59 | @Override
60 | public String getFormattedMessage() {
61 | return logRecord.getFormattedMessage();
62 | }
63 |
64 | @Override
65 | public String getLoggerName() {
66 | return logRecord.getLoggerName();
67 | }
68 |
69 | @Override
70 | public LoggerContextVO getLoggerContextVO() {
71 | return LogbackRecorder.defaultLoggerContext.getLoggerContextRemoteView();
72 | }
73 |
74 | @Override
75 | public IThrowableProxy getThrowableProxy() {
76 | if (logRecord.getThrown() != null) {
77 | return new ThrowableProxy(logRecord.getThrown());
78 | }
79 | return null;
80 | }
81 |
82 | @Override
83 | public StackTraceElement[] getCallerData() {
84 | if (callerData == null) {
85 | callerData = new StackTraceElement[] {
86 | new StackTraceElement(
87 | null,
88 | logRecord.getSourceModuleName(),
89 | logRecord.getSourceModuleVersion(),
90 | logRecord.getSourceClassName(),
91 | logRecord.getSourceMethodName(),
92 | logRecord.getSourceFileName(),
93 | logRecord.getSourceLineNumber())
94 | };
95 | }
96 | return callerData;
97 | }
98 |
99 | @Override
100 | public boolean hasCallerData() {
101 | return getCallerData() != null;
102 | }
103 |
104 | @Override
105 | public Marker getMarker() {
106 | return (Marker) logRecord.getMarker();
107 | }
108 |
109 | @Override
110 | public Map getMDCPropertyMap() {
111 | return logRecord.getMdcCopy();
112 | }
113 |
114 | @Override
115 | public Map getMdc() {
116 | return getMDCPropertyMap();
117 | }
118 |
119 | @Override
120 | public long getTimeStamp() {
121 | return logRecord.getMillis();
122 | }
123 |
124 | @Override
125 | public void prepareForDeferredProcessing() {
126 |
127 | }
128 |
129 | @Override
130 | public List getMarkerList() {
131 | Marker marker = getMarker();
132 |
133 | if (marker == null) {
134 | return List.of();
135 | }
136 |
137 | return List.of(marker);
138 | }
139 |
140 | @Override
141 | public int getNanoseconds() {
142 | return 0;
143 | }
144 |
145 | @Override
146 | public long getSequenceNumber() {
147 | return logRecord.getSequenceNumber();
148 | }
149 |
150 | @Override
151 | public List getKeyValuePairs() {
152 | return List.of();
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/impl/runtime/src/main/java/io/quarkiverse/logback/runtime/LogbackRecorder.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logback.runtime;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Map;
6 | import java.util.Optional;
7 | import java.util.Set;
8 | import java.util.logging.Handler;
9 |
10 | import org.jboss.logmanager.ExtHandler;
11 | import org.jboss.logmanager.ExtLogRecord;
12 | import org.slf4j.helpers.Util;
13 | import org.xml.sax.InputSource;
14 | import org.xml.sax.helpers.AttributesImpl;
15 |
16 | import ch.qos.logback.classic.Logger;
17 | import ch.qos.logback.classic.LoggerContext;
18 | import ch.qos.logback.classic.joran.JoranConfigurator;
19 | import ch.qos.logback.core.joran.event.BodyEvent;
20 | import ch.qos.logback.core.joran.event.SaxEvent;
21 | import ch.qos.logback.core.joran.event.SaxEventRecorder;
22 | import ch.qos.logback.core.joran.event.StartEvent;
23 | import ch.qos.logback.core.joran.spi.JoranException;
24 | import ch.qos.logback.core.status.StatusUtil;
25 | import ch.qos.logback.core.util.StatusPrinter;
26 | import io.quarkiverse.logback.runtime.events.BodySub;
27 | import io.quarkiverse.logback.runtime.events.EventSubstitution;
28 | import io.quarkus.runtime.RuntimeValue;
29 | import io.quarkus.runtime.ShutdownContext;
30 | import io.quarkus.runtime.annotations.Recorder;
31 | import io.smallrye.common.expression.Expression;
32 | import io.smallrye.config.ConfigValue;
33 | import io.smallrye.config.SmallRyeConfig;
34 | import io.smallrye.config.SmallRyeConfigProviderResolver;
35 |
36 | @Recorder
37 | public class LogbackRecorder {
38 |
39 | public static final String DELAYED = "$$delayed";
40 | static volatile LoggerContext defaultLoggerContext;
41 |
42 | public static final List DELAYED_START_HANDLERS = new ArrayList<>();
43 | private static volatile boolean started;
44 |
45 | public static void addDelayed(DelayedStart delayedStart) {
46 | if (started) {
47 | delayedStart.doQuarkusDelayedStart();
48 | } else {
49 | DELAYED_START_HANDLERS.add(delayedStart);
50 | }
51 | }
52 |
53 | public void init(List originalEvents, Set delayedStartClasses, ShutdownContext context,
54 | Map buildSystemProps) {
55 | EventSubstitution substitution = new EventSubstitution();
56 | if (defaultLoggerContext == null) {
57 | SmallRyeConfig config = (SmallRyeConfig) SmallRyeConfigProviderResolver.instance().getConfig();
58 | List configEvents = new ArrayList<>();
59 | for (SaxEvent i : originalEvents) {
60 | if (i instanceof StartEvent) {
61 | AttributesImpl impl = (AttributesImpl) ((StartEvent) i).attributes;
62 | int index = impl.getIndex("class");
63 | if (index > -1) {
64 | String val = impl.getValue(index);
65 | if (delayedStartClasses.contains(val)) {
66 | impl.setValue(index, val + DELAYED);
67 | }
68 | }
69 | for (int j = 1; j <= impl.getLength(); ++j) {
70 | String val = impl.getValue(index);
71 | if (val != null && val.contains("${")) {
72 | final String expanded = doExpand(config, val, buildSystemProps);
73 | impl.setValue(j, expanded);
74 | }
75 | }
76 | configEvents.add(i);
77 | } else if (i instanceof BodyEvent) {
78 | String val = ((BodyEvent) i).getText();
79 | if (val.contains("${")) {
80 | final String expanded = doExpand(config, val, buildSystemProps);
81 | configEvents.add(substitution.deserialize(
82 | new BodySub(i.getNamespaceURI(), i.getLocalName(), i.getQName(), i.getLocator(), expanded)));
83 | } else {
84 | configEvents.add(i);
85 | }
86 | } else {
87 | configEvents.add(i);
88 | }
89 | }
90 | defaultLoggerContext = new LoggerContext();
91 | try {
92 | JoranConfigurator configurator = new JoranConfigurator() {
93 |
94 | @Override
95 | public SaxEventRecorder populateSaxEventRecorder(InputSource inputSource) throws JoranException {
96 | SaxEventRecorder recorder = new SaxEventRecorder(context) {
97 | public List getSaxEventList() {
98 | return configEvents;
99 | }
100 | };
101 | return recorder;
102 | };
103 | };
104 | configurator.setContext(defaultLoggerContext);
105 | configurator.doConfigure((InputSource) null);
106 | // logback-292
107 | if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) {
108 | StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext);
109 | }
110 | } catch (Exception t) { // see LOGBACK-1159
111 | Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t);
112 | }
113 | context.addLastShutdownTask(new Runnable() {
114 | @Override
115 | public void run() {
116 | defaultLoggerContext.stop();
117 | defaultLoggerContext = null;
118 | started = false;
119 | }
120 | });
121 | }
122 | }
123 |
124 | private String doExpand(SmallRyeConfig config, String val, Map buildSystemProps) {
125 | Expression expression = Expression.compile(val);
126 | final String expanded = expression.evaluate((resolveContext, stringBuilder) -> {
127 | final ConfigValue resolve = config.getConfigValue(resolveContext.getKey());
128 | if (resolve.getValue() != null) {
129 | stringBuilder.append(resolve.getValue());
130 | } else if (buildSystemProps.containsKey(resolveContext.getKey())) {
131 | stringBuilder.append(buildSystemProps.get(resolveContext.getKey()));
132 | } else if (resolveContext.hasDefault()) {
133 | resolveContext.expandDefault();
134 | } else {
135 | stringBuilder.append("${" + resolveContext.getKey() + "}");
136 | }
137 | });
138 | return expanded;
139 | }
140 |
141 | public RuntimeValue> createHandler() {
142 | started = true;
143 | for (DelayedStart i : DELAYED_START_HANDLERS) {
144 | i.doQuarkusDelayedStart();
145 | }
146 | DELAYED_START_HANDLERS.clear();
147 | return new RuntimeValue<>(Optional.of(new ExtHandler() {
148 |
149 | @Override
150 | public final void doPublish(final ExtLogRecord record) {
151 | if (defaultLoggerContext == null) {
152 | return;
153 | }
154 | Logger logger = defaultLoggerContext.getLogger(record.getLoggerName());
155 | logger.callAppenders(new LoggingEventWrapper(record, getFormatter()));
156 | }
157 |
158 | @Override
159 | public void flush() {
160 |
161 | }
162 |
163 | @Override
164 | public void close() throws SecurityException {
165 |
166 | }
167 | }));
168 |
169 | }
170 |
171 | }
172 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/impl/deployment/src/main/java/io/quarkiverse/logging/logback/deployment/LoggingLogbackProcessor.java:
--------------------------------------------------------------------------------
1 | package io.quarkiverse.logging.logback.deployment;
2 |
3 | import java.io.IOException;
4 | import java.io.InputStream;
5 | import java.net.URL;
6 | import java.nio.charset.StandardCharsets;
7 | import java.util.Arrays;
8 | import java.util.Collections;
9 | import java.util.HashMap;
10 | import java.util.HashSet;
11 | import java.util.List;
12 | import java.util.Map;
13 | import java.util.Objects;
14 | import java.util.Properties;
15 | import java.util.Set;
16 | import java.util.concurrent.atomic.AtomicReference;
17 | import java.util.function.Function;
18 | import java.util.stream.Collectors;
19 |
20 | import org.jboss.logging.Logger;
21 | import org.xml.sax.InputSource;
22 |
23 | import ch.qos.logback.classic.LoggerContext;
24 | import ch.qos.logback.classic.joran.JoranConfigurator;
25 | import ch.qos.logback.classic.util.ContextInitializer;
26 | import ch.qos.logback.core.joran.event.BodyEvent;
27 | import ch.qos.logback.core.joran.event.EndEvent;
28 | import ch.qos.logback.core.joran.event.SaxEvent;
29 | import ch.qos.logback.core.joran.event.SaxEventRecorder;
30 | import ch.qos.logback.core.joran.event.StartEvent;
31 | import ch.qos.logback.core.joran.spi.JoranException;
32 | import ch.qos.logback.core.joran.spi.NoAutoStart;
33 | import ch.qos.logback.core.spi.LifeCycle;
34 | import ch.qos.logback.core.util.Loader;
35 | import io.quarkiverse.logback.runtime.DelayedStart;
36 | import io.quarkiverse.logback.runtime.LogbackRecorder;
37 | import io.quarkiverse.logback.runtime.events.BodySub;
38 | import io.quarkiverse.logback.runtime.events.EndSub;
39 | import io.quarkiverse.logback.runtime.events.EventSubstitution;
40 | import io.quarkiverse.logback.runtime.events.StartSub;
41 | import io.quarkus.bootstrap.model.AppArtifactKey;
42 | import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
43 | import io.quarkus.deployment.annotations.BuildProducer;
44 | import io.quarkus.deployment.annotations.BuildStep;
45 | import io.quarkus.deployment.annotations.ExecutionTime;
46 | import io.quarkus.deployment.annotations.Record;
47 | import io.quarkus.deployment.builditem.FeatureBuildItem;
48 | import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
49 | import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
50 | import io.quarkus.deployment.builditem.LogHandlerBuildItem;
51 | import io.quarkus.deployment.builditem.RemovedResourceBuildItem;
52 | import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem;
53 | import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
54 | import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
55 | import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
56 | import io.quarkus.deployment.recording.RecorderContext;
57 | import io.quarkus.gizmo.ClassCreator;
58 | import io.quarkus.gizmo.MethodCreator;
59 | import io.quarkus.gizmo.MethodDescriptor;
60 | import io.smallrye.common.version.VersionScheme;
61 |
62 | class LoggingLogbackProcessor {
63 |
64 | private static final Logger log = Logger.getLogger(LoggingLogbackProcessor.class);
65 |
66 | private static final String FEATURE = "logging-logback";
67 | public static final String PROJECT_VERSION = "project.version";
68 |
69 | @BuildStep
70 | FeatureBuildItem feature() {
71 | return new FeatureBuildItem(FEATURE);
72 | }
73 |
74 | @BuildStep
75 | RemovedResourceBuildItem removeSlf4jBinding() {
76 | return new RemovedResourceBuildItem(new AppArtifactKey("ch.qos.logback", "logback-classic", null, "jar"),
77 | Collections.singleton("org/slf4j/impl/StaticLoggerBinder.class"));
78 | }
79 |
80 | @BuildStep
81 | HotDeploymentWatchedFileBuildItem watchLogback() {
82 | return new HotDeploymentWatchedFileBuildItem("logback.xml");
83 | }
84 |
85 | @Record(ExecutionTime.STATIC_INIT)
86 | @BuildStep
87 | void init(LogbackRecorder recorder, RecorderContext context,
88 | BuildProducer runTimeConfigurationDefaultBuildItemBuildProducer,
89 | BuildProducer generatedClasses,
90 | OutputTargetBuildItem outputTargetBuildItem,
91 | CurateOutcomeBuildItem curateOutcomeBuildItem,
92 | ShutdownContextBuildItem shutdownContextBuildItem)
93 | throws Exception {
94 | //first check the versions
95 | doVersionCheck();
96 |
97 | URL url = getUrl();
98 | if (url == null) {
99 | return;
100 | }
101 | context.registerSubstitution(StartEvent.class, StartSub.class, (Class) EventSubstitution.class);
102 | context.registerSubstitution(BodyEvent.class, BodySub.class, (Class) EventSubstitution.class);
103 | context.registerSubstitution(EndEvent.class, EndSub.class, (Class) EventSubstitution.class);
104 | final AtomicReference> events = new AtomicReference<>();
105 |
106 | JoranConfigurator configurator = new JoranConfigurator() {
107 |
108 | @Override
109 | public SaxEventRecorder populateSaxEventRecorder(InputSource inputSource) throws JoranException {
110 | SaxEventRecorder recorder = super.populateSaxEventRecorder(inputSource);
111 | events.set(recorder.getSaxEventList());
112 | return recorder;
113 | };
114 | };
115 | configurator.setContext(new LoggerContext());
116 | configurator.doConfigure(url);
117 |
118 | List loggerPath = Arrays.asList("configuration", "logger");
119 | List rootPath = Arrays.asList("configuration", "root");
120 | String rootLevel = null;
121 | Map levels = new HashMap<>();
122 | Set allClasses = new HashSet<>();
123 | for (SaxEvent i : events.get()) {
124 | if (i instanceof StartEvent) {
125 | StartEvent s = ((StartEvent) i);
126 | if (Objects.equals(loggerPath, s.elementPath.getCopyOfPartList())) {
127 | String level = s.attributes.getValue("level");
128 | if (level != null) {
129 | levels.put(s.attributes.getValue("name"), level);
130 | }
131 | } else if (Objects.equals(rootPath, s.elementPath.getCopyOfPartList())) {
132 | String level = s.attributes.getValue("level");
133 | if (level != null) {
134 | rootLevel = level;
135 | }
136 | }
137 | int classIndex = s.attributes.getIndex("class");
138 | if (classIndex != -1) {
139 | allClasses.add(s.attributes.getValue(classIndex));
140 | }
141 | }
142 | }
143 |
144 | boolean disableConsole = false;
145 | Set delayedClasses = new HashSet<>();
146 | for (String i : allClasses) {
147 | if (i.equals("ch.qos.logback.core.ConsoleAppender")) {
148 | disableConsole = true;
149 | }
150 | try {
151 | Class> c = Thread.currentThread().getContextClassLoader().loadClass(i);
152 | if (LifeCycle.class.isAssignableFrom(c) && c.getAnnotation(NoAutoStart.class) == null) {
153 | delayedClasses.add(i);
154 | }
155 | } catch (ClassNotFoundException exception) {
156 | throw new RuntimeException(exception);
157 | }
158 | }
159 | if (disableConsole) {
160 | runTimeConfigurationDefaultBuildItemBuildProducer
161 | .produce(new RunTimeConfigurationDefaultBuildItem("quarkus.log.console.enable", "false"));
162 | }
163 |
164 | for (String i : delayedClasses) {
165 | try (ClassCreator c = new ClassCreator(
166 | new GeneratedClassGizmoAdaptor(generatedClasses,
167 | (Function) s -> s.substring(s.length() - LogbackRecorder.DELAYED.length())),
168 | i + LogbackRecorder.DELAYED, null, i, DelayedStart.class.getName())) {
169 | MethodCreator start = c.getMethodCreator("start", void.class);
170 | start.invokeStaticMethod(
171 | MethodDescriptor.ofMethod(LogbackRecorder.class, "addDelayed", void.class, DelayedStart.class),
172 | start.getThis());
173 | start.returnValue(null);
174 | MethodCreator method = c.getMethodCreator("doQuarkusDelayedStart", void.class);
175 | method.invokeSpecialMethod(MethodDescriptor.ofMethod(i, "start", void.class), method.getThis());
176 | method.returnValue(null);
177 | }
178 | }
179 |
180 | if (rootLevel != null) {
181 | runTimeConfigurationDefaultBuildItemBuildProducer
182 | .produce(new RunTimeConfigurationDefaultBuildItem("quarkus.log.level", rootLevel));
183 | }
184 | for (Map.Entry e : levels.entrySet()) {
185 | runTimeConfigurationDefaultBuildItemBuildProducer.produce(new RunTimeConfigurationDefaultBuildItem(
186 | "quarkus.log.category.\"" + e.getKey() + "\".level", e.getValue()));
187 | }
188 |
189 | Map buildProperties = new HashMap<>(outputTargetBuildItem.getBuildSystemProperties()
190 | .entrySet().stream().collect(Collectors.toMap(Object::toString, Object::toString)));
191 | buildProperties.put(PROJECT_VERSION, curateOutcomeBuildItem.getApplicationModel().getAppArtifact().getVersion());
192 | recorder.init(events.get(), delayedClasses, shutdownContextBuildItem, buildProperties);
193 | }
194 |
195 | private void doVersionCheck() throws IOException {
196 | //if the versions are wrong you get really hard to understand errors
197 | //easier to just verify this ourselves
198 | String compiledVersion;
199 | String coreVersion = null;
200 | String classicVersion = null;
201 |
202 | try (InputStream in = getClass().getClassLoader().getResourceAsStream("quarkus-logback-version.txt")) {
203 | compiledVersion = new String(in.readAllBytes(), StandardCharsets.UTF_8);
204 | }
205 | try (InputStream in = getClass().getClassLoader()
206 | .getResourceAsStream("META-INF/maven/ch.qos.logback/logback-core/pom.properties")) {
207 | if (in != null) {
208 | Properties p = new Properties();
209 | p.load(in);
210 | coreVersion = p.getProperty("version");
211 | }
212 | }
213 | try (InputStream in = getClass().getClassLoader()
214 | .getResourceAsStream("META-INF/maven/ch.qos.logback/logback-classic/pom.properties")) {
215 | if (in != null) {
216 | Properties p = new Properties();
217 | p.load(in);
218 | classicVersion = p.getProperty("version");
219 | }
220 | }
221 | if (coreVersion != null) {
222 | if (VersionScheme.MAVEN.compare(coreVersion, compiledVersion) < 0) {
223 | throw new RuntimeException("ch.qos.logback:logback-core version " + coreVersion
224 | + " is not compatible with quarkus-logback which requires at least " + compiledVersion
225 | + " please use the correct logback version");
226 | }
227 | if (classicVersion != null) {
228 | if (VersionScheme.MAVEN.compare(classicVersion, coreVersion) != 0) {
229 | throw new RuntimeException("logback-core(" + coreVersion + ") and logback-classic(" + classicVersion
230 | + ") versions must match");
231 | }
232 | }
233 | } else {
234 | log.warn("Could not determine logback version on class path");
235 | }
236 | }
237 |
238 | @BuildStep
239 | @Record(ExecutionTime.RUNTIME_INIT)
240 | LogHandlerBuildItem handler(LogbackRecorder recorder) {
241 | return new LogHandlerBuildItem(recorder.createHandler());
242 | }
243 |
244 | private URL getUrl() {
245 | URL url = Loader.getResource(ContextInitializer.TEST_AUTOCONFIG_FILE, Thread.currentThread().getContextClassLoader());
246 | if (url != null) {
247 | return url;
248 | }
249 | return Loader.getResource(ContextInitializer.AUTOCONFIG_FILE, Thread.currentThread().getContextClassLoader());
250 | }
251 | }
252 |
--------------------------------------------------------------------------------